1 /*
2  * Copyright (c) 2006-2024, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2024-03-10     Meco Man     the first version
9  */
10 
11 #include <rtthread.h>
12 
13 #if defined(RT_KLIBC_USING_LIBC_MEMSET) || \
14     defined(RT_KLIBC_USING_LIBC_MEMCPY) || \
15     defined(RT_KLIBC_USING_LIBC_MEMMOVE) || \
16     defined(RT_KLIBC_USING_LIBC_MEMCMP) || \
17     defined(RT_KLIBC_USING_LIBC_STRSTR) || \
18     defined(RT_KLIBC_USING_LIBC_STRNCPY) || \
19     defined(RT_KLIBC_USING_LIBC_STRCPY) || \
20     defined(RT_KLIBC_USING_LIBC_STRNCMP) || \
21     defined(RT_KLIBC_USING_LIBC_STRCMP) || \
22     defined(RT_KLIBC_USING_LIBC_STRLEN)
23 #include <string.h>
24 #endif
25 
26 /**
27  * @brief  This function will set the content of memory to specified value.
28  *
29  * @param  s is the address of source memory, point to the memory block to be filled.
30  *
31  * @param  c is the value to be set. The value is passed in int form, but the function
32  *         uses the unsigned character form of the value when filling the memory block.
33  *
34  * @param  count number of bytes to be set.
35  *
36  * @return The address of source memory.
37  */
38 #ifndef RT_KLIBC_USING_USER_MEMSET
rt_memset(void * s,int c,rt_ubase_t count)39 void *rt_memset(void *s, int c, rt_ubase_t count)
40 {
41 #if defined(RT_KLIBC_USING_LIBC_MEMSET)
42     return memset(s, c, count);
43 #elif defined(RT_KLIBC_USING_TINY_MEMSET)
44     char *xs = (char *)s;
45 
46     while (count--)
47         *xs++ = c;
48 
49     return s;
50 #else
51 
52 #define LBLOCKSIZE      (sizeof(rt_ubase_t))
53 #define UNALIGNED(X)    ((long)X & (LBLOCKSIZE - 1))
54 #define TOO_SMALL(LEN)  ((LEN) < LBLOCKSIZE)
55 
56     unsigned int i = 0;
57     char *m = (char *)s;
58     unsigned long buffer = 0;
59     unsigned long *aligned_addr = RT_NULL;
60     unsigned char d = (unsigned int)c & (unsigned char)(-1);  /* To avoid sign extension, copy C to an
61                                 unsigned variable. (unsigned)((char)(-1))=0xFF for 8bit and =0xFFFF for 16bit: word independent */
62 
63     RT_ASSERT(LBLOCKSIZE == 2 || LBLOCKSIZE == 4 || LBLOCKSIZE == 8);
64 
65     if (!TOO_SMALL(count) && !UNALIGNED(s))
66     {
67         /* If we get this far, we know that count is large and s is word-aligned. */
68         aligned_addr = (unsigned long *)s;
69 
70         /* Store d into each char sized location in buffer so that
71          * we can set large blocks quickly.
72          */
73         for (i = 0; i < LBLOCKSIZE; i++)
74         {
75             *(((unsigned char *)&buffer)+i) = d;
76         }
77 
78         while (count >= LBLOCKSIZE * 4)
79         {
80             *aligned_addr++ = buffer;
81             *aligned_addr++ = buffer;
82             *aligned_addr++ = buffer;
83             *aligned_addr++ = buffer;
84             count -= 4 * LBLOCKSIZE;
85         }
86 
87         while (count >= LBLOCKSIZE)
88         {
89             *aligned_addr++ = buffer;
90             count -= LBLOCKSIZE;
91         }
92 
93         /* Pick up the remainder with a bytewise loop. */
94         m = (char *)aligned_addr;
95     }
96 
97     while (count--)
98     {
99         *m++ = (char)d;
100     }
101 
102     return s;
103 
104 #undef LBLOCKSIZE
105 #undef UNALIGNED
106 #undef TOO_SMALL
107 #endif /* RT_KLIBC_USING_LIBC_MEMSET */
108 }
109 #endif /* RT_KLIBC_USING_USER_MEMSET */
110 RTM_EXPORT(rt_memset);
111 
112 /**
113  * @brief  This function will copy memory content from source address to destination address.
114  *
115  * @param  dst is the address of destination memory, points to the copied content.
116  *
117  * @param  src  is the address of source memory, pointing to the data source to be copied.
118  *
119  * @param  count is the copied length.
120  *
121  * @return The address of destination memory
122  */
123 #ifndef RT_KLIBC_USING_USER_MEMCPY
rt_memcpy(void * dst,const void * src,rt_ubase_t count)124 void *rt_memcpy(void *dst, const void *src, rt_ubase_t count)
125 {
126 #if defined(RT_KLIBC_USING_LIBC_MEMCPY)
127     return memcpy(dst, src, count);
128 #elif defined(RT_KLIBC_USING_TINY_MEMCPY)
129     char *tmp = (char *)dst, *s = (char *)src;
130     rt_ubase_t len = 0;
131 
132     if (tmp <= s || tmp > (s + count))
133     {
134         while (count--)
135             *tmp ++ = *s ++;
136     }
137     else
138     {
139         for (len = count; len > 0; len --)
140             tmp[len - 1] = s[len - 1];
141     }
142 
143     return dst;
144 #else
145 
146 #define UNALIGNED(X, Y) \
147     (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
148 #define BIGBLOCKSIZE    (sizeof (long) << 2)
149 #define LITTLEBLOCKSIZE (sizeof (long))
150 #define TOO_SMALL(LEN)  ((LEN) < BIGBLOCKSIZE)
151 
152     char *dst_ptr = (char *)dst;
153     char *src_ptr = (char *)src;
154     long *aligned_dst = RT_NULL;
155     long *aligned_src = RT_NULL;
156     rt_ubase_t len = count;
157 
158     /* If the size is small, or either SRC or DST is unaligned,
159     then punt into the byte copy loop.  This should be rare. */
160     if (!TOO_SMALL(len) && !UNALIGNED(src_ptr, dst_ptr))
161     {
162         aligned_dst = (long *)dst_ptr;
163         aligned_src = (long *)src_ptr;
164 
165         /* Copy 4X long words at a time if possible. */
166         while (len >= BIGBLOCKSIZE)
167         {
168             *aligned_dst++ = *aligned_src++;
169             *aligned_dst++ = *aligned_src++;
170             *aligned_dst++ = *aligned_src++;
171             *aligned_dst++ = *aligned_src++;
172             len -= BIGBLOCKSIZE;
173         }
174 
175         /* Copy one long word at a time if possible. */
176         while (len >= LITTLEBLOCKSIZE)
177         {
178             *aligned_dst++ = *aligned_src++;
179             len -= LITTLEBLOCKSIZE;
180         }
181 
182         /* Pick up any residual with a byte copier. */
183         dst_ptr = (char *)aligned_dst;
184         src_ptr = (char *)aligned_src;
185     }
186 
187     while (len--)
188         *dst_ptr++ = *src_ptr++;
189 
190     return dst;
191 #undef UNALIGNED
192 #undef BIGBLOCKSIZE
193 #undef LITTLEBLOCKSIZE
194 #undef TOO_SMALL
195 #endif /* RT_KLIBC_USING_LIBC_MEMCPY */
196 }
197 #endif /* RT_KLIBC_USING_USER_MEMCPY */
198 RTM_EXPORT(rt_memcpy);
199 
200 /**
201  * @brief  This function will move memory content from source address to destination
202  * address. If the destination memory does not overlap with the source memory,
203  * the function is the same as memcpy().
204  *
205  * @param  dest is the address of destination memory, points to the copied content.
206  *
207  * @param  src is the address of source memory, point to the data source to be copied.
208  *
209  * @param  n is the copied length.
210  *
211  * @return The address of destination memory.
212  */
213 #ifndef RT_KLIBC_USING_USER_MEMMOVE
rt_memmove(void * dest,const void * src,rt_size_t n)214 void *rt_memmove(void *dest, const void *src, rt_size_t n)
215 {
216 #ifdef RT_KLIBC_USING_LIBC_MEMMOVE
217     return memmove(dest, src, n);
218 #else
219     char *tmp = (char *)dest, *s = (char *)src;
220 
221     if (s < tmp && tmp < s + n)
222     {
223         tmp += n;
224         s += n;
225 
226         while (n--)
227             *(--tmp) = *(--s);
228     }
229     else
230     {
231         while (n--)
232             *tmp++ = *s++;
233     }
234 
235     return dest;
236 #endif /* RT_KLIBC_USING_LIBC_MEMMOVE */
237 }
238 #endif /* RT_KLIBC_USING_USER_MEMMOVE */
239 RTM_EXPORT(rt_memmove);
240 
241 /**
242  * @brief  This function will compare two areas of memory.
243  *
244  * @param  cs is a block of memory.
245  *
246  * @param  ct is another block of memory.
247  *
248  * @param  count is the size of the area.
249  *
250  * @return Compare the results:
251  *         If the result < 0, cs is smaller than ct.
252  *         If the result > 0, cs is greater than ct.
253  *         If the result = 0, cs is equal to ct.
254  */
255 #ifndef RT_KLIBC_USING_USER_MEMCMP
rt_memcmp(const void * cs,const void * ct,rt_size_t count)256 rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_size_t count)
257 {
258 #ifdef RT_KLIBC_USING_LIBC_MEMCMP
259     return memcmp(cs, ct, count);
260 #else
261     const unsigned char *su1 = RT_NULL, *su2 = RT_NULL;
262     int res = 0;
263 
264     for (su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--)
265         if ((res = *su1 - *su2) != 0)
266             break;
267 
268     return res;
269 #endif /* RT_KLIBC_USING_LIBC_MEMCMP */
270 }
271 #endif /* RT_KLIBC_USING_USER_MEMCMP */
272 RTM_EXPORT(rt_memcmp);
273 
274 /**
275  * @brief  This function will return the first occurrence of a string, without the
276  * terminator '\0'.
277  *
278  * @param  s1 is the source string.
279  *
280  * @param  s2 is the find string.
281  *
282  * @return The first occurrence of a s2 in s1, or RT_NULL if no found.
283  */
284 #ifndef RT_KLIBC_USING_USER_STRSTR
rt_strstr(const char * s1,const char * s2)285 char *rt_strstr(const char *s1, const char *s2)
286 {
287 #ifdef RT_KLIBC_USING_LIBC_STRSTR
288     return strstr(s1, s2);
289 #else
290     int l1 = 0, l2 = 0;
291 
292     l2 = rt_strlen(s2);
293     if (!l2)
294     {
295         return (char *)s1;
296     }
297 
298     l1 = rt_strlen(s1);
299     while (l1 >= l2)
300     {
301         l1 --;
302         if (!rt_memcmp(s1, s2, l2))
303         {
304             return (char *)s1;
305         }
306 
307         s1 ++;
308     }
309 
310     return RT_NULL;
311 #endif /* RT_KLIBC_USING_LIBC_STRSTR */
312 }
313 #endif /* RT_KLIBC_USING_USER_STRSTR */
314 RTM_EXPORT(rt_strstr);
315 
316 /**
317  * @brief  This function will compare two strings while ignoring differences in case
318  *
319  * @param  a is the string to be compared.
320  *
321  * @param  b is the string to be compared.
322  *
323  * @return Compare the results:
324  *         If the result < 0, a is smaller than a.
325  *         If the result > 0, a is greater than a.
326  *         If the result = 0, a is equal to a.
327  */
328 #ifndef RT_KLIBC_USING_USER_STRCASECMP
rt_strcasecmp(const char * a,const char * b)329 rt_int32_t rt_strcasecmp(const char *a, const char *b)
330 {
331     int ca = 0, cb = 0;
332 
333     do
334     {
335         ca = *a++ & 0xff;
336         cb = *b++ & 0xff;
337         if (ca >= 'A' && ca <= 'Z')
338             ca += 'a' - 'A';
339         if (cb >= 'A' && cb <= 'Z')
340             cb += 'a' - 'A';
341     }
342     while (ca == cb && ca != '\0');
343 
344     return ca - cb;
345 }
346 #endif /* RT_KLIBC_USING_USER_STRCASECMP */
347 RTM_EXPORT(rt_strcasecmp);
348 
349 /**
350  * @brief  This function will copy string no more than n bytes.
351  *
352  * @param  dst points to the address used to store the copied content.
353  *
354  * @param  src is the string to be copied.
355  *
356  * @param  n is the maximum copied length.
357  *
358  * @return The address where the copied content is stored.
359  */
360 #ifndef RT_KLIBC_USING_USER_STRNCPY
rt_strncpy(char * dst,const char * src,rt_size_t n)361 char *rt_strncpy(char *dst, const char *src, rt_size_t n)
362 {
363 #ifdef RT_KLIBC_USING_LIBC_STRNCPY
364     return strncpy(dst, src, n);
365 #else
366     if (n != 0)
367     {
368         char *d = dst;
369         const char *s = src;
370 
371         do
372         {
373             if ((*d++ = *s++) == 0)
374             {
375                 /* NUL pad the remaining n-1 bytes */
376                 while (--n != 0)
377                 {
378                     *d++ = 0;
379                 }
380 
381                 break;
382             }
383         } while (--n != 0);
384     }
385 
386     return (dst);
387 #endif /* RT_KLIBC_USING_LIBC_STRNCPY */
388 }
389 #endif /* RT_KLIBC_USING_USER_STRNCPY */
390 RTM_EXPORT(rt_strncpy);
391 
392 /**
393  * @brief  This function will copy string.
394  *
395  * @param  dst points to the address used to store the copied content.
396  *
397  * @param  src is the string to be copied.
398  *
399  * @return The address where the copied content is stored.
400  */
401 #ifndef RT_KLIBC_USING_USER_STRCPY
rt_strcpy(char * dst,const char * src)402 char *rt_strcpy(char *dst, const char *src)
403 {
404 #ifdef RT_KLIBC_USING_LIBC_STRCPY
405     return strcpy(dst, src);
406 #else
407     char *dest = dst;
408 
409     while (*src != '\0')
410     {
411         *dst = *src;
412         dst++;
413         src++;
414     }
415 
416     *dst = '\0';
417     return dest;
418 #endif /* RT_KLIBC_USING_LIBC_STRCPY */
419 }
420 #endif /* RT_KLIBC_USING_USER_STRCPY */
421 RTM_EXPORT(rt_strcpy);
422 
423 /**
424  * @brief  This function will compare two strings with specified maximum length.
425  *
426  * @param  cs is the string to be compared.
427  *
428  * @param  ct is the string to be compared.
429  *
430  * @param  count is the maximum compare length.
431  *
432  * @return Compare the results:
433  *         If the result < 0, cs is smaller than ct.
434  *         If the result > 0, cs is greater than ct.
435  *         If the result = 0, cs is equal to ct.
436  */
437 #ifndef RT_KLIBC_USING_USER_STRNCMP
rt_strncmp(const char * cs,const char * ct,rt_size_t count)438 rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_size_t count)
439 {
440 #ifdef RT_KLIBC_USING_LIBC_STRNCMP
441     return strncmp(cs, ct, count);
442 #else
443     signed char res = 0;
444 
445     while (count)
446     {
447         if ((res = *cs - *ct++) != 0 || !*cs++)
448         {
449             break;
450         }
451 
452         count --;
453     }
454 
455     return res;
456 #endif /* RT_KLIBC_USING_LIBC_STRNCMP */
457 }
458 #endif /* RT_KLIBC_USING_USER_STRNCMP */
459 RTM_EXPORT(rt_strncmp);
460 
461 /**
462  * @brief  This function will compare two strings without specified length.
463  *
464  * @param  cs is the string to be compared.
465  *
466  * @param  ct is the string to be compared.
467  *
468  * @return Compare the results:
469  *         If the result < 0, cs is smaller than ct.
470  *         If the result > 0, cs is greater than ct.
471  *         If the result = 0, cs is equal to ct.
472  */
473 #ifndef RT_KLIBC_USING_USER_STRCMP
rt_strcmp(const char * cs,const char * ct)474 rt_int32_t rt_strcmp(const char *cs, const char *ct)
475 {
476 #ifdef RT_KLIBC_USING_LIBC_STRCMP
477     return strcmp(cs, ct);
478 #else
479     while (*cs && *cs == *ct)
480     {
481         cs++;
482         ct++;
483     }
484 
485     return (*cs - *ct);
486 #endif /* RT_KLIBC_USING_LIBC_STRCMP */
487 }
488 #endif /* RT_KLIBC_USING_USER_STRCMP */
489 RTM_EXPORT(rt_strcmp);
490 
491 /**
492  * @brief  This function will return the length of a string, which terminate will
493  * null character.
494  *
495  * @param  s is the string
496  *
497  * @return The length of string.
498  */
499 #ifndef RT_KLIBC_USING_USER_STRLEN
rt_strlen(const char * s)500 rt_size_t rt_strlen(const char *s)
501 {
502 #ifdef RT_KLIBC_USING_LIBC_STRLEN
503     return strlen(s);
504 #else
505     const char *sc = RT_NULL;
506     for (sc = s; *sc != '\0'; ++sc);
507     return sc - s;
508 #endif /* RT_KLIBC_USING_LIBC_STRLEN */
509 }
510 #endif /* RT_KLIBC_USING_USER_STRLEN */
511 RTM_EXPORT(rt_strlen);
512 
513 /**
514  * @brief  The  strnlen()  function  returns the number of characters in the
515  * string pointed to by s, excluding the terminating null byte ('\0'),
516  * but at most maxlen.  In doing this, strnlen() looks only at the
517  * first maxlen characters in the string pointed to by s and never
518  * beyond s+maxlen.
519  *
520  * @param  s is the string.
521  *
522  * @param  maxlen is the max size.
523  *
524  * @return The length of string.
525  */
526 #ifndef RT_KLIBC_USING_USER_STRNLEN
rt_strnlen(const char * s,rt_ubase_t maxlen)527 rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen)
528 {
529     const char *sc;
530     for (sc = s; *sc != '\0' && (rt_ubase_t)(sc - s) < maxlen; ++sc);
531     return sc - s;
532 }
533 #endif /* RT_KLIBC_USING_USER_STRNLEN */
534 RTM_EXPORT(rt_strnlen);
535 
536 #ifdef RT_USING_HEAP
537 /**
538  * @brief  This function will duplicate a string.
539  *
540  * @param  s is the string to be duplicated.
541  *
542  * @return The string address of the copy.
543  */
rt_strdup(const char * s)544 char *rt_strdup(const char *s)
545 {
546     rt_size_t len = rt_strlen(s) + 1;
547     char *tmp = (char *)rt_malloc(len);
548 
549     if (!tmp)
550     {
551         return RT_NULL;
552     }
553 
554     rt_memcpy(tmp, s, len);
555 
556     return tmp;
557 }
558 RTM_EXPORT(rt_strdup);
559 #endif /* RT_USING_HEAP */
560