xref: /freebsd/contrib/llvm-project/openmp/runtime/src/kmp_str.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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   buffer->str[buffer->used] = '\0';
141   KMP_STRNCAT_S(buffer->str + buffer->used, len + 1, str, len);
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   dest->str[dest->used] = '\0';
155   KMP_STRNCAT_S(dest->str + dest->used, src->used + 1, src->str, src->used);
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_basic_str_to_int(char const *str) {
623   int result;
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   return result;
635 }
636 
637 int __kmp_str_to_int(char const *str, char sentinel) {
638   int result, factor;
639   char const *t;
640 
641   result = 0;
642 
643   for (t = str; *t != '\0'; ++t) {
644     if (*t < '0' || *t > '9')
645       break;
646     result = (result * 10) + (*t - '0');
647   }
648 
649   switch (*t) {
650   case '\0': /* the current default for no suffix is bytes */
651     factor = 1;
652     break;
653   case 'b':
654   case 'B': /* bytes */
655     ++t;
656     factor = 1;
657     break;
658   case 'k':
659   case 'K': /* kilo-bytes */
660     ++t;
661     factor = 1024;
662     break;
663   case 'm':
664   case 'M': /* mega-bytes */
665     ++t;
666     factor = (1024 * 1024);
667     break;
668   default:
669     if (*t != sentinel)
670       return (-1);
671     t = "";
672     factor = 1;
673   }
674 
675   if (result > (INT_MAX / factor))
676     result = INT_MAX;
677   else
678     result *= factor;
679 
680   return (*t != 0 ? 0 : result);
681 } // __kmp_str_to_int
682 
683 /* The routine parses input string. It is expected it is a unsigned integer with
684    optional unit. Units are: "b" for bytes, "kb" or just "k" for kilobytes, "mb"
685    or "m" for megabytes, ..., "yb" or "y" for yottabytes. :-) Unit name is
686    case-insensitive. The routine returns 0 if everything is ok, or error code:
687    -1 in case of overflow, -2 in case of unknown unit. *size is set to parsed
688    value. In case of overflow *size is set to KMP_SIZE_T_MAX, in case of unknown
689    unit *size is set to zero. */
690 void __kmp_str_to_size( // R: Error code.
691     char const *str, // I: String of characters, unsigned number and unit ("b",
692     // "kb", etc).
693     size_t *out, // O: Parsed number.
694     size_t dfactor, // I: The factor if none of the letters specified.
695     char const **error // O: Null if everything is ok, error message otherwise.
696 ) {
697 
698   size_t value = 0;
699   size_t factor = 0;
700   int overflow = 0;
701   int i = 0;
702   int digit;
703 
704   KMP_DEBUG_ASSERT(str != NULL);
705 
706   // Skip spaces.
707   while (str[i] == ' ' || str[i] == '\t') {
708     ++i;
709   }
710 
711   // Parse number.
712   if (str[i] < '0' || str[i] > '9') {
713     *error = KMP_I18N_STR(NotANumber);
714     return;
715   }
716   do {
717     digit = str[i] - '0';
718     overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);
719     value = (value * 10) + digit;
720     ++i;
721   } while (str[i] >= '0' && str[i] <= '9');
722 
723   // Skip spaces.
724   while (str[i] == ' ' || str[i] == '\t') {
725     ++i;
726   }
727 
728 // Parse unit.
729 #define _case(ch, exp)                                                         \
730   case ch:                                                                     \
731   case ch - ('a' - 'A'): {                                                     \
732     size_t shift = (exp)*10;                                                   \
733     ++i;                                                                       \
734     if (shift < sizeof(size_t) * 8) {                                          \
735       factor = (size_t)(1) << shift;                                           \
736     } else {                                                                   \
737       overflow = 1;                                                            \
738     }                                                                          \
739   } break;
740   switch (str[i]) {
741     _case('k', 1); // Kilo
742     _case('m', 2); // Mega
743     _case('g', 3); // Giga
744     _case('t', 4); // Tera
745     _case('p', 5); // Peta
746     _case('e', 6); // Exa
747     _case('z', 7); // Zetta
748     _case('y', 8); // Yotta
749     // Oops. No more units...
750   }
751 #undef _case
752   if (str[i] == 'b' || str[i] == 'B') { // Skip optional "b".
753     if (factor == 0) {
754       factor = 1;
755     }
756     ++i;
757   }
758   if (!(str[i] == ' ' || str[i] == '\t' || str[i] == 0)) { // Bad unit
759     *error = KMP_I18N_STR(BadUnit);
760     return;
761   }
762 
763   if (factor == 0) {
764     factor = dfactor;
765   }
766 
767   // Apply factor.
768   overflow = overflow || (value > (KMP_SIZE_T_MAX / factor));
769   value *= factor;
770 
771   // Skip spaces.
772   while (str[i] == ' ' || str[i] == '\t') {
773     ++i;
774   }
775 
776   if (str[i] != 0) {
777     *error = KMP_I18N_STR(IllegalCharacters);
778     return;
779   }
780 
781   if (overflow) {
782     *error = KMP_I18N_STR(ValueTooLarge);
783     *out = KMP_SIZE_T_MAX;
784     return;
785   }
786 
787   *error = NULL;
788   *out = value;
789 } // __kmp_str_to_size
790 
791 void __kmp_str_to_uint( // R: Error code.
792     char const *str, // I: String of characters, unsigned number.
793     kmp_uint64 *out, // O: Parsed number.
794     char const **error // O: Null if everything is ok, error message otherwise.
795 ) {
796   size_t value = 0;
797   int overflow = 0;
798   int i = 0;
799   int digit;
800 
801   KMP_DEBUG_ASSERT(str != NULL);
802 
803   // Skip spaces.
804   while (str[i] == ' ' || str[i] == '\t') {
805     ++i;
806   }
807 
808   // Parse number.
809   if (str[i] < '0' || str[i] > '9') {
810     *error = KMP_I18N_STR(NotANumber);
811     return;
812   }
813   do {
814     digit = str[i] - '0';
815     overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);
816     value = (value * 10) + digit;
817     ++i;
818   } while (str[i] >= '0' && str[i] <= '9');
819 
820   // Skip spaces.
821   while (str[i] == ' ' || str[i] == '\t') {
822     ++i;
823   }
824 
825   if (str[i] != 0) {
826     *error = KMP_I18N_STR(IllegalCharacters);
827     return;
828   }
829 
830   if (overflow) {
831     *error = KMP_I18N_STR(ValueTooLarge);
832     *out = (kmp_uint64)-1;
833     return;
834   }
835 
836   *error = NULL;
837   *out = value;
838 } // __kmp_str_to_unit
839 
840 // end of file //
841