xref: /freebsd/contrib/llvm-project/openmp/runtime/src/kmp_str.cpp (revision 6ba2210ee039f2f12878c217bcf058e9c8b26b29)
1 /*
2  * kmp_str.cpp -- String manipulation routines.
3  */
4 
5 //===----------------------------------------------------------------------===//
6 //
7 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
8 // See https://llvm.org/LICENSE.txt for license information.
9 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "kmp_str.h"
14 
15 #include <stdarg.h> // va_*
16 #include <stdio.h> // vsnprintf()
17 #include <stdlib.h> // malloc(), realloc()
18 
19 #include "kmp.h"
20 #include "kmp_i18n.h"
21 
22 /* String buffer.
23 
24    Usage:
25 
26    // Declare buffer and initialize it.
27    kmp_str_buf_t  buffer;
28    __kmp_str_buf_init( & buffer );
29 
30    // Print to buffer.
31    __kmp_str_buf_print(& buffer, "Error in file \"%s\" line %d\n", "foo.c", 12);
32    __kmp_str_buf_print(& buffer, "    <%s>\n", line);
33 
34    // Use buffer contents. buffer.str is a pointer to data, buffer.used is a
35    // number of printed characters (not including terminating zero).
36    write( fd, buffer.str, buffer.used );
37 
38    // Free buffer.
39    __kmp_str_buf_free( & buffer );
40 
41    // Alternatively, you can detach allocated memory from buffer:
42    __kmp_str_buf_detach( & buffer );
43    return buffer.str;    // That memory should be freed eventually.
44 
45    Notes:
46 
47    * Buffer users may use buffer.str and buffer.used. Users should not change
48      any fields of buffer directly.
49    * buffer.str is never NULL. If buffer is empty, buffer.str points to empty
50      string ("").
51    * For performance reasons, buffer uses stack memory (buffer.bulk) first. If
52      stack memory is exhausted, buffer allocates memory on heap by malloc(), and
53      reallocates it by realloc() as amount of used memory grows.
54    * Buffer doubles amount of allocated memory each time it is exhausted.
55 */
56 
57 // TODO: __kmp_str_buf_print() can use thread local memory allocator.
58 
59 #define KMP_STR_BUF_INVARIANT(b)                                               \
60   {                                                                            \
61     KMP_DEBUG_ASSERT((b)->str != NULL);                                        \
62     KMP_DEBUG_ASSERT((b)->size >= sizeof((b)->bulk));                          \
63     KMP_DEBUG_ASSERT((b)->size % sizeof((b)->bulk) == 0);                      \
64     KMP_DEBUG_ASSERT((unsigned)(b)->used < (b)->size);                         \
65     KMP_DEBUG_ASSERT(                                                          \
66         (b)->size == sizeof((b)->bulk) ? (b)->str == &(b)->bulk[0] : 1);       \
67     KMP_DEBUG_ASSERT((b)->size > sizeof((b)->bulk) ? (b)->str != &(b)->bulk[0] \
68                                                    : 1);                       \
69   }
70 
71 void __kmp_str_buf_clear(kmp_str_buf_t *buffer) {
72   KMP_STR_BUF_INVARIANT(buffer);
73   if (buffer->used > 0) {
74     buffer->used = 0;
75     buffer->str[0] = 0;
76   }
77   KMP_STR_BUF_INVARIANT(buffer);
78 } // __kmp_str_buf_clear
79 
80 void __kmp_str_buf_reserve(kmp_str_buf_t *buffer, size_t size) {
81   KMP_STR_BUF_INVARIANT(buffer);
82   KMP_DEBUG_ASSERT(size >= 0);
83 
84   if (buffer->size < (unsigned int)size) {
85     // Calculate buffer size.
86     do {
87       buffer->size *= 2;
88     } while (buffer->size < (unsigned int)size);
89 
90     // Enlarge buffer.
91     if (buffer->str == &buffer->bulk[0]) {
92       buffer->str = (char *)KMP_INTERNAL_MALLOC(buffer->size);
93       if (buffer->str == NULL) {
94         KMP_FATAL(MemoryAllocFailed);
95       }
96       KMP_MEMCPY_S(buffer->str, buffer->size, buffer->bulk, buffer->used + 1);
97     } else {
98       buffer->str = (char *)KMP_INTERNAL_REALLOC(buffer->str, buffer->size);
99       if (buffer->str == NULL) {
100         KMP_FATAL(MemoryAllocFailed);
101       }
102     }
103   }
104 
105   KMP_DEBUG_ASSERT(buffer->size > 0);
106   KMP_DEBUG_ASSERT(buffer->size >= (unsigned)size);
107   KMP_STR_BUF_INVARIANT(buffer);
108 } // __kmp_str_buf_reserve
109 
110 void __kmp_str_buf_detach(kmp_str_buf_t *buffer) {
111   KMP_STR_BUF_INVARIANT(buffer);
112 
113   // If internal bulk is used, allocate memory and copy it.
114   if (buffer->size <= sizeof(buffer->bulk)) {
115     buffer->str = (char *)KMP_INTERNAL_MALLOC(buffer->size);
116     if (buffer->str == NULL) {
117       KMP_FATAL(MemoryAllocFailed);
118     }
119     KMP_MEMCPY_S(buffer->str, buffer->size, buffer->bulk, buffer->used + 1);
120   }
121 } // __kmp_str_buf_detach
122 
123 void __kmp_str_buf_free(kmp_str_buf_t *buffer) {
124   KMP_STR_BUF_INVARIANT(buffer);
125   if (buffer->size > sizeof(buffer->bulk)) {
126     KMP_INTERNAL_FREE(buffer->str);
127   }
128   buffer->str = buffer->bulk;
129   buffer->size = sizeof(buffer->bulk);
130   buffer->used = 0;
131   KMP_STR_BUF_INVARIANT(buffer);
132 } // __kmp_str_buf_free
133 
134 void __kmp_str_buf_cat(kmp_str_buf_t *buffer, char const *str, size_t len) {
135   KMP_STR_BUF_INVARIANT(buffer);
136   KMP_DEBUG_ASSERT(str != NULL);
137   KMP_DEBUG_ASSERT(len >= 0);
138 
139   __kmp_str_buf_reserve(buffer, buffer->used + len + 1);
140   KMP_MEMCPY(buffer->str + buffer->used, str, len);
141   buffer->str[buffer->used + len] = 0;
142   __kmp_type_convert(buffer->used + len, &(buffer->used));
143   KMP_STR_BUF_INVARIANT(buffer);
144 } // __kmp_str_buf_cat
145 
146 void __kmp_str_buf_catbuf(kmp_str_buf_t *dest, const kmp_str_buf_t *src) {
147   KMP_DEBUG_ASSERT(dest);
148   KMP_DEBUG_ASSERT(src);
149   KMP_STR_BUF_INVARIANT(dest);
150   KMP_STR_BUF_INVARIANT(src);
151   if (!src->str || !src->used)
152     return;
153   __kmp_str_buf_reserve(dest, dest->used + src->used + 1);
154   KMP_MEMCPY(dest->str + dest->used, src->str, src->used);
155   dest->str[dest->used + src->used] = 0;
156   dest->used += src->used;
157   KMP_STR_BUF_INVARIANT(dest);
158 } // __kmp_str_buf_catbuf
159 
160 // Return the number of characters written
161 int __kmp_str_buf_vprint(kmp_str_buf_t *buffer, char const *format,
162                          va_list args) {
163   int rc;
164   KMP_STR_BUF_INVARIANT(buffer);
165 
166   for (;;) {
167     int const free = buffer->size - buffer->used;
168     int size;
169 
170     // Try to format string.
171     {
172 /* On Linux* OS Intel(R) 64, vsnprintf() modifies args argument, so vsnprintf()
173    crashes if it is called for the second time with the same args. To prevent
174    the crash, we have to pass a fresh intact copy of args to vsnprintf() on each
175    iteration.
176 
177    Unfortunately, standard va_copy() macro is not available on Windows* OS.
178    However, it seems vsnprintf() does not modify args argument on Windows* OS.
179 */
180 
181 #if !KMP_OS_WINDOWS
182       va_list _args;
183       va_copy(_args, args); // Make copy of args.
184 #define args _args // Substitute args with its copy, _args.
185 #endif // KMP_OS_WINDOWS
186       rc = KMP_VSNPRINTF(buffer->str + buffer->used, free, format, args);
187 #if !KMP_OS_WINDOWS
188 #undef args // Remove substitution.
189       va_end(_args);
190 #endif // KMP_OS_WINDOWS
191     }
192 
193     // No errors, string has been formatted.
194     if (rc >= 0 && rc < free) {
195       buffer->used += rc;
196       break;
197     }
198 
199     // Error occurred, buffer is too small.
200     if (rc >= 0) {
201       // C99-conforming implementation of vsnprintf returns required buffer size
202       size = buffer->used + rc + 1;
203     } else {
204       // Older implementations just return -1. Double buffer size.
205       size = buffer->size * 2;
206     }
207 
208     // Enlarge buffer.
209     __kmp_str_buf_reserve(buffer, size);
210 
211     // And try again.
212   }
213 
214   KMP_DEBUG_ASSERT(buffer->size > 0);
215   KMP_STR_BUF_INVARIANT(buffer);
216   return rc;
217 } // __kmp_str_buf_vprint
218 
219 // Return the number of characters written
220 int __kmp_str_buf_print(kmp_str_buf_t *buffer, char const *format, ...) {
221   int rc;
222   va_list args;
223   va_start(args, format);
224   rc = __kmp_str_buf_vprint(buffer, format, args);
225   va_end(args);
226   return rc;
227 } // __kmp_str_buf_print
228 
229 /* The function prints specified size to buffer. Size is expressed using biggest
230    possible unit, for example 1024 is printed as "1k". */
231 void __kmp_str_buf_print_size(kmp_str_buf_t *buf, size_t size) {
232   char const *names[] = {"", "k", "M", "G", "T", "P", "E", "Z", "Y"};
233   int const units = sizeof(names) / sizeof(char const *);
234   int u = 0;
235   if (size > 0) {
236     while ((size % 1024 == 0) && (u + 1 < units)) {
237       size = size / 1024;
238       ++u;
239     }
240   }
241 
242   __kmp_str_buf_print(buf, "%" KMP_SIZE_T_SPEC "%s", size, names[u]);
243 } // __kmp_str_buf_print_size
244 
245 void __kmp_str_fname_init(kmp_str_fname_t *fname, char const *path) {
246   fname->path = NULL;
247   fname->dir = NULL;
248   fname->base = NULL;
249 
250   if (path != NULL) {
251     char *slash = NULL; // Pointer to the last character of dir.
252     char *base = NULL; // Pointer to the beginning of basename.
253     fname->path = __kmp_str_format("%s", path);
254     // Original code used strdup() function to copy a string, but on Windows* OS
255     // Intel(R) 64 it causes assertion id debug heap, so I had to replace
256     // strdup with __kmp_str_format().
257     if (KMP_OS_WINDOWS) {
258       __kmp_str_replace(fname->path, '\\', '/');
259     }
260     fname->dir = __kmp_str_format("%s", fname->path);
261     slash = strrchr(fname->dir, '/');
262     if (KMP_OS_WINDOWS &&
263         slash == NULL) { // On Windows* OS, if slash not found,
264       char first = (char)TOLOWER(fname->dir[0]); // look for drive.
265       if ('a' <= first && first <= 'z' && fname->dir[1] == ':') {
266         slash = &fname->dir[1];
267       }
268     }
269     base = (slash == NULL ? fname->dir : slash + 1);
270     fname->base = __kmp_str_format("%s", base); // Copy basename
271     *base = 0; // and truncate dir.
272   }
273 
274 } // kmp_str_fname_init
275 
276 void __kmp_str_fname_free(kmp_str_fname_t *fname) {
277   __kmp_str_free(&fname->path);
278   __kmp_str_free(&fname->dir);
279   __kmp_str_free(&fname->base);
280 } // kmp_str_fname_free
281 
282 int __kmp_str_fname_match(kmp_str_fname_t const *fname, char const *pattern) {
283   int dir_match = 1;
284   int base_match = 1;
285 
286   if (pattern != NULL) {
287     kmp_str_fname_t ptrn;
288     __kmp_str_fname_init(&ptrn, pattern);
289     dir_match = strcmp(ptrn.dir, "*/") == 0 ||
290                 (fname->dir != NULL && __kmp_str_eqf(fname->dir, ptrn.dir));
291     base_match = strcmp(ptrn.base, "*") == 0 ||
292                  (fname->base != NULL && __kmp_str_eqf(fname->base, ptrn.base));
293     __kmp_str_fname_free(&ptrn);
294   }
295 
296   return dir_match && base_match;
297 } // __kmp_str_fname_match
298 
299 // Get the numeric fields from source location string.
300 // For clang these fields are Line/Col of the start of the construct.
301 // For icc these are LineBegin/LineEnd of the construct.
302 // Function is fast as it does not duplicate string (which involves memory
303 // allocation), and parses the string in place.
304 void __kmp_str_loc_numbers(char const *Psource, int *LineBeg,
305                            int *LineEndOrCol) {
306   char *Str;
307   KMP_DEBUG_ASSERT(LineBeg);
308   KMP_DEBUG_ASSERT(LineEndOrCol);
309   // Parse Psource string ";file;func;line;line_end_or_column;;" to get
310   // numbers only, skipping string fields "file" and "func".
311 
312   // Find 1-st semicolon.
313   KMP_DEBUG_ASSERT(Psource);
314 #ifdef __cplusplus
315   Str = strchr(CCAST(char *, Psource), ';');
316 #else
317   Str = strchr(Psource, ';');
318 #endif
319   // Check returned pointer to see if the format of Psource is broken.
320   if (Str) {
321     // Find 2-nd semicolon.
322     Str = strchr(Str + 1, ';');
323   }
324   if (Str) {
325     // Find 3-rd semicolon.
326     Str = strchr(Str + 1, ';');
327   }
328   if (Str) {
329     // Read begin line number.
330     *LineBeg = atoi(Str + 1);
331     // Find 4-th semicolon.
332     Str = strchr(Str + 1, ';');
333   } else {
334     // Broken format of input string, cannot read the number.
335     *LineBeg = 0;
336   }
337   if (Str) {
338     // Read end line or column number.
339     *LineEndOrCol = atoi(Str + 1);
340   } else {
341     // Broken format of input string, cannot read the number.
342     *LineEndOrCol = 0;
343   }
344 }
345 
346 kmp_str_loc_t __kmp_str_loc_init(char const *psource, bool init_fname) {
347   kmp_str_loc_t loc;
348 
349   loc._bulk = NULL;
350   loc.file = NULL;
351   loc.func = NULL;
352   loc.line = 0;
353   loc.col = 0;
354 
355   if (psource != NULL) {
356     char *str = NULL;
357     char *dummy = NULL;
358     char *line = NULL;
359     char *col = NULL;
360 
361     // Copy psource to keep it intact.
362     loc._bulk = __kmp_str_format("%s", psource);
363 
364     // Parse psource string: ";file;func;line;col;;"
365     str = loc._bulk;
366     __kmp_str_split(str, ';', &dummy, &str);
367     __kmp_str_split(str, ';', &loc.file, &str);
368     __kmp_str_split(str, ';', &loc.func, &str);
369     __kmp_str_split(str, ';', &line, &str);
370     __kmp_str_split(str, ';', &col, &str);
371 
372     // Convert line and col into numberic values.
373     if (line != NULL) {
374       loc.line = atoi(line);
375       if (loc.line < 0) {
376         loc.line = 0;
377       }
378     }
379     if (col != NULL) {
380       loc.col = atoi(col);
381       if (loc.col < 0) {
382         loc.col = 0;
383       }
384     }
385   }
386 
387   __kmp_str_fname_init(&loc.fname, init_fname ? loc.file : NULL);
388 
389   return loc;
390 } // kmp_str_loc_init
391 
392 void __kmp_str_loc_free(kmp_str_loc_t *loc) {
393   __kmp_str_fname_free(&loc->fname);
394   __kmp_str_free(&(loc->_bulk));
395   loc->file = NULL;
396   loc->func = NULL;
397 } // kmp_str_loc_free
398 
399 /* This function is intended to compare file names. On Windows* OS file names
400    are case-insensitive, so functions performs case-insensitive comparison. On
401    Linux* OS it performs case-sensitive comparison. Note: The function returns
402    *true* if strings are *equal*. */
403 int __kmp_str_eqf( // True, if strings are equal, false otherwise.
404     char const *lhs, // First string.
405     char const *rhs // Second string.
406     ) {
407   int result;
408 #if KMP_OS_WINDOWS
409   result = (_stricmp(lhs, rhs) == 0);
410 #else
411   result = (strcmp(lhs, rhs) == 0);
412 #endif
413   return result;
414 } // __kmp_str_eqf
415 
416 /* This function is like sprintf, but it *allocates* new buffer, which must be
417    freed eventually by __kmp_str_free(). The function is very convenient for
418    constructing strings, it successfully replaces strdup(), strcat(), it frees
419    programmer from buffer allocations and helps to avoid buffer overflows.
420    Examples:
421 
422    str = __kmp_str_format("%s", orig); //strdup() doesn't care about buffer size
423    __kmp_str_free( & str );
424    str = __kmp_str_format( "%s%s", orig1, orig2 ); // strcat(), doesn't care
425                                                    // about buffer size.
426    __kmp_str_free( & str );
427    str = __kmp_str_format( "%s/%s.txt", path, file ); // constructing string.
428    __kmp_str_free( & str );
429 
430    Performance note:
431    This function allocates memory with malloc() calls, so do not call it from
432    performance-critical code. In performance-critical code consider using
433    kmp_str_buf_t instead, since it uses stack-allocated buffer for short
434    strings.
435 
436    Why does this function use malloc()?
437    1. __kmp_allocate() returns cache-aligned memory allocated with malloc().
438       There are no reasons in using __kmp_allocate() for strings due to extra
439       overhead while cache-aligned memory is not necessary.
440    2. __kmp_thread_malloc() cannot be used because it requires pointer to thread
441       structure. We need to perform string operations during library startup
442       (for example, in __kmp_register_library_startup()) when no thread
443       structures are allocated yet.
444    So standard malloc() is the only available option.
445 */
446 
447 char *__kmp_str_format( // Allocated string.
448     char const *format, // Format string.
449     ... // Other parameters.
450     ) {
451   va_list args;
452   int size = 512;
453   char *buffer = NULL;
454   int rc;
455 
456   // Allocate buffer.
457   buffer = (char *)KMP_INTERNAL_MALLOC(size);
458   if (buffer == NULL) {
459     KMP_FATAL(MemoryAllocFailed);
460   }
461 
462   for (;;) {
463     // Try to format string.
464     va_start(args, format);
465     rc = KMP_VSNPRINTF(buffer, size, format, args);
466     va_end(args);
467 
468     // No errors, string has been formatted.
469     if (rc >= 0 && rc < size) {
470       break;
471     }
472 
473     // Error occurred, buffer is too small.
474     if (rc >= 0) {
475       // C99-conforming implementation of vsnprintf returns required buffer
476       // size.
477       size = rc + 1;
478     } else {
479       // Older implementations just return -1.
480       size = size * 2;
481     }
482 
483     // Enlarge buffer and try again.
484     buffer = (char *)KMP_INTERNAL_REALLOC(buffer, size);
485     if (buffer == NULL) {
486       KMP_FATAL(MemoryAllocFailed);
487     }
488   }
489 
490   return buffer;
491 } // func __kmp_str_format
492 
493 void __kmp_str_free(char **str) {
494   KMP_DEBUG_ASSERT(str != NULL);
495   KMP_INTERNAL_FREE(*str);
496   *str = NULL;
497 } // func __kmp_str_free
498 
499 /* If len is zero, returns true iff target and data have exact case-insensitive
500    match. If len is negative, returns true iff target is a case-insensitive
501    substring of data. If len is positive, returns true iff target is a
502    case-insensitive substring of data or vice versa, and neither is shorter than
503    len. */
504 int __kmp_str_match(char const *target, int len, char const *data) {
505   int i;
506   if (target == NULL || data == NULL) {
507     return FALSE;
508   }
509   for (i = 0; target[i] && data[i]; ++i) {
510     if (TOLOWER(target[i]) != TOLOWER(data[i])) {
511       return FALSE;
512     }
513   }
514   return ((len > 0) ? i >= len : (!target[i] && (len || !data[i])));
515 } // __kmp_str_match
516 
517 int __kmp_str_match_false(char const *data) {
518   int result =
519       __kmp_str_match("false", 1, data) || __kmp_str_match("off", 2, data) ||
520       __kmp_str_match("0", 1, data) || __kmp_str_match(".false.", 2, data) ||
521       __kmp_str_match(".f.", 2, data) || __kmp_str_match("no", 1, data) ||
522       __kmp_str_match("disabled", 0, data);
523   return result;
524 } // __kmp_str_match_false
525 
526 int __kmp_str_match_true(char const *data) {
527   int result =
528       __kmp_str_match("true", 1, data) || __kmp_str_match("on", 2, data) ||
529       __kmp_str_match("1", 1, data) || __kmp_str_match(".true.", 2, data) ||
530       __kmp_str_match(".t.", 2, data) || __kmp_str_match("yes", 1, data) ||
531       __kmp_str_match("enabled", 0, data);
532   return result;
533 } // __kmp_str_match_true
534 
535 void __kmp_str_replace(char *str, char search_for, char replace_with) {
536   char *found = NULL;
537 
538   found = strchr(str, search_for);
539   while (found) {
540     *found = replace_with;
541     found = strchr(found + 1, search_for);
542   }
543 } // __kmp_str_replace
544 
545 void __kmp_str_split(char *str, // I: String to split.
546                      char delim, // I: Character to split on.
547                      char **head, // O: Pointer to head (may be NULL).
548                      char **tail // O: Pointer to tail (may be NULL).
549                      ) {
550   char *h = str;
551   char *t = NULL;
552   if (str != NULL) {
553     char *ptr = strchr(str, delim);
554     if (ptr != NULL) {
555       *ptr = 0;
556       t = ptr + 1;
557     }
558   }
559   if (head != NULL) {
560     *head = h;
561   }
562   if (tail != NULL) {
563     *tail = t;
564   }
565 } // __kmp_str_split
566 
567 /* strtok_r() is not available on Windows* OS. This function reimplements
568    strtok_r(). */
569 char *__kmp_str_token(
570     char *str, // String to split into tokens. Note: String *is* modified!
571     char const *delim, // Delimiters.
572     char **buf // Internal buffer.
573     ) {
574   char *token = NULL;
575 #if KMP_OS_WINDOWS
576   // On Windows* OS there is no strtok_r() function. Let us implement it.
577   if (str != NULL) {
578     *buf = str; // First call, initialize buf.
579   }
580   *buf += strspn(*buf, delim); // Skip leading delimiters.
581   if (**buf != 0) { // Rest of the string is not yet empty.
582     token = *buf; // Use it as result.
583     *buf += strcspn(*buf, delim); // Skip non-delimiters.
584     if (**buf != 0) { // Rest of the string is not yet empty.
585       **buf = 0; // Terminate token here.
586       *buf += 1; // Advance buf to start with the next token next time.
587     }
588   }
589 #else
590   // On Linux* OS and OS X*, strtok_r() is available. Let us use it.
591   token = strtok_r(str, delim, buf);
592 #endif
593   return token;
594 } // __kmp_str_token
595 
596 int __kmp_str_to_int(char const *str, char sentinel) {
597   int result, factor;
598   char const *t;
599 
600   result = 0;
601 
602   for (t = str; *t != '\0'; ++t) {
603     if (*t < '0' || *t > '9')
604       break;
605     result = (result * 10) + (*t - '0');
606   }
607 
608   switch (*t) {
609   case '\0': /* the current default for no suffix is bytes */
610     factor = 1;
611     break;
612   case 'b':
613   case 'B': /* bytes */
614     ++t;
615     factor = 1;
616     break;
617   case 'k':
618   case 'K': /* kilo-bytes */
619     ++t;
620     factor = 1024;
621     break;
622   case 'm':
623   case 'M': /* mega-bytes */
624     ++t;
625     factor = (1024 * 1024);
626     break;
627   default:
628     if (*t != sentinel)
629       return (-1);
630     t = "";
631     factor = 1;
632   }
633 
634   if (result > (INT_MAX / factor))
635     result = INT_MAX;
636   else
637     result *= factor;
638 
639   return (*t != 0 ? 0 : result);
640 } // __kmp_str_to_int
641 
642 /* The routine parses input string. It is expected it is a unsigned integer with
643    optional unit. Units are: "b" for bytes, "kb" or just "k" for kilobytes, "mb"
644    or "m" for megabytes, ..., "yb" or "y" for yottabytes. :-) Unit name is
645    case-insensitive. The routine returns 0 if everything is ok, or error code:
646    -1 in case of overflow, -2 in case of unknown unit. *size is set to parsed
647    value. In case of overflow *size is set to KMP_SIZE_T_MAX, in case of unknown
648    unit *size is set to zero. */
649 void __kmp_str_to_size( // R: Error code.
650     char const *str, // I: String of characters, unsigned number and unit ("b",
651     // "kb", etc).
652     size_t *out, // O: Parsed number.
653     size_t dfactor, // I: The factor if none of the letters specified.
654     char const **error // O: Null if everything is ok, error message otherwise.
655     ) {
656 
657   size_t value = 0;
658   size_t factor = 0;
659   int overflow = 0;
660   int i = 0;
661   int digit;
662 
663   KMP_DEBUG_ASSERT(str != NULL);
664 
665   // Skip spaces.
666   while (str[i] == ' ' || str[i] == '\t') {
667     ++i;
668   }
669 
670   // Parse number.
671   if (str[i] < '0' || str[i] > '9') {
672     *error = KMP_I18N_STR(NotANumber);
673     return;
674   }
675   do {
676     digit = str[i] - '0';
677     overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);
678     value = (value * 10) + digit;
679     ++i;
680   } while (str[i] >= '0' && str[i] <= '9');
681 
682   // Skip spaces.
683   while (str[i] == ' ' || str[i] == '\t') {
684     ++i;
685   }
686 
687 // Parse unit.
688 #define _case(ch, exp)                                                         \
689   case ch:                                                                     \
690   case ch - ('a' - 'A'): {                                                     \
691     size_t shift = (exp)*10;                                                   \
692     ++i;                                                                       \
693     if (shift < sizeof(size_t) * 8) {                                          \
694       factor = (size_t)(1) << shift;                                           \
695     } else {                                                                   \
696       overflow = 1;                                                            \
697     }                                                                          \
698   } break;
699   switch (str[i]) {
700     _case('k', 1); // Kilo
701     _case('m', 2); // Mega
702     _case('g', 3); // Giga
703     _case('t', 4); // Tera
704     _case('p', 5); // Peta
705     _case('e', 6); // Exa
706     _case('z', 7); // Zetta
707     _case('y', 8); // Yotta
708     // Oops. No more units...
709   }
710 #undef _case
711   if (str[i] == 'b' || str[i] == 'B') { // Skip optional "b".
712     if (factor == 0) {
713       factor = 1;
714     }
715     ++i;
716   }
717   if (!(str[i] == ' ' || str[i] == '\t' || str[i] == 0)) { // Bad unit
718     *error = KMP_I18N_STR(BadUnit);
719     return;
720   }
721 
722   if (factor == 0) {
723     factor = dfactor;
724   }
725 
726   // Apply factor.
727   overflow = overflow || (value > (KMP_SIZE_T_MAX / factor));
728   value *= factor;
729 
730   // Skip spaces.
731   while (str[i] == ' ' || str[i] == '\t') {
732     ++i;
733   }
734 
735   if (str[i] != 0) {
736     *error = KMP_I18N_STR(IllegalCharacters);
737     return;
738   }
739 
740   if (overflow) {
741     *error = KMP_I18N_STR(ValueTooLarge);
742     *out = KMP_SIZE_T_MAX;
743     return;
744   }
745 
746   *error = NULL;
747   *out = value;
748 } // __kmp_str_to_size
749 
750 void __kmp_str_to_uint( // R: Error code.
751     char const *str, // I: String of characters, unsigned number.
752     kmp_uint64 *out, // O: Parsed number.
753     char const **error // O: Null if everything is ok, error message otherwise.
754     ) {
755   size_t value = 0;
756   int overflow = 0;
757   int i = 0;
758   int digit;
759 
760   KMP_DEBUG_ASSERT(str != NULL);
761 
762   // Skip spaces.
763   while (str[i] == ' ' || str[i] == '\t') {
764     ++i;
765   }
766 
767   // Parse number.
768   if (str[i] < '0' || str[i] > '9') {
769     *error = KMP_I18N_STR(NotANumber);
770     return;
771   }
772   do {
773     digit = str[i] - '0';
774     overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);
775     value = (value * 10) + digit;
776     ++i;
777   } while (str[i] >= '0' && str[i] <= '9');
778 
779   // Skip spaces.
780   while (str[i] == ' ' || str[i] == '\t') {
781     ++i;
782   }
783 
784   if (str[i] != 0) {
785     *error = KMP_I18N_STR(IllegalCharacters);
786     return;
787   }
788 
789   if (overflow) {
790     *error = KMP_I18N_STR(ValueTooLarge);
791     *out = (kmp_uint64)-1;
792     return;
793   }
794 
795   *error = NULL;
796   *out = value;
797 } // __kmp_str_to_unit
798 
799 // end of file //
800