The Pedigree Project 0.1
lib/string.c
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/compiler.h"
21#include "pedigree/kernel/processor/types.h"
22#include "pedigree/kernel/utilities/utility.h"
23
24#include <stdarg.h>
25#include <stddef.h>
26
27extern void* malloc(size_t);
28extern void free(void*);
29
30EXPORTED_PUBLIC size_t strlen(const char* s);
31char* strcpy(char* dest, const char* src);
32EXPORTED_PUBLIC char* strncpy(char* dest, const char* src, size_t len);
33EXPORTED_PUBLIC unsigned long strtoul(const char* nptr, char** endptr, int base);
34EXPORTED_PUBLIC int strcmp(const char* p1, const char* p2);
35EXPORTED_PUBLIC int strncmp(const char* p1, const char* p2, size_t n);
36char* strcat(char* dest, const char* src);
37char* strncat(char* dest, const char* src, size_t n);
38char* strchr(const char* str, int target);
39char* strrchr(const char* str, int target);
40int vsprintf(char* buf, const char* fmt, va_list arg);
41unsigned long strtoul(const char* nptr, char** endptr, int base);
42
43#define ULONG_MAX -1
44
45char toUpper(char c) {
46 if (c < 'a' || c > 'z')
47 return c; // special chars
48 c += ('A' - 'a');
49 return c;
50}
51
52char toLower(char c) {
53 if (c < 'A' || c > 'Z')
54 return c; // special chars
55 c -= ('A' - 'a');
56 return c;
57}
58
59int max(size_t a, size_t b) {
60 return a > b ? a : b;
61}
62
63int min(size_t a, size_t b) {
64 return a > b ? b : a;
65}
66
67WEAK size_t _StringLength(const char* src) {
68 if (!src) {
69 return 0;
70 }
71
72 // Unrolled loop that still avoids reading past the end of src (instead of
73 // e.g. doing bitmasks with 64-bit views of src).
74 const char* orig = src;
75 size_t result = 0;
76 while (1) {
77#define UNROLL(n) \
78 if (!*(src + n)) \
79 return (src + n) - orig;
80 UNROLL(0);
81 UNROLL(1);
82 UNROLL(2);
83 UNROLL(3);
84 UNROLL(4);
85 UNROLL(5);
86 UNROLL(6);
87 UNROLL(7);
88#undef UNROLL
89 src += 8;
90 }
91}
92
93WEAK size_t _BoundedStringLength(const char* src, size_t maxlen) {
94 if (UNLIKELY(!src)) {
95 return 0;
96 }
97
98 size_t n = 0;
99 while ((n < maxlen) && *src++) {
100 ++n;
101 }
102
103 return n;
104}
105
106char* StringCopy(char* dest, const char* src) {
107 char* orig_dest = dest;
108 while (*src) {
109 *dest = *src;
110 ++dest;
111 ++src;
112 }
113 *dest = '\0';
114
115 return orig_dest;
116}
117
118char* StringCopyN(char* dest, const char* src, size_t len) {
119 char* orig_dest = dest;
120 while (len && LIKELY(*src)) {
121 *dest = *src;
122 --len;
123 ++dest;
124 ++src;
125 }
126
127 // zero-pad if we hit the end of src but len is still non-zero
128 while (len) {
129 *dest = '\0';
130 --len;
131 ++dest;
132 }
133
134 return orig_dest;
135}
136
137int StringFormat(char* buf, const char* fmt, ...) {
138 va_list args;
139 int i;
140
141 va_start(args, fmt);
142 i = VStringFormat(buf, fmt, args);
143 va_end(args);
144
145 return i;
146}
147
148WEAK int StringCompare(const char* restrict p1, const char* restrict p2) {
149 if (p1 == p2)
150 return 0;
151
152 char c1 = 0, c2 = 0;
153 while (1) {
154 c1 = *p1++;
155 c2 = *p2++;
156 if ((!c1) || (c1 != c2)) {
157 break;
158 }
159 }
160
161 return c1 - c2;
162}
163
164WEAK int StringCompareN(const char* restrict p1, const char* restrict p2, size_t n) {
165 if (!n) {
166 return 0;
167 } else if (p1 == p2) {
168 return 0;
169 }
170
171 size_t i;
172 char c1 = 0, c2 = 0;
173 for (i = 0; i < n; ++i) {
174 c1 = p1[i];
175 c2 = p2[i];
176
177 if ((!c1) || (c1 != c2)) {
178 break;
179 }
180 }
181
182 return c1 - c2;
183}
184
185WEAK int StringCompareNOffset(const char* restrict p1, const char* restrict p2, size_t n,
186 size_t* offset) {
187 if (!n) {
188 return 0;
189 } else if (p1 == p2) {
190 return 0;
191 }
192
193 size_t i;
194 char c1 = 0, c2 = 0;
195 for (i = 0; i < n; ++i) {
196 c1 = p1[i];
197 c2 = p2[i];
198
199 if ((!c1) || (c1 != c2)) {
200 break;
201 }
202 }
203
204 if (offset) {
205 *offset = i;
206 }
207 return c1 - c2;
208}
209
210WEAK int StringMatch(const char* restrict p1, const char* restrict p2) {
211 return StringCompare(p1, p2) == 0 ? 0 : 1;
212}
213
214WEAK int StringMatchN(const char* restrict p1, const char* restrict p2, size_t n) {
215 if (!n) {
216 return 0;
217 } else if (p1 == p2) {
218 return 0;
219 }
220
221 size_t i;
222 unsigned c1 = 0, c2 = 0;
223 for (i = 0; i < n; ++i) {
224 c1 = p1[i];
225 c2 = p2[i];
226
227 if ((!c1) || (c1 != c2)) {
228 break;
229 }
230 }
231
232 return (c1 == c2) ? 0 : 1;
233}
234
235WEAK int StringMatchNOffset(const char* restrict p1, const char* restrict p2, size_t n,
236 size_t* offset) {
237 return StringCompareNOffset(p1, p2, n, offset) == 0 ? 0 : 1;
238}
239
240char* StringConcat(char* dest, const char* src) {
241 char* origDest = dest;
242 while (*dest)
243 ++dest;
244 while (src && *src) {
245 *dest++ = *src++;
246 }
247
248 *dest++ = 0;
249
250 return origDest;
251}
252
253char* StringConcatN(char* dest, const char* src, size_t n) {
254 char* origDest = dest;
255 while (*dest)
256 ++dest;
257 while (src && *src && n) {
258 *dest++ = *src++;
259 --n;
260 }
261
262 *dest++ = 0;
263
264 return origDest;
265}
266
267#if !(UTILITY_LINUX && defined(__APPLE__))
268int isspace(int c) {
269 return (c == ' ' || c == '\n' || c == '\r' || c == '\t');
270}
271
272int isupper(int c) {
273 return (c >= 'A' && c <= 'Z');
274}
275
276int islower(int c) {
277 return (c >= 'a' && c <= 'z');
278}
279
280int isdigit(int c) {
281 return (c >= '0' && c <= '9');
282}
283
284int isalpha(int c) {
285 return isupper(c) || islower(c) || isdigit(c);
286}
287#endif
288
289// Intentionally casting const char * to char * in these functions, don't warn
290#pragma GCC diagnostic push
291#pragma GCC diagnostic ignored "-Wcast-qual"
292
293unsigned long StringToUnsignedLong(const char* nptr, char** endptr, int base) {
294 register const char* s = nptr;
295 register unsigned long acc;
296 register int c;
297 register unsigned long cutoff;
298 register int neg = 0, any, cutlim;
299
300 /*
301 * See strtol for comments as to the logic used.
302 */
303 do {
304 c = *s++;
305 } while (isspace(c));
306 if (c == '-') {
307 neg = 1;
308 c = *s++;
309 } else if (c == '+')
310 c = *s++;
311 if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) {
312 c = s[1];
313 s += 2;
314 base = 16;
315 }
316 if (base == 0)
317 base = c == '0' ? 8 : 10;
318 cutoff = (unsigned long)ULONG_MAX / (unsigned long)base;
319 cutlim = (unsigned long)ULONG_MAX % (unsigned long)base;
320 for (acc = 0, any = 0;; c = *s++) {
321 if (isdigit(c))
322 c -= '0';
323 else if (isalpha(c))
324 c -= isupper(c) ? 'A' - 10 : 'a' - 10;
325 else
326 break;
327 if (c >= base)
328 break;
329 if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
330 any = -1;
331 else {
332 any = 1;
333 acc *= base;
334 acc += c;
335 }
336 }
337 if (any < 0) {
338 acc = ULONG_MAX;
339 } else if (neg)
340 acc = -acc;
341 if (endptr != 0)
342 *endptr = (char*)(any ? s - 1 : nptr);
343
344 return (acc);
345}
346
347char* StringFind(const char* str, int target) {
348 const char* s;
349 char ch;
350 while (1) {
351#define UNROLL(n) \
352 s = str + n; \
353 ch = *s; \
354 if (!ch) \
355 return NULL; \
356 if (ch == target) \
357 return (char*)s;
358
359 UNROLL(0);
360 UNROLL(1);
361 UNROLL(2);
362 UNROLL(3);
363 UNROLL(4);
364 UNROLL(5);
365 UNROLL(6);
366 UNROLL(7);
367#undef UNROLL
368 str += 8;
369 }
370}
371
372char* StringReverseFind(const char* str, int target) {
373 // StringLength must traverse the entire string once to find the length,
374 // so rather than finding the length and then traversing in reverse, we just
375 // traverse the string once. This gives a small performance boost.
376 const char* s;
377 const char* result = NULL;
378 char ch;
379 while (1) {
380#define UNROLL(n) \
381 s = str + n; \
382 ch = *s; \
383 if (!ch) \
384 return (char*)result; \
385 if (ch == target) \
386 result = s;
387
388 UNROLL(0);
389 UNROLL(1);
390 UNROLL(2);
391 UNROLL(3);
392 UNROLL(4);
393 UNROLL(5);
394 UNROLL(6);
395 UNROLL(7);
396#undef UNROLL
397 str += 8;
398 }
399}
400
401#pragma GCC diagnostic pop
402
403int StringContains(const char* str, const char* search) {
404 size_t alen = StringLength(str);
405 size_t blen = StringLength(search);
406 return StringContainsN(str, alen, search, blen);
407}
408
409static int isPrefix(const char* word, size_t wordLength, size_t pos) {
410 size_t suffixLength = wordLength - pos;
411 return StringCompareN(word, word + pos, suffixLength) == 0 ? 1 : 0;
412}
413
414static size_t suffixLength(const char* word, size_t wordLength, size_t pos) {
415 size_t i = 0;
416 for (; (word[pos - i] == word[wordLength - 1 - i]) && (i < pos); i++)
417 ;
418 return i;
419}
420
421int StringContainsN(const char* str, size_t len, const char* search, size_t slen) {
422 // Quick exit cases (these shouldn't really be "contains" queries).
423 if (len < slen) {
424 return 0;
425 } else if (!slen) {
426 return 1;
427 } else if (!len) {
428 return 0;
429 } else if (len == slen) {
430 return StringCompareN(str, search, slen) == 0;
431 }
432
433 // Boyer-Moore string searching (around 2x faster than a naive search)
434 size_t delta1[256];
435 size_t* delta2 = (size_t*)malloc(slen * sizeof(size_t));
436
437 for (size_t i = 0; i < 256; ++i) {
438 delta1[i] = slen;
439 }
440
441 // Build delta1 array (deltas of rightmost unique character in pattern).
442 for (size_t i = 0; i < slen; ++i) {
443 delta1[(int)search[i]] = slen - 1 - i;
444 }
445
446 // Build delta2 array (full match alignment).
447 ByteSet(delta2, 0, slen * sizeof(size_t));
448
449 ssize_t lastPrefix = slen - 1;
450 for (ssize_t i = slen - 1; i >= 0; --i) {
451 if (isPrefix(search, slen, i + 1)) {
452 lastPrefix = i + 1;
453 }
454 delta2[i] = lastPrefix + (slen - 1 - i);
455 }
456 for (size_t i = 0; i < slen - 1; ++i) {
457 size_t suffixLen = suffixLength(search, slen, i);
458 if (search[i - suffixLen] != search[slen - 1 - suffixLen]) {
459 delta2[slen - 1 - suffixLen] = slen - 1 - i + suffixLen;
460 }
461 }
462
463 for (size_t i = slen - 1; i < len;) {
464 ssize_t j = slen - 1;
465 while (j >= 0 && (str[i] == search[j])) {
466 --i;
467 --j;
468 }
469
470 if (j < 0) {
471 free(delta2);
472 return 1;
473 }
474
475 i += max(delta1[(int)str[i]], delta2[j]);
476 }
477
478 free(delta2);
479 return 0;
480}
481
482int StringCompareCase(const char* restrict s1, const char* restrict s2, int sensitive,
483 size_t length, size_t* offset) {
484 // Case-sensitive compare is just strncmp, basically.
485 if (LIKELY(sensitive)) {
486 return StringCompareNOffset(s1, s2, length, offset);
487 }
488
489 if (!length) {
490 return 0;
491 } else if (s1 == s2) {
492 if (offset) {
493 *offset = StringLength(s1);
494 }
495 return 0;
496 } else if (!s1) {
497 return -1;
498 } else if (!s2) {
499 return 1;
500 }
501
502 static size_t local = 0;
503 if (UNLIKELY(!offset)) {
504 offset = &local;
505 }
506
507 size_t i;
508 char c1 = 0, c2 = 0, r1 = 0, r2 = 0;
509 for (i = 0; i < length; ++i) {
510 r1 = s1[i];
511 r2 = s2[i];
512 c1 = toLower(r1);
513 c2 = toLower(r2);
514
515 if (c1 != c2) {
516 if (offset) {
517 *offset = i;
518 }
519
520 break;
521 }
522
523 if (!c1) {
524 break;
525 }
526 }
527
528 return r1 - r2;
529}
530
531size_t nextCharacter(const char* s, size_t i) {
532 if (UNLIKELY(!s)) {
533 return i;
534 }
535
536 // UTF-8 version of getting the next character
537 const uint8_t* u8buf = (const uint8_t*)s;
538 if (LIKELY(u8buf[i] <= 0x7F)) {
539 return i + 1;
540 } else if ((u8buf[i] & 0xC0) == 0xC0) {
541 if ((u8buf[i] & 0xF8) == 0xF0) {
542 return i + 4; // 4-byte sequence
543 } else if ((u8buf[i] & 0xF0) == 0xE0) {
544 return i + 3;
545 } else {
546 return i + 2;
547 }
548 }
549 return i + 1;
550}
551
552size_t prevCharacter(const char* s, size_t i) {
553 if (!s) {
554 return i;
555 }
556
557 // TODO handle multibyte chars.
558 return i - 1;
559}
560
561#if !UTILITY_LINUX
562// Provide forwarding functions to handle GCC optimising things.
563size_t strlen(const char* s) {
564 return StringLength(s);
565}
566
567char* strcpy(char* dest, const char* src) {
568 return StringCopy(dest, src);
569}
570
571char* strncpy(char* dest, const char* src, size_t len) {
572 return StringCopyN(dest, src, len);
573}
574
575int strcmp(const char* p1, const char* p2) {
576 return StringCompare(p1, p2);
577}
578
579int strncmp(const char* p1, const char* p2, size_t n) {
580 return StringCompareN(p1, p2, n);
581}
582
583char* strcat(char* dest, const char* src) {
584 return StringConcat(dest, src);
585}
586
587char* strncat(char* dest, const char* src, size_t n) {
588 return StringConcatN(dest, src, n);
589}
590
591char* strchr(const char* str, int target) {
592 return StringFind(str, target);
593}
594
595char* strrchr(const char* str, int target) {
596 return StringReverseFind(str, target);
597}
598
599int vsprintf(char* buf, const char* fmt, va_list arg) {
600 return VStringFormat(buf, fmt, arg);
601}
602
603unsigned long strtoul(const char* nptr, char** endptr, int base) {
604 return StringToUnsignedLong(nptr, endptr, base);
605}
606#endif