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