brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.0 KiB · 12cce53 Raw
857 lines · cpp
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-exception10//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 a35   // 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 change48     any fields of buffer directly.49   * buffer.str is never NULL. If buffer is empty, buffer.str points to empty50     string ("").51   * For performance reasons, buffer uses stack memory (buffer.bulk) first. If52     stack memory is exhausted, buffer allocates memory on heap by malloc(), and53     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 71void __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_clear79 80void __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_reserve109 110void __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_detach122 123void __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_free133 134void __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_cat145 146void __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_catbuf159 160// Return the number of characters written161int __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, so173         vsnprintf() crashes if it is called for the second time with the same174         args. To prevent the crash, we have to pass a fresh intact copy of args175         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 on179         Windows* OS.180      */181 182#if !KMP_OS_WINDOWS183      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_WINDOWS187      rc = KMP_VSNPRINTF(buffer->str + buffer->used, free, format, args);188#if !KMP_OS_WINDOWS189#undef args // Remove substitution.190      va_end(_args);191#endif // KMP_OS_WINDOWS192    }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 size203      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_vprint219 220// Return the number of characters written221int __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_print229 230/* The function prints specified size to buffer. Size is expressed using biggest231   possible unit, for example 1024 is printed as "1k". */232void __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_size245 246void __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* OS256    // Intel(R) 64 it causes assertion id debug heap, so I had to replace257    // 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 basename272    *base = 0; // and truncate dir.273  }274 275} // kmp_str_fname_init276 277void __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_free282 283int __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_match299 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 memory304// allocation), and parses the string in place.305void __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 get311  // numbers only, skipping string fields "file" and "func".312 313  // Find 1-st semicolon.314  KMP_DEBUG_ASSERT(Psource);315#ifdef __cplusplus316  Str = strchr(CCAST(char *, Psource), ';');317#else318  Str = strchr(Psource, ';');319#endif320  // 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 347kmp_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_init392 393void __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_free399 400/* This function is intended to compare file names. On Windows* OS file names401   are case-insensitive, so functions performs case-insensitive comparison. On402   Linux* OS it performs case-sensitive comparison. Note: The function returns403   *true* if strings are *equal*. */404int __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_WINDOWS410  result = (_stricmp(lhs, rhs) == 0);411#else412  result = (strcmp(lhs, rhs) == 0);413#endif414  return result;415} // __kmp_str_eqf416 417/* This function is like sprintf, but it *allocates* new buffer, which must be418   freed eventually by __kmp_str_free(). The function is very convenient for419   constructing strings, it successfully replaces strdup(), strcat(), it frees420   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 size424   __kmp_str_free( & str );425   str = __kmp_str_format( "%s%s", orig1, orig2 ); // strcat(), doesn't care426                                                   // 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 from433   performance-critical code. In performance-critical code consider using434   kmp_str_buf_t instead, since it uses stack-allocated buffer for short435   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 extra440      overhead while cache-aligned memory is not necessary.441   2. __kmp_thread_malloc() cannot be used because it requires pointer to thread442      structure. We need to perform string operations during library startup443      (for example, in __kmp_register_library_startup()) when no thread444      structures are allocated yet.445   So standard malloc() is the only available option.446*/447 448char *__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 buffer477      // 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_format493 494void __kmp_str_free(char **str) {495  KMP_DEBUG_ASSERT(str != NULL);496  KMP_INTERNAL_FREE(*str);497  *str = NULL;498} // func __kmp_str_free499 500/* If len is zero, returns true iff target and data have exact case-insensitive501   match. If len is negative, returns true iff target is a case-insensitive502   substring of data. If len is positive, returns true iff target is a503   case-insensitive substring of data or vice versa, and neither is shorter than504   len. */505int __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_match517 518// If data contains all of target, returns true, otherwise returns false.519// len should be the length of target520bool __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_contains542 543int __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_false551 552int __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_true560 561void __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_replace570 571void __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_split592 593/* strtok_r() is not available on Windows* OS. This function reimplements594   strtok_r(). */595char *__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_WINDOWS602  // 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#else616  // On Linux* OS and OS X*, strtok_r() is available. Let us use it.617  token = strtok_r(str, delim, buf);618#endif619  return token;620} // __kmp_str_token621 622int __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    // Cap parsing to create largest integer632    if (result >= (INT_MAX - (*t - '0')) / 10) {633      result = INT_MAX;634      break;635    }636    result = (result * 10) + (*t - '0');637  }638 639  return result;640}641 642int __kmp_str_to_int(char const *str, char sentinel) {643  int result, factor;644  char const *t;645 646  result = 0;647 648  for (t = str; *t != '\0'; ++t) {649    if (*t < '0' || *t > '9')650      break;651    // Cap parsing to create largest integer652    if (result >= (INT_MAX - (*t - '0')) / 10) {653      result = INT_MAX;654      break;655    }656    result = (result * 10) + (*t - '0');657  }658 659  // Parse rest of large number by skipping the digits so t points to sentinel660  if (result == INT_MAX)661    for (t = str; *t != '\0'; ++t)662      if (*t < '0' || *t > '9')663        break;664 665  switch (*t) {666  case '\0': /* the current default for no suffix is bytes */667    factor = 1;668    break;669  case 'b':670  case 'B': /* bytes */671    ++t;672    factor = 1;673    break;674  case 'k':675  case 'K': /* kilo-bytes */676    ++t;677    factor = 1024;678    break;679  case 'm':680  case 'M': /* mega-bytes */681    ++t;682    factor = (1024 * 1024);683    break;684  default:685    if (*t != sentinel)686      return (-1);687    t = "";688    factor = 1;689  }690 691  if (result > (INT_MAX / factor))692    result = INT_MAX;693  else694    result *= factor;695 696  return (*t != 0 ? 0 : result);697} // __kmp_str_to_int698 699/* The routine parses input string. It is expected it is a unsigned integer with700   optional unit. Units are: "b" for bytes, "kb" or just "k" for kilobytes, "mb"701   or "m" for megabytes, ..., "yb" or "y" for yottabytes. :-) Unit name is702   case-insensitive. The routine returns 0 if everything is ok, or error code:703   -1 in case of overflow, -2 in case of unknown unit. *size is set to parsed704   value. In case of overflow *size is set to KMP_SIZE_T_MAX, in case of unknown705   unit *size is set to zero. */706void __kmp_str_to_size( // R: Error code.707    char const *str, // I: String of characters, unsigned number and unit ("b",708    // "kb", etc).709    size_t *out, // O: Parsed number.710    size_t dfactor, // I: The factor if none of the letters specified.711    char const **error // O: Null if everything is ok, error message otherwise.712) {713 714  size_t value = 0;715  size_t factor = 0;716  int overflow = 0;717  int i = 0;718  int digit;719 720  KMP_DEBUG_ASSERT(str != NULL);721 722  // Skip spaces.723  while (str[i] == ' ' || str[i] == '\t') {724    ++i;725  }726 727  // Parse number.728  if (str[i] < '0' || str[i] > '9') {729    *error = KMP_I18N_STR(NotANumber);730    return;731  }732  do {733    digit = str[i] - '0';734    overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);735    value = (value * 10) + digit;736    ++i;737  } while (str[i] >= '0' && str[i] <= '9');738 739  // Skip spaces.740  while (str[i] == ' ' || str[i] == '\t') {741    ++i;742  }743 744// Parse unit.745#define _case(ch, exp)                                                         \746  case ch:                                                                     \747  case ch - ('a' - 'A'): {                                                     \748    size_t shift = (exp)*10;                                                   \749    ++i;                                                                       \750    if (shift < sizeof(size_t) * 8) {                                          \751      factor = (size_t)(1) << shift;                                           \752    } else {                                                                   \753      overflow = 1;                                                            \754    }                                                                          \755  } break;756  switch (str[i]) {757    _case('k', 1); // Kilo758    _case('m', 2); // Mega759    _case('g', 3); // Giga760    _case('t', 4); // Tera761    _case('p', 5); // Peta762    _case('e', 6); // Exa763    _case('z', 7); // Zetta764    _case('y', 8); // Yotta765    // Oops. No more units...766  }767#undef _case768  if (str[i] == 'b' || str[i] == 'B') { // Skip optional "b".769    if (factor == 0) {770      factor = 1;771    }772    ++i;773  }774  if (!(str[i] == ' ' || str[i] == '\t' || str[i] == 0)) { // Bad unit775    *error = KMP_I18N_STR(BadUnit);776    return;777  }778 779  if (factor == 0) {780    factor = dfactor;781  }782 783  // Apply factor.784  overflow = overflow || (value > (KMP_SIZE_T_MAX / factor));785  value *= factor;786 787  // Skip spaces.788  while (str[i] == ' ' || str[i] == '\t') {789    ++i;790  }791 792  if (str[i] != 0) {793    *error = KMP_I18N_STR(IllegalCharacters);794    return;795  }796 797  if (overflow) {798    *error = KMP_I18N_STR(ValueTooLarge);799    *out = KMP_SIZE_T_MAX;800    return;801  }802 803  *error = NULL;804  *out = value;805} // __kmp_str_to_size806 807void __kmp_str_to_uint( // R: Error code.808    char const *str, // I: String of characters, unsigned number.809    kmp_uint64 *out, // O: Parsed number.810    char const **error // O: Null if everything is ok, error message otherwise.811) {812  size_t value = 0;813  int overflow = 0;814  int i = 0;815  int digit;816 817  KMP_DEBUG_ASSERT(str != NULL);818 819  // Skip spaces.820  while (str[i] == ' ' || str[i] == '\t') {821    ++i;822  }823 824  // Parse number.825  if (str[i] < '0' || str[i] > '9') {826    *error = KMP_I18N_STR(NotANumber);827    return;828  }829  do {830    digit = str[i] - '0';831    overflow = overflow || (value > (KMP_SIZE_T_MAX - digit) / 10);832    value = (value * 10) + digit;833    ++i;834  } while (str[i] >= '0' && str[i] <= '9');835 836  // Skip spaces.837  while (str[i] == ' ' || str[i] == '\t') {838    ++i;839  }840 841  if (str[i] != 0) {842    *error = KMP_I18N_STR(IllegalCharacters);843    return;844  }845 846  if (overflow) {847    *error = KMP_I18N_STR(ValueTooLarge);848    *out = (kmp_uint64)-1;849    return;850  }851 852  *error = NULL;853  *out = value;854} // __kmp_str_to_unit855 856// end of file //857