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