xref: /freebsd/contrib/llvm-project/openmp/runtime/src/kmp_str.cpp (revision 3e8eb5c7f4909209c042403ddee340b2ee7003a5)
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 // If data contains all of target, returns true, otherwise returns false.
519 // len should be the length of target
520 bool __kmp_str_contains(char const *target, int len, char const *data) {
521   int i = 0, j = 0, start = 0;
522   if (target == NULL || data == NULL) {
523     return FALSE;
524   }
525   while (target[i]) {
526     if (!data[j])
527       return FALSE;
528     if (TOLOWER(target[i]) != TOLOWER(data[j])) {
529       j = start + 1;
530       start = j;
531       i = 0;
532     } else {
533       if (i == 0)
534         start = j;
535       j++;
536       i++;
537     }
538   }
539 
540   return i == len;
541 } // __kmp_str_contains
542 
543 int __kmp_str_match_false(char const *data) {
544   int result =
545       __kmp_str_match("false", 1, data) || __kmp_str_match("off", 2, data) ||
546       __kmp_str_match("0", 1, data) || __kmp_str_match(".false.", 2, data) ||
547       __kmp_str_match(".f.", 2, data) || __kmp_str_match("no", 1, data) ||
548       __kmp_str_match("disabled", 0, data);
549   return result;
550 } // __kmp_str_match_false
551 
552 int __kmp_str_match_true(char const *data) {
553   int result =
554       __kmp_str_match("true", 1, data) || __kmp_str_match("on", 2, data) ||
555       __kmp_str_match("1", 1, data) || __kmp_str_match(".true.", 2, data) ||
556       __kmp_str_match(".t.", 2, data) || __kmp_str_match("yes", 1, data) ||
557       __kmp_str_match("enabled", 0, data);
558   return result;
559 } // __kmp_str_match_true
560 
561 void __kmp_str_replace(char *str, char search_for, char replace_with) {
562   char *found = NULL;
563 
564   found = strchr(str, search_for);
565   while (found) {
566     *found = replace_with;
567     found = strchr(found + 1, search_for);
568   }
569 } // __kmp_str_replace
570 
571 void __kmp_str_split(char *str, // I: String to split.
572                      char delim, // I: Character to split on.
573                      char **head, // O: Pointer to head (may be NULL).
574                      char **tail // O: Pointer to tail (may be NULL).
575 ) {
576   char *h = str;
577   char *t = NULL;
578   if (str != NULL) {
579     char *ptr = strchr(str, delim);
580     if (ptr != NULL) {
581       *ptr = 0;
582       t = ptr + 1;
583     }
584   }
585   if (head != NULL) {
586     *head = h;
587   }
588   if (tail != NULL) {
589     *tail = t;
590   }
591 } // __kmp_str_split
592 
593 /* strtok_r() is not available on Windows* OS. This function reimplements
594    strtok_r(). */
595 char *__kmp_str_token(
596     char *str, // String to split into tokens. Note: String *is* modified!
597     char const *delim, // Delimiters.
598     char **buf // Internal buffer.
599 ) {
600   char *token = NULL;
601 #if KMP_OS_WINDOWS
602   // On Windows* OS there is no strtok_r() function. Let us implement it.
603   if (str != NULL) {
604     *buf = str; // First call, initialize buf.
605   }
606   *buf += strspn(*buf, delim); // Skip leading delimiters.
607   if (**buf != 0) { // Rest of the string is not yet empty.
608     token = *buf; // Use it as result.
609     *buf += strcspn(*buf, delim); // Skip non-delimiters.
610     if (**buf != 0) { // Rest of the string is not yet empty.
611       **buf = 0; // Terminate token here.
612       *buf += 1; // Advance buf to start with the next token next time.
613     }
614   }
615 #else
616   // On Linux* OS and OS X*, strtok_r() is available. Let us use it.
617   token = strtok_r(str, delim, buf);
618 #endif
619   return token;
620 } // __kmp_str_token
621 
622 int __kmp_str_to_int(char const *str, char sentinel) {
623   int result, factor;
624   char const *t;
625 
626   result = 0;
627 
628   for (t = str; *t != '\0'; ++t) {
629     if (*t < '0' || *t > '9')
630       break;
631     result = (result * 10) + (*t - '0');
632   }
633 
634   switch (*t) {
635   case '\0': /* the current default for no suffix is bytes */
636     factor = 1;
637     break;
638   case 'b':
639   case 'B': /* bytes */
640     ++t;
641     factor = 1;
642     break;
643   case 'k':
644   case 'K': /* kilo-bytes */
645     ++t;
646     factor = 1024;
647     break;
648   case 'm':
649   case 'M': /* mega-bytes */
650     ++t;
651     factor = (1024 * 1024);
652     break;
653   default:
654     if (*t != sentinel)
655       return (-1);
656     t = "";
657     factor = 1;
658   }
659 
660   if (result > (INT_MAX / factor))
661     result = INT_MAX;
662   else
663     result *= factor;
664 
665   return (*t != 0 ? 0 : result);
666 } // __kmp_str_to_int
667 
668 /* The routine parses input string. It is expected it is a unsigned integer with
669    optional unit. Units are: "b" for bytes, "kb" or just "k" for kilobytes, "mb"
670    or "m" for megabytes, ..., "yb" or "y" for yottabytes. :-) Unit name is
671    case-insensitive. The routine returns 0 if everything is ok, or error code:
672    -1 in case of overflow, -2 in case of unknown unit. *size is set to parsed
673    value. In case of overflow *size is set to KMP_SIZE_T_MAX, in case of unknown
674    unit *size is set to zero. */
675 void __kmp_str_to_size( // R: Error code.
676     char const *str, // I: String of characters, unsigned number and unit ("b",
677     // "kb", etc).
678     size_t *out, // O: Parsed number.
679     size_t dfactor, // I: The factor if none of the letters specified.
680     char const **error // O: Null if everything is ok, error message otherwise.
681 ) {
682 
683   size_t value = 0;
684   size_t factor = 0;
685   int overflow = 0;
686   int i = 0;
687   int digit;
688 
689   KMP_DEBUG_ASSERT(str != NULL);
690 
691   // Skip spaces.
692   while (str[i] == ' ' || str[i] == '\t') {
693     ++i;
694   }
695 
696   // Parse number.
697   if (str[i] < '0' || str[i] > '9') {
698     *error = KMP_I18N_STR(NotANumber);
699     return;
700   }
701   do {
702     digit = str[i] - '0';
703     overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);
704     value = (value * 10) + digit;
705     ++i;
706   } while (str[i] >= '0' && str[i] <= '9');
707 
708   // Skip spaces.
709   while (str[i] == ' ' || str[i] == '\t') {
710     ++i;
711   }
712 
713 // Parse unit.
714 #define _case(ch, exp)                                                         \
715   case ch:                                                                     \
716   case ch - ('a' - 'A'): {                                                     \
717     size_t shift = (exp)*10;                                                   \
718     ++i;                                                                       \
719     if (shift < sizeof(size_t) * 8) {                                          \
720       factor = (size_t)(1) << shift;                                           \
721     } else {                                                                   \
722       overflow = 1;                                                            \
723     }                                                                          \
724   } break;
725   switch (str[i]) {
726     _case('k', 1); // Kilo
727     _case('m', 2); // Mega
728     _case('g', 3); // Giga
729     _case('t', 4); // Tera
730     _case('p', 5); // Peta
731     _case('e', 6); // Exa
732     _case('z', 7); // Zetta
733     _case('y', 8); // Yotta
734     // Oops. No more units...
735   }
736 #undef _case
737   if (str[i] == 'b' || str[i] == 'B') { // Skip optional "b".
738     if (factor == 0) {
739       factor = 1;
740     }
741     ++i;
742   }
743   if (!(str[i] == ' ' || str[i] == '\t' || str[i] == 0)) { // Bad unit
744     *error = KMP_I18N_STR(BadUnit);
745     return;
746   }
747 
748   if (factor == 0) {
749     factor = dfactor;
750   }
751 
752   // Apply factor.
753   overflow = overflow || (value > (KMP_SIZE_T_MAX / factor));
754   value *= factor;
755 
756   // Skip spaces.
757   while (str[i] == ' ' || str[i] == '\t') {
758     ++i;
759   }
760 
761   if (str[i] != 0) {
762     *error = KMP_I18N_STR(IllegalCharacters);
763     return;
764   }
765 
766   if (overflow) {
767     *error = KMP_I18N_STR(ValueTooLarge);
768     *out = KMP_SIZE_T_MAX;
769     return;
770   }
771 
772   *error = NULL;
773   *out = value;
774 } // __kmp_str_to_size
775 
776 void __kmp_str_to_uint( // R: Error code.
777     char const *str, // I: String of characters, unsigned number.
778     kmp_uint64 *out, // O: Parsed number.
779     char const **error // O: Null if everything is ok, error message otherwise.
780 ) {
781   size_t value = 0;
782   int overflow = 0;
783   int i = 0;
784   int digit;
785 
786   KMP_DEBUG_ASSERT(str != NULL);
787 
788   // Skip spaces.
789   while (str[i] == ' ' || str[i] == '\t') {
790     ++i;
791   }
792 
793   // Parse number.
794   if (str[i] < '0' || str[i] > '9') {
795     *error = KMP_I18N_STR(NotANumber);
796     return;
797   }
798   do {
799     digit = str[i] - '0';
800     overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);
801     value = (value * 10) + digit;
802     ++i;
803   } while (str[i] >= '0' && str[i] <= '9');
804 
805   // Skip spaces.
806   while (str[i] == ' ' || str[i] == '\t') {
807     ++i;
808   }
809 
810   if (str[i] != 0) {
811     *error = KMP_I18N_STR(IllegalCharacters);
812     return;
813   }
814 
815   if (overflow) {
816     *error = KMP_I18N_STR(ValueTooLarge);
817     *out = (kmp_uint64)-1;
818     return;
819   }
820 
821   *error = NULL;
822   *out = value;
823 } // __kmp_str_to_unit
824 
825 // end of file //
826