| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "mos_string.hpp" |
| 4 | |
| 5 | #include <mos/moslib_global.hpp> |
| 6 | #include <mos_stdlib.hpp> |
| 7 | |
| 8 | size_t strlen(const char *str) |
| 9 | { |
| 10 | size_t len = 0; |
| 11 | while (str[len]) |
| 12 | len++; |
| 13 | return len; |
| 14 | } |
| 15 | |
| 16 | size_t strnlen(const char *str, size_t n) |
| 17 | { |
| 18 | size_t len = 0; |
| 19 | while (len < n && str[len]) |
| 20 | len++; |
| 21 | return len; |
| 22 | } |
| 23 | |
| 24 | s32 strcmp(const char *s1, const char *s2) |
| 25 | { |
| 26 | size_t i = 0; |
| 27 | while (s1[i] && s2[i] && s1[i] == s2[i]) |
| 28 | i++; |
| 29 | return s1[i] - s2[i]; |
| 30 | } |
| 31 | |
| 32 | s32 strncmp(const char *str1, const char *str2, size_t n) |
| 33 | { |
| 34 | u8 c1, c2; |
| 35 | while (n-- > 0) |
| 36 | { |
| 37 | c1 = (u8) *str1++; |
| 38 | c2 = (u8) *str2++; |
| 39 | if (c1 != c2) |
| 40 | return c1 - c2; |
| 41 | if (c1 == '\0') |
| 42 | return 0; |
| 43 | } |
| 44 | return 0; |
| 45 | } |
| 46 | |
| 47 | s32 strncasecmp(const char *a, const char *b, size_t n) |
| 48 | { |
| 49 | for (size_t i = 0; i < n; i++) |
| 50 | { |
| 51 | unsigned char a_byte = tolower(c: a[i]); |
| 52 | unsigned char b_byte = tolower(c: b[i]); |
| 53 | if (!a_byte && !b_byte) |
| 54 | return 0; |
| 55 | // If only one char is null, one of the following cases applies. |
| 56 | if (a_byte < b_byte) |
| 57 | return -1; |
| 58 | if (a_byte > b_byte) |
| 59 | return 1; |
| 60 | } |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | void *memcpy(void *__restrict _dst, const void *__restrict _src, size_t n) |
| 65 | { |
| 66 | // https://github.com/eblot/newlib/blob/master/newlib/libc/string/memcpy.c |
| 67 | |
| 68 | #define UNALIGNED(X, Y) (((long) X & (sizeof(long) - 1)) | ((long) Y & (sizeof(long) - 1))) // Nonzero if either X or Y is not aligned on a "long" boundary. |
| 69 | #define BIGBLOCKSIZE (sizeof(long) << 2) // How many bytes are copied each iteration of the 4X unrolled loop. |
| 70 | #define LITTLEBLOCKSIZE (sizeof(long)) // How many bytes are copied each iteration of the word copy loop. |
| 71 | #define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) // Threshold for punting to the byte copier. |
| 72 | |
| 73 | auto dst = (char *) _dst; |
| 74 | const char *src = (const char *) _src; |
| 75 | |
| 76 | /* If the size is small, or either SRC or DST is unaligned, |
| 77 | then punt into the byte copy loop. This should be rare. */ |
| 78 | if (!TOO_SMALL(n) && !UNALIGNED(src, dst)) |
| 79 | { |
| 80 | long *aligned_dst = (long *) dst; |
| 81 | const long *aligned_src = (long *) src; |
| 82 | |
| 83 | /* Copy 4X long words at a time if possible. */ |
| 84 | while (n >= BIGBLOCKSIZE) |
| 85 | { |
| 86 | *aligned_dst++ = *aligned_src++; |
| 87 | *aligned_dst++ = *aligned_src++; |
| 88 | *aligned_dst++ = *aligned_src++; |
| 89 | *aligned_dst++ = *aligned_src++; |
| 90 | n -= BIGBLOCKSIZE; |
| 91 | } |
| 92 | |
| 93 | /* Copy one long word at a time if possible. */ |
| 94 | while (n >= LITTLEBLOCKSIZE) |
| 95 | { |
| 96 | *aligned_dst++ = *aligned_src++; |
| 97 | n -= LITTLEBLOCKSIZE; |
| 98 | } |
| 99 | |
| 100 | /* Pick up any residual with a byte copier. */ |
| 101 | dst = (char *) aligned_dst; |
| 102 | src = (char *) aligned_src; |
| 103 | } |
| 104 | |
| 105 | while (n--) |
| 106 | *dst++ = *src++; |
| 107 | |
| 108 | return _dst; |
| 109 | } |
| 110 | |
| 111 | void *memmove(void *dest, const void *source, size_t length) |
| 112 | { |
| 113 | // https://github.com/eblot/newlib/blob/master/newlib/libc/string/memmove.c |
| 114 | char *dst = static_cast<char *>(dest); |
| 115 | const char *src = static_cast<const char *>(source); |
| 116 | |
| 117 | if (src < dst && dst < src + length) |
| 118 | { |
| 119 | /* Have to copy backwards */ |
| 120 | src += length; |
| 121 | dst += length; |
| 122 | while (length--) |
| 123 | *--dst = *--src; |
| 124 | } |
| 125 | else |
| 126 | { |
| 127 | while (length--) |
| 128 | *dst++ = *src++; |
| 129 | } |
| 130 | |
| 131 | return dest; |
| 132 | } |
| 133 | |
| 134 | void *memset(void *s, int c, size_t n) |
| 135 | { |
| 136 | u8 *d = static_cast<u8 *>(s); |
| 137 | for (size_t i = 0; i < n; i++) |
| 138 | d[i] = c; |
| 139 | return s; |
| 140 | } |
| 141 | |
| 142 | int memcmp(const void *s1, const void *s2, size_t n) |
| 143 | { |
| 144 | const u8 *p1 = static_cast<const u8 *>(s1); |
| 145 | const u8 *p2 = static_cast<const u8 *>(s2); |
| 146 | |
| 147 | for (size_t i = 0; i < n; i++) |
| 148 | { |
| 149 | if (p1[i] != p2[i]) |
| 150 | return p1[i] - p2[i]; |
| 151 | } |
| 152 | return 0; |
| 153 | } |
| 154 | |
| 155 | void memzero(void *s, size_t n) |
| 156 | { |
| 157 | typedef u64 largeint_t; |
| 158 | largeint_t *lsrc = (largeint_t *) s; |
| 159 | |
| 160 | while (n >= sizeof(largeint_t)) |
| 161 | { |
| 162 | *lsrc++ = 0; |
| 163 | n -= sizeof(largeint_t); |
| 164 | } |
| 165 | |
| 166 | char *csrc = (char *) lsrc; |
| 167 | |
| 168 | while (n > 0) |
| 169 | { |
| 170 | *csrc++ = 0; |
| 171 | n--; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | void *memchr(const void *m, int c, size_t n) |
| 176 | { |
| 177 | const u8 *s = static_cast<const u8 *>(m); |
| 178 | for (size_t i = 0; i < n; i++) |
| 179 | { |
| 180 | if (s[i] == c) |
| 181 | return (void *) &s[i]; |
| 182 | } |
| 183 | return NULL; |
| 184 | } |
| 185 | |
| 186 | char *strcpy(char *__restrict dest, const char *__restrict src) |
| 187 | { |
| 188 | while (*src) |
| 189 | *dest++ = *src++; |
| 190 | *dest = 0; |
| 191 | return dest; |
| 192 | } |
| 193 | |
| 194 | char *strcat(char *__restrict dest, const char *__restrict src) |
| 195 | { |
| 196 | while (*dest) |
| 197 | dest++; |
| 198 | while (*src) |
| 199 | *dest++ = *src++; |
| 200 | *dest = 0; |
| 201 | return dest; |
| 202 | } |
| 203 | |
| 204 | char *strncpy(char *__restrict dest, const char *__restrict src, size_t n) |
| 205 | { |
| 206 | char *ret = dest; |
| 207 | while (n > 0 && *src) |
| 208 | { |
| 209 | *dest++ = *src++; |
| 210 | n--; |
| 211 | } |
| 212 | while (n > 0) |
| 213 | { |
| 214 | *dest++ = 0; |
| 215 | n--; |
| 216 | } |
| 217 | return ret; |
| 218 | } |
| 219 | |
| 220 | // not available for minimal libc |
| 221 | #ifdef __MOS_KERNEL__ |
| 222 | char *strdup(const char *src) |
| 223 | { |
| 224 | char *dst = (char *) malloc(size: strlen(str: src) + 1); |
| 225 | strcpy(dest: dst, src); |
| 226 | return dst; |
| 227 | } |
| 228 | |
| 229 | char *strndup(const char *src, size_t len) |
| 230 | { |
| 231 | char *dst = (char *) malloc(size: len + 1); |
| 232 | strncpy(dest: dst, src, n: len); |
| 233 | dst[len] = '\0'; |
| 234 | return dst; |
| 235 | } |
| 236 | #endif |
| 237 | |
| 238 | char *strchr(const char *s, int c) |
| 239 | { |
| 240 | while (*s) |
| 241 | { |
| 242 | if (*s == c) |
| 243 | return (char *) s; |
| 244 | s++; |
| 245 | } |
| 246 | return NULL; |
| 247 | } |
| 248 | |
| 249 | char *strrchr(const char *s, int c) |
| 250 | { |
| 251 | const char *last = NULL; |
| 252 | do |
| 253 | { |
| 254 | if (*s == c) |
| 255 | last = s; |
| 256 | } while (*s++); |
| 257 | return (char *) last; |
| 258 | } |
| 259 | |
| 260 | size_t strspn(const char *s, const char *accept) |
| 261 | { |
| 262 | size_t i = 0; |
| 263 | while (s[i]) |
| 264 | { |
| 265 | if (!strchr(s: accept, c: s[i])) |
| 266 | break; |
| 267 | i++; |
| 268 | } |
| 269 | return i; |
| 270 | } |
| 271 | |
| 272 | char *strpbrk(const char *s, const char *accept) |
| 273 | { |
| 274 | while (*s) |
| 275 | { |
| 276 | if (strchr(s: accept, c: *s)) |
| 277 | return (char *) s; |
| 278 | s++; |
| 279 | } |
| 280 | return NULL; |
| 281 | } |
| 282 | |
| 283 | char *strtok(char *str, const char *delim) |
| 284 | { |
| 285 | static char *last; |
| 286 | if (str == NULL) |
| 287 | str = last; |
| 288 | if (str == NULL) |
| 289 | return NULL; |
| 290 | str += strspn(s: str, accept: delim); |
| 291 | if (*str == '\0') |
| 292 | return last = NULL; |
| 293 | char *token = str; |
| 294 | str = strpbrk(s: token, accept: delim); |
| 295 | if (str == NULL) |
| 296 | last = NULL; |
| 297 | else |
| 298 | { |
| 299 | *str = '\0'; |
| 300 | last = str + 1; |
| 301 | } |
| 302 | return token; |
| 303 | } |
| 304 | |
| 305 | char *strtok_r(char *str, const char *delim, char **saveptr) |
| 306 | { |
| 307 | if (str == NULL) |
| 308 | str = *saveptr; |
| 309 | |
| 310 | if (str == NULL) |
| 311 | return NULL; |
| 312 | |
| 313 | str += strspn(s: str, accept: delim); |
| 314 | |
| 315 | if (*str == '\0') |
| 316 | return *saveptr = NULL; |
| 317 | |
| 318 | char *token = str; |
| 319 | str = strpbrk(s: token, accept: delim); |
| 320 | |
| 321 | if (str == NULL) |
| 322 | *saveptr = NULL; |
| 323 | else |
| 324 | { |
| 325 | *str = '\0'; |
| 326 | *saveptr = str + 1; |
| 327 | } |
| 328 | return token; |
| 329 | } |
| 330 | |