1 /* 2 * Copyright 2010-2019 Branimir Karadzic. All rights reserved. 3 * License: https://github.com/bkaradzic/bx#license-bsd-2-clause 4 */ 5 6 #include "bx_p.h" 7 #include <bx/allocator.h> 8 #include <bx/file.h> 9 #include <bx/hash.h> 10 #include <bx/string.h> 11 12 namespace bx 13 { isInRange(char _ch,char _from,char _to)14 inline bool isInRange(char _ch, char _from, char _to) 15 { 16 return unsigned(_ch - _from) <= unsigned(_to-_from); 17 } 18 isSpace(char _ch)19 bool isSpace(char _ch) 20 { 21 return ' ' == _ch 22 || '\t' == _ch 23 || '\n' == _ch 24 || '\v' == _ch 25 || '\f' == _ch 26 || '\r' == _ch 27 ; 28 } 29 isUpper(char _ch)30 bool isUpper(char _ch) 31 { 32 return isInRange(_ch, 'A', 'Z'); 33 } 34 isLower(char _ch)35 bool isLower(char _ch) 36 { 37 return isInRange(_ch, 'a', 'z'); 38 } 39 isAlpha(char _ch)40 bool isAlpha(char _ch) 41 { 42 return isLower(_ch) || isUpper(_ch); 43 } 44 isNumeric(char _ch)45 bool isNumeric(char _ch) 46 { 47 return isInRange(_ch, '0', '9'); 48 } 49 isAlphaNum(char _ch)50 bool isAlphaNum(char _ch) 51 { 52 return false 53 || isAlpha(_ch) 54 || isNumeric(_ch) 55 ; 56 } 57 isHexNum(char _ch)58 bool isHexNum(char _ch) 59 { 60 return false 61 || isInRange(toLower(_ch), 'a', 'f') 62 || isNumeric(_ch) 63 ; 64 } 65 isPrint(char _ch)66 bool isPrint(char _ch) 67 { 68 return isInRange(_ch, ' ', '~'); 69 } 70 71 typedef bool (*CharTestFn)(char _ch); 72 73 template<CharTestFn fn> isCharTest(const StringView & _str)74 inline bool isCharTest(const StringView& _str) 75 { 76 bool result = true; 77 78 for (const char* ptr = _str.getPtr(), *term = _str.getTerm() 79 ; ptr != term && result 80 ; ++ptr 81 ) 82 { 83 result &= fn(*ptr); 84 } 85 86 return result; 87 } 88 isSpace(const StringView & _str)89 bool isSpace(const StringView& _str) 90 { 91 return isCharTest<isSpace>(_str); 92 } 93 isUpper(const StringView & _str)94 bool isUpper(const StringView& _str) 95 { 96 return isCharTest<isUpper>(_str); 97 } 98 isLower(const StringView & _str)99 bool isLower(const StringView& _str) 100 { 101 return isCharTest<isLower>(_str); 102 } 103 isAlpha(const StringView & _str)104 bool isAlpha(const StringView& _str) 105 { 106 return isCharTest<isAlpha>(_str); 107 } 108 isNumeric(const StringView & _str)109 bool isNumeric(const StringView& _str) 110 { 111 return isCharTest<isNumeric>(_str); 112 } 113 isAlphaNum(const StringView & _str)114 bool isAlphaNum(const StringView& _str) 115 { 116 return isCharTest<isAlphaNum>(_str); 117 } 118 isHexNum(const StringView & _str)119 bool isHexNum(const StringView& _str) 120 { 121 return isCharTest<isHexNum>(_str); 122 } 123 isPrint(const StringView & _str)124 bool isPrint(const StringView& _str) 125 { 126 return isCharTest<isPrint>(_str); 127 } 128 toLower(char _ch)129 char toLower(char _ch) 130 { 131 return _ch + (isUpper(_ch) ? 0x20 : 0); 132 } 133 toLowerUnsafe(char * _inOutStr,int32_t _len)134 void toLowerUnsafe(char* _inOutStr, int32_t _len) 135 { 136 for (int32_t ii = 0; ii < _len; ++ii) 137 { 138 *_inOutStr = toLower(*_inOutStr); 139 } 140 } 141 toLower(char * _inOutStr,int32_t _max)142 void toLower(char* _inOutStr, int32_t _max) 143 { 144 const int32_t len = strLen(_inOutStr, _max); 145 toLowerUnsafe(_inOutStr, len); 146 } 147 toUpper(char _ch)148 char toUpper(char _ch) 149 { 150 return _ch - (isLower(_ch) ? 0x20 : 0); 151 } 152 toUpperUnsafe(char * _inOutStr,int32_t _len)153 void toUpperUnsafe(char* _inOutStr, int32_t _len) 154 { 155 for (int32_t ii = 0; ii < _len; ++ii) 156 { 157 *_inOutStr = toUpper(*_inOutStr); 158 } 159 } 160 toUpper(char * _inOutStr,int32_t _max)161 void toUpper(char* _inOutStr, int32_t _max) 162 { 163 const int32_t len = strLen(_inOutStr, _max); 164 toUpperUnsafe(_inOutStr, len); 165 } 166 167 typedef char (*CharFn)(char _ch); 168 toNoop(char _ch)169 inline char toNoop(char _ch) 170 { 171 return _ch; 172 } 173 174 template<CharFn fn> strCmp(const char * _lhs,int32_t _lhsMax,const char * _rhs,int32_t _rhsMax)175 inline int32_t strCmp(const char* _lhs, int32_t _lhsMax, const char* _rhs, int32_t _rhsMax) 176 { 177 int32_t max = min(_lhsMax, _rhsMax); 178 179 for ( 180 ; 0 < max && fn(*_lhs) == fn(*_rhs) 181 ; ++_lhs, ++_rhs, --max 182 ) 183 { 184 if (*_lhs == '\0' 185 || *_rhs == '\0') 186 { 187 break; 188 } 189 } 190 191 if (0 == max) 192 { 193 return _lhsMax == _rhsMax ? 0 : _lhsMax > _rhsMax ? 1 : -1; 194 } 195 196 return fn(*_lhs) - fn(*_rhs); 197 } 198 strCmp(const StringView & _lhs,const StringView & _rhs,int32_t _max)199 int32_t strCmp(const StringView& _lhs, const StringView& _rhs, int32_t _max) 200 { 201 return strCmp<toNoop>( 202 _lhs.getPtr() 203 , min(_lhs.getLength(), _max) 204 , _rhs.getPtr() 205 , min(_rhs.getLength(), _max) 206 ); 207 } 208 strCmpI(const StringView & _lhs,const StringView & _rhs,int32_t _max)209 int32_t strCmpI(const StringView& _lhs, const StringView& _rhs, int32_t _max) 210 { 211 return strCmp<toLower>( 212 _lhs.getPtr() 213 , min(_lhs.getLength(), _max) 214 , _rhs.getPtr() 215 , min(_rhs.getLength(), _max) 216 ); 217 } 218 strCmpV(const char * _lhs,int32_t _lhsMax,const char * _rhs,int32_t _rhsMax)219 inline int32_t strCmpV(const char* _lhs, int32_t _lhsMax, const char* _rhs, int32_t _rhsMax) 220 { 221 int32_t max = min(_lhsMax, _rhsMax); 222 int32_t ii = 0; 223 int32_t idx = 0; 224 bool zero = true; 225 226 for ( 227 ; 0 < max && _lhs[ii] == _rhs[ii] 228 ; ++ii, --max 229 ) 230 { 231 const uint8_t ch = _lhs[ii]; 232 if ('\0' == ch 233 || '\0' == _rhs[ii]) 234 { 235 break; 236 } 237 238 if (!isNumeric(ch) ) 239 { 240 idx = ii+1; 241 zero = true; 242 } 243 else if ('0' != ch) 244 { 245 zero = false; 246 } 247 } 248 249 if (0 == max) 250 { 251 return _lhsMax == _rhsMax ? 0 : _lhs[ii] - _rhs[ii]; 252 } 253 254 if ('0' != _lhs[idx] 255 && '0' != _rhs[idx]) 256 { 257 int32_t jj = 0; 258 for (jj = ii 259 ; 0 < max && isNumeric(_lhs[jj]) 260 ; ++jj, --max 261 ) 262 { 263 if (!isNumeric(_rhs[jj]) ) 264 { 265 return 1; 266 } 267 } 268 269 if (isNumeric(_rhs[jj])) 270 { 271 return -1; 272 } 273 } 274 else if (zero 275 && idx < ii 276 && (isNumeric(_lhs[ii]) || isNumeric(_rhs[ii]) ) ) 277 { 278 return (_lhs[ii] - '0') - (_rhs[ii] - '0'); 279 } 280 281 return 0 == max && _lhsMax == _rhsMax ? 0 : _lhs[ii] - _rhs[ii]; 282 } 283 strCmpV(const StringView & _lhs,const StringView & _rhs,int32_t _max)284 int32_t strCmpV(const StringView& _lhs, const StringView& _rhs, int32_t _max) 285 { 286 return strCmpV( 287 _lhs.getPtr() 288 , min(_lhs.getLength(), _max) 289 , _rhs.getPtr() 290 , min(_rhs.getLength(), _max) 291 ); 292 } 293 strLen(const char * _str,int32_t _max)294 int32_t strLen(const char* _str, int32_t _max) 295 { 296 if (NULL == _str) 297 { 298 return 0; 299 } 300 301 const char* ptr = _str; 302 for (; 0 < _max && *ptr != '\0'; ++ptr, --_max) {}; 303 return int32_t(ptr - _str); 304 } 305 strLen(const StringView & _str,int32_t _max)306 int32_t strLen(const StringView& _str, int32_t _max) 307 { 308 return strLen(_str.getPtr(), min(_str.getLength(), _max) ); 309 } 310 strCopy(char * _dst,int32_t _dstSize,const char * _src,int32_t _num)311 inline int32_t strCopy(char* _dst, int32_t _dstSize, const char* _src, int32_t _num) 312 { 313 BX_CHECK(NULL != _dst, "_dst can't be NULL!"); 314 BX_CHECK(NULL != _src, "_src can't be NULL!"); 315 BX_CHECK(0 < _dstSize, "_dstSize can't be 0!"); 316 317 const int32_t len = strLen(_src, _num); 318 const int32_t max = _dstSize-1; 319 const int32_t num = (len < max ? len : max); 320 memCopy(_dst, _src, num); 321 _dst[num] = '\0'; 322 323 return num; 324 } 325 strCopy(char * _dst,int32_t _dstSize,const StringView & _str,int32_t _num)326 int32_t strCopy(char* _dst, int32_t _dstSize, const StringView& _str, int32_t _num) 327 { 328 return strCopy(_dst, _dstSize, _str.getPtr(), min(_str.getLength(), _num) ); 329 } 330 strCat(char * _dst,int32_t _dstSize,const char * _src,int32_t _num)331 inline int32_t strCat(char* _dst, int32_t _dstSize, const char* _src, int32_t _num) 332 { 333 BX_CHECK(NULL != _dst, "_dst can't be NULL!"); 334 BX_CHECK(NULL != _src, "_src can't be NULL!"); 335 BX_CHECK(0 < _dstSize, "_dstSize can't be 0!"); 336 337 const int32_t max = _dstSize; 338 const int32_t len = strLen(_dst, max); 339 return strCopy(&_dst[len], max-len, _src, _num); 340 } 341 strCat(char * _dst,int32_t _dstSize,const StringView & _str,int32_t _num)342 int32_t strCat(char* _dst, int32_t _dstSize, const StringView& _str, int32_t _num) 343 { 344 return strCat(_dst, _dstSize, _str.getPtr(), min(_str.getLength(), _num) ); 345 } 346 strFindUnsafe(const char * _str,int32_t _len,char _ch)347 inline const char* strFindUnsafe(const char* _str, int32_t _len, char _ch) 348 { 349 for (int32_t ii = 0; ii < _len; ++ii) 350 { 351 if (_str[ii] == _ch) 352 { 353 return &_str[ii]; 354 } 355 } 356 357 return NULL; 358 } 359 strFind(const char * _str,int32_t _max,char _ch)360 inline const char* strFind(const char* _str, int32_t _max, char _ch) 361 { 362 return strFindUnsafe(_str, strLen(_str, _max), _ch); 363 } 364 strFind(const StringView & _str,char _ch)365 StringView strFind(const StringView& _str, char _ch) 366 { 367 const char* ptr = strFindUnsafe(_str.getPtr(), _str.getLength(), _ch); 368 if (NULL == ptr) 369 { 370 return StringView(_str.getTerm(), _str.getTerm() ); 371 } 372 373 return StringView(ptr, ptr+1); 374 } 375 strRFindUnsafe(const char * _str,int32_t _len,char _ch)376 inline const char* strRFindUnsafe(const char* _str, int32_t _len, char _ch) 377 { 378 for (int32_t ii = _len-1; 0 <= ii; --ii) 379 { 380 if (_str[ii] == _ch) 381 { 382 return &_str[ii]; 383 } 384 } 385 386 return NULL; 387 } 388 strRFind(const StringView & _str,char _ch)389 StringView strRFind(const StringView& _str, char _ch) 390 { 391 const char* ptr = strRFindUnsafe(_str.getPtr(), _str.getLength(), _ch); 392 if (NULL == ptr) 393 { 394 return StringView(_str.getTerm(), _str.getTerm() ); 395 } 396 397 return StringView(ptr, ptr+1); 398 } 399 400 template<CharFn fn> strFind(const char * _str,int32_t _strMax,const char * _find,int32_t _findMax)401 inline const char* strFind(const char* _str, int32_t _strMax, const char* _find, int32_t _findMax) 402 { 403 const char* ptr = _str; 404 405 int32_t stringLen = _strMax; 406 const int32_t findLen = _findMax; 407 408 for (; stringLen >= findLen; ++ptr, --stringLen) 409 { 410 // Find start of the string. 411 while (fn(*ptr) != fn(*_find) ) 412 { 413 ++ptr; 414 --stringLen; 415 416 // Search pattern lenght can't be longer than the string. 417 if (findLen > stringLen) 418 { 419 return NULL; 420 } 421 } 422 423 // Set pointers. 424 const char* string = ptr; 425 const char* search = _find; 426 427 // Start comparing. 428 while (fn(*string++) == fn(*search++) ) 429 { 430 // If end of the 'search' string is reached, all characters match. 431 if ('\0' == *search) 432 { 433 return ptr; 434 } 435 } 436 } 437 438 return NULL; 439 } 440 strFind(const StringView & _str,const StringView & _find,int32_t _num)441 StringView strFind(const StringView& _str, const StringView& _find, int32_t _num) 442 { 443 int32_t len = min(_find.getLength(), _num); 444 445 const char* ptr = strFind<toNoop>( 446 _str.getPtr() 447 , _str.getLength() 448 , _find.getPtr() 449 , len 450 ); 451 452 if (NULL == ptr) 453 { 454 return StringView(_str.getTerm(), _str.getTerm() ); 455 } 456 457 return StringView(ptr, len); 458 } 459 strFindI(const StringView & _str,const StringView & _find,int32_t _num)460 StringView strFindI(const StringView& _str, const StringView& _find, int32_t _num) 461 { 462 int32_t len = min(_find.getLength(), _num); 463 464 const char* ptr = strFind<toLower>( 465 _str.getPtr() 466 , _str.getLength() 467 , _find.getPtr() 468 , len 469 ); 470 471 if (NULL == ptr) 472 { 473 return StringView(_str.getTerm(), _str.getTerm() ); 474 } 475 476 return StringView(ptr, len); 477 } 478 strLTrim(const StringView & _str,const StringView & _chars)479 StringView strLTrim(const StringView& _str, const StringView& _chars) 480 { 481 const char* ptr = _str.getPtr(); 482 const char* chars = _chars.getPtr(); 483 const uint32_t charsLen = _chars.getLength(); 484 485 for (uint32_t ii = 0, len = _str.getLength(); ii < len; ++ii) 486 { 487 if (NULL == strFindUnsafe(chars, charsLen, ptr[ii]) ) 488 { 489 return StringView(ptr + ii, len-ii); 490 } 491 } 492 493 return _str; 494 } 495 strLTrimSpace(const StringView & _str)496 StringView strLTrimSpace(const StringView& _str) 497 { 498 for (const char* ptr = _str.getPtr(), *term = _str.getTerm(); ptr != term; ++ptr) 499 { 500 if (!isSpace(*ptr) ) 501 { 502 return StringView(ptr, term); 503 } 504 } 505 506 return StringView(_str.getTerm(), _str.getTerm() ); 507 } 508 strLTrimNonSpace(const StringView & _str)509 StringView strLTrimNonSpace(const StringView& _str) 510 { 511 for (const char* ptr = _str.getPtr(), *term = _str.getTerm(); ptr != term; ++ptr) 512 { 513 if (isSpace(*ptr) ) 514 { 515 return StringView(ptr, term); 516 } 517 } 518 519 return StringView(_str.getTerm(), _str.getTerm() ); 520 } 521 strRTrim(const StringView & _str,const StringView & _chars)522 StringView strRTrim(const StringView& _str, const StringView& _chars) 523 { 524 if (!_str.isEmpty() ) 525 { 526 const char* ptr = _str.getPtr(); 527 const char* chars = _chars.getPtr(); 528 const uint32_t charsLen = _chars.getLength(); 529 530 for (int32_t len = _str.getLength(), ii = len - 1; 0 <= ii; --ii) 531 { 532 if (NULL == strFindUnsafe(chars, charsLen, ptr[ii])) 533 { 534 return StringView(ptr, ii + 1); 535 } 536 } 537 } 538 539 return _str; 540 } 541 strTrim(const StringView & _str,const StringView & _chars)542 StringView strTrim(const StringView& _str, const StringView& _chars) 543 { 544 return strLTrim(strRTrim(_str, _chars), _chars); 545 } 546 547 constexpr uint32_t kFindStep = 1024; 548 strFindNl(const StringView & _str)549 StringView strFindNl(const StringView& _str) 550 { 551 StringView str(_str); 552 553 for (; str.getPtr() != _str.getTerm() 554 ; str = StringView(min(str.getPtr() + kFindStep, _str.getTerm() ), min(str.getPtr() + kFindStep*2, _str.getTerm() ) ) 555 ) 556 { 557 StringView eol = strFind(str, "\r\n"); 558 if (!eol.isEmpty() ) 559 { 560 return StringView(eol.getTerm(), _str.getTerm() ); 561 } 562 563 eol = strFind(str, '\n'); 564 if (!eol.isEmpty() ) 565 { 566 return StringView(eol.getTerm(), _str.getTerm() ); 567 } 568 } 569 570 return StringView(_str.getTerm(), _str.getTerm() ); 571 } 572 strFindEol(const StringView & _str)573 StringView strFindEol(const StringView& _str) 574 { 575 StringView str(_str); 576 577 for (; str.getPtr() != _str.getTerm() 578 ; str = StringView(min(str.getPtr() + kFindStep, _str.getTerm() ), min(str.getPtr() + kFindStep*2, _str.getTerm() ) ) 579 ) 580 { 581 StringView eol = strFind(str, "\r\n"); 582 if (!eol.isEmpty() ) 583 { 584 return StringView(eol.getPtr(), _str.getTerm() ); 585 } 586 587 eol = strFind(str, '\n'); 588 if (!eol.isEmpty() ) 589 { 590 return StringView(eol.getPtr(), _str.getTerm() ); 591 } 592 } 593 594 return StringView(_str.getTerm(), _str.getTerm() ); 595 } 596 strSkipWord(const char * _str,int32_t _max)597 static const char* strSkipWord(const char* _str, int32_t _max) 598 { 599 for (char ch = *_str++; 0 < _max && (isAlphaNum(ch) || '_' == ch); ch = *_str++, --_max) {}; 600 return _str-1; 601 } 602 strWord(const StringView & _str)603 StringView strWord(const StringView& _str) 604 { 605 const char* ptr = _str.getPtr(); 606 const char* term = strSkipWord(ptr, _str.getLength() ); 607 return StringView(ptr, term); 608 } 609 strFindBlock(const StringView & _str,char _open,char _close)610 StringView strFindBlock(const StringView& _str, char _open, char _close) 611 { 612 const char* curr = _str.getPtr(); 613 const char* term = _str.getTerm(); 614 const char* start = NULL; 615 616 int32_t count = 0; 617 for (char ch = *curr; curr != term && count >= 0; ch = *(++curr) ) 618 { 619 if (ch == _open) 620 { 621 if (0 == count) 622 { 623 start = curr; 624 } 625 626 ++count; 627 } 628 else if (ch == _close) 629 { 630 --count; 631 632 if (NULL == start) 633 { 634 break; 635 } 636 637 if (0 == count) 638 { 639 return StringView(start, curr+1); 640 } 641 } 642 } 643 644 return StringView(term, term); 645 } 646 normalizeEolLf(char * _out,int32_t _size,const StringView & _str)647 StringView normalizeEolLf(char* _out, int32_t _size, const StringView& _str) 648 { 649 const char* start = _out; 650 const char* end = _out; 651 652 if (0 < _size) 653 { 654 const char* curr = _str.getPtr(); 655 const char* term = _str.getTerm(); 656 end = _out + _size; 657 for (char ch = *curr; curr != term && _out < end; ch = *(++curr) ) 658 { 659 if ('\r' != ch) 660 { 661 *_out++ = ch; 662 } 663 } 664 665 end = _out; 666 } 667 668 return StringView(start, end); 669 } 670 findIdentifierMatch(const StringView & _str,const StringView & _word)671 StringView findIdentifierMatch(const StringView& _str, const StringView& _word) 672 { 673 const int32_t len = _word.getLength(); 674 StringView ptr = strFind(_str, _word); 675 for (; !ptr.isEmpty(); ptr = strFind(StringView(ptr.getPtr() + len, _str.getTerm() ), _word) ) 676 { 677 char ch = *(ptr.getPtr() - 1); 678 if (isAlphaNum(ch) || '_' == ch) 679 { 680 continue; 681 } 682 683 ch = *(ptr.getPtr() + len); 684 if (isAlphaNum(ch) || '_' == ch) 685 { 686 continue; 687 } 688 689 return ptr; 690 } 691 692 return StringView(_str.getTerm(), _str.getTerm() ); 693 } 694 findIdentifierMatch(const StringView & _str,const char ** _words,int32_t _num)695 StringView findIdentifierMatch(const StringView& _str, const char** _words, int32_t _num) 696 { 697 int32_t ii = 0; 698 for (StringView word = *_words; ii < _num && !word.isEmpty(); ++ii, ++_words, word = *_words) 699 { 700 StringView match = findIdentifierMatch(_str, word); 701 if (!match.isEmpty() ) 702 { 703 return match; 704 } 705 } 706 707 return StringView(_str.getTerm(), _str.getTerm() ); 708 } 709 710 namespace 711 { 712 struct Param 713 { Parambx::__anonbd9e53320111::Param714 Param() 715 : width(0) 716 , base(10) 717 , prec(INT32_MAX) 718 , fill(' ') 719 , bits(0) 720 , left(false) 721 , upper(false) 722 , spec(false) 723 , sign(false) 724 { 725 } 726 727 int32_t width; 728 int32_t base; 729 int32_t prec; 730 char fill; 731 uint8_t bits; 732 bool left; 733 bool upper; 734 bool spec; 735 bool sign; 736 }; 737 write(WriterI * _writer,const char * _str,int32_t _len,const Param & _param,Error * _err)738 static int32_t write(WriterI* _writer, const char* _str, int32_t _len, const Param& _param, Error* _err) 739 { 740 int32_t size = 0; 741 int32_t len = (int32_t)strLen(_str, _len); 742 int32_t padding = _param.width > len ? _param.width - len : 0; 743 bool sign = _param.sign && len > 1 && _str[0] != '-'; 744 padding = padding > 0 ? padding - sign : 0; 745 746 if (!_param.left) 747 { 748 size += writeRep(_writer, _param.fill, padding, _err); 749 } 750 751 if (NULL == _str) 752 { 753 size += write(_writer, "(null)", 6, _err); 754 } 755 else if (_param.upper) 756 { 757 for (int32_t ii = 0; ii < len; ++ii) 758 { 759 size += write(_writer, toUpper(_str[ii]), _err); 760 } 761 } 762 else if (sign) 763 { 764 size += write(_writer, '+', _err); 765 size += write(_writer, _str, len, _err); 766 } 767 else 768 { 769 size += write(_writer, _str, len, _err); 770 } 771 772 if (_param.left) 773 { 774 size += writeRep(_writer, _param.fill, padding, _err); 775 } 776 777 return size; 778 } 779 write(WriterI * _writer,char _ch,const Param & _param,Error * _err)780 static int32_t write(WriterI* _writer, char _ch, const Param& _param, Error* _err) 781 { 782 return write(_writer, &_ch, 1, _param, _err); 783 } 784 write(WriterI * _writer,const char * _str,const Param & _param,Error * _err)785 static int32_t write(WriterI* _writer, const char* _str, const Param& _param, Error* _err) 786 { 787 return write(_writer, _str, _param.prec, _param, _err); 788 } 789 write(WriterI * _writer,int32_t _i,const Param & _param,Error * _err)790 static int32_t write(WriterI* _writer, int32_t _i, const Param& _param, Error* _err) 791 { 792 char str[33]; 793 int32_t len = toString(str, sizeof(str), _i, _param.base); 794 795 if (len == 0) 796 { 797 return 0; 798 } 799 800 return write(_writer, str, len, _param, _err); 801 } 802 write(WriterI * _writer,int64_t _i,const Param & _param,Error * _err)803 static int32_t write(WriterI* _writer, int64_t _i, const Param& _param, Error* _err) 804 { 805 char str[33]; 806 int32_t len = toString(str, sizeof(str), _i, _param.base); 807 808 if (len == 0) 809 { 810 return 0; 811 } 812 813 return write(_writer, str, len, _param, _err); 814 } 815 write(WriterI * _writer,uint32_t _u,const Param & _param,Error * _err)816 static int32_t write(WriterI* _writer, uint32_t _u, const Param& _param, Error* _err) 817 { 818 char str[33]; 819 int32_t len = toString(str, sizeof(str), _u, _param.base); 820 821 if (len == 0) 822 { 823 return 0; 824 } 825 826 return write(_writer, str, len, _param, _err); 827 } 828 write(WriterI * _writer,uint64_t _u,const Param & _param,Error * _err)829 static int32_t write(WriterI* _writer, uint64_t _u, const Param& _param, Error* _err) 830 { 831 char str[33]; 832 int32_t len = toString(str, sizeof(str), _u, _param.base); 833 834 if (len == 0) 835 { 836 return 0; 837 } 838 839 return write(_writer, str, len, _param, _err); 840 } 841 write(WriterI * _writer,double _d,const Param & _param,Error * _err)842 static int32_t write(WriterI* _writer, double _d, const Param& _param, Error* _err) 843 { 844 char str[1024]; 845 int32_t len = toString(str, sizeof(str), _d); 846 847 if (len == 0) 848 { 849 return 0; 850 } 851 852 if (_param.upper) 853 { 854 toUpperUnsafe(str, len); 855 } 856 857 const char* dot = strFind(str, INT32_MAX, '.'); 858 if (NULL != dot) 859 { 860 const int32_t prec = INT32_MAX == _param.prec ? 6 : _param.prec; 861 const int32_t precLen = int32_t( 862 dot 863 + uint32_min(prec + _param.spec, 1) 864 + prec 865 - str 866 ); 867 if (precLen > len) 868 { 869 for (int32_t ii = len; ii < precLen; ++ii) 870 { 871 str[ii] = '0'; 872 } 873 str[precLen] = '\0'; 874 } 875 len = precLen; 876 } 877 878 return write(_writer, str, len, _param, _err); 879 } 880 write(WriterI * _writer,const void * _ptr,const Param & _param,Error * _err)881 static int32_t write(WriterI* _writer, const void* _ptr, const Param& _param, Error* _err) 882 { 883 char str[35] = "0x"; 884 int32_t len = toString(str + 2, sizeof(str) - 2, uint32_t(uintptr_t(_ptr) ), 16); 885 886 if (len == 0) 887 { 888 return 0; 889 } 890 891 len += 2; 892 return write(_writer, str, len, _param, _err); 893 } 894 } // anonymous namespace 895 write(WriterI * _writer,const StringView & _format,va_list _argList,Error * _err)896 int32_t write(WriterI* _writer, const StringView& _format, va_list _argList, Error* _err) 897 { 898 MemoryReader reader(_format.getPtr(), _format.getLength() ); 899 900 int32_t size = 0; 901 902 while (_err->isOk() ) 903 { 904 char ch = '\0'; 905 906 Error err; 907 read(&reader, ch, &err); 908 909 if (!_err->isOk() 910 || !err.isOk() ) 911 { 912 break; 913 } 914 else if ('%' == ch) 915 { 916 // %[flags][width][.precision][length sub-specifier]specifier 917 read(&reader, ch); 918 919 Param param; 920 921 // flags 922 while (' ' == ch 923 || '-' == ch 924 || '+' == ch 925 || '0' == ch 926 || '#' == ch) 927 { 928 switch (ch) 929 { 930 default: 931 case ' ': param.fill = ' '; break; 932 case '-': param.left = true; break; 933 case '+': param.sign = true; break; 934 case '0': param.fill = '0'; break; 935 case '#': param.spec = true; break; 936 } 937 938 read(&reader, ch); 939 } 940 941 if (param.left) 942 { 943 param.fill = ' '; 944 } 945 946 // width 947 if ('*' == ch) 948 { 949 read(&reader, ch); 950 param.width = va_arg(_argList, int32_t); 951 952 if (0 > param.width) 953 { 954 param.left = true; 955 param.width = -param.width; 956 } 957 958 } 959 else 960 { 961 while (isNumeric(ch) ) 962 { 963 param.width = param.width * 10 + ch - '0'; 964 read(&reader, ch); 965 } 966 } 967 968 // .precision 969 if ('.' == ch) 970 { 971 read(&reader, ch); 972 973 if ('*' == ch) 974 { 975 read(&reader, ch); 976 param.prec = va_arg(_argList, int32_t); 977 } 978 else 979 { 980 param.prec = 0; 981 while (isNumeric(ch) ) 982 { 983 param.prec = param.prec * 10 + ch - '0'; 984 read(&reader, ch); 985 } 986 } 987 } 988 989 // length sub-specifier 990 while ('h' == ch 991 || 'I' == ch 992 || 'l' == ch 993 || 'j' == ch 994 || 't' == ch 995 || 'z' == ch) 996 { 997 switch (ch) 998 { 999 default: break; 1000 1001 case 'j': param.bits = sizeof(intmax_t )*8; break; 1002 case 't': param.bits = sizeof(size_t )*8; break; 1003 case 'z': param.bits = sizeof(ptrdiff_t)*8; break; 1004 1005 case 'h': case 'I': case 'l': 1006 switch (ch) 1007 { 1008 case 'h': param.bits = sizeof(short int)*8; break; 1009 case 'l': param.bits = sizeof(long int )*8; break; 1010 default: break; 1011 } 1012 1013 read(&reader, ch); 1014 switch (ch) 1015 { 1016 case 'h': param.bits = sizeof(signed char )*8; break; 1017 case 'l': param.bits = sizeof(long long int)*8; break; 1018 case '3': 1019 case '6': 1020 read(&reader, ch); 1021 switch (ch) 1022 { 1023 case '2': param.bits = sizeof(int32_t)*8; break; 1024 case '4': param.bits = sizeof(int64_t)*8; break; 1025 default: break; 1026 } 1027 break; 1028 1029 default: seek(&reader, -1); break; 1030 } 1031 break; 1032 } 1033 1034 read(&reader, ch); 1035 } 1036 1037 // specifier 1038 switch (toLower(ch) ) 1039 { 1040 case 'c': 1041 size += write(_writer, char(va_arg(_argList, int32_t) ), param, _err); 1042 break; 1043 1044 case 's': 1045 size += write(_writer, va_arg(_argList, const char*), param, _err); 1046 break; 1047 1048 case 'o': 1049 param.base = 8; 1050 switch (param.bits) 1051 { 1052 default: size += write(_writer, va_arg(_argList, int32_t), param, _err); break; 1053 case 64: size += write(_writer, va_arg(_argList, int64_t), param, _err); break; 1054 } 1055 break; 1056 1057 case 'i': 1058 case 'd': 1059 param.base = 10; 1060 switch (param.bits) 1061 { 1062 default: size += write(_writer, va_arg(_argList, int32_t), param, _err); break; 1063 case 64: size += write(_writer, va_arg(_argList, int64_t), param, _err); break; 1064 }; 1065 break; 1066 1067 case 'e': 1068 case 'f': 1069 case 'g': 1070 param.upper = isUpper(ch); 1071 size += write(_writer, va_arg(_argList, double), param, _err); 1072 break; 1073 1074 case 'p': 1075 size += write(_writer, va_arg(_argList, void*), param, _err); 1076 break; 1077 1078 case 'x': 1079 param.base = 16; 1080 param.upper = isUpper(ch); 1081 switch (param.bits) 1082 { 1083 default: size += write(_writer, va_arg(_argList, uint32_t), param, _err); break; 1084 case 64: size += write(_writer, va_arg(_argList, uint64_t), param, _err); break; 1085 } 1086 break; 1087 1088 case 'u': 1089 param.base = 10; 1090 switch (param.bits) 1091 { 1092 default: size += write(_writer, va_arg(_argList, uint32_t), param, _err); break; 1093 case 64: size += write(_writer, va_arg(_argList, uint64_t), param, _err); break; 1094 } 1095 break; 1096 1097 default: 1098 size += write(_writer, ch, _err); 1099 break; 1100 } 1101 } 1102 else 1103 { 1104 size += write(_writer, ch, _err); 1105 } 1106 } 1107 1108 return size; 1109 } 1110 write(WriterI * _writer,Error * _err,const StringView * _format,...)1111 int32_t write(WriterI* _writer, Error* _err, const StringView* _format, ...) 1112 { 1113 va_list argList; 1114 va_start(argList, _format); 1115 int32_t total = write(_writer, *_format, argList, _err); 1116 va_end(argList); 1117 return total; 1118 } 1119 write(WriterI * _writer,Error * _err,const char * _format,...)1120 int32_t write(WriterI* _writer, Error* _err, const char* _format, ...) 1121 { 1122 va_list argList; 1123 va_start(argList, _format); 1124 int32_t total = write(_writer, _format, argList, _err); 1125 va_end(argList); 1126 return total; 1127 } 1128 vsnprintf(char * _out,int32_t _max,const char * _format,va_list _argList)1129 int32_t vsnprintf(char* _out, int32_t _max, const char* _format, va_list _argList) 1130 { 1131 if (1 < _max) 1132 { 1133 StaticMemoryBlockWriter writer(_out, uint32_t(_max) ); 1134 1135 Error err; 1136 va_list argListCopy; 1137 va_copy(argListCopy, _argList); 1138 int32_t size = write(&writer, _format, argListCopy, &err); 1139 va_end(argListCopy); 1140 1141 if (err.isOk() ) 1142 { 1143 size += write(&writer, '\0', &err); 1144 return size - 1 /* size without '\0' terminator */; 1145 } 1146 else 1147 { 1148 _out[_max-1] = '\0'; 1149 } 1150 } 1151 1152 Error err; 1153 SizerWriter sizer; 1154 va_list argListCopy; 1155 va_copy(argListCopy, _argList); 1156 int32_t total = write(&sizer, _format, argListCopy, &err); 1157 va_end(argListCopy); 1158 1159 return total; 1160 } 1161 snprintf(char * _out,int32_t _max,const char * _format,...)1162 int32_t snprintf(char* _out, int32_t _max, const char* _format, ...) 1163 { 1164 va_list argList; 1165 va_start(argList, _format); 1166 int32_t total = vsnprintf(_out, _max, _format, argList); 1167 va_end(argList); 1168 1169 return total; 1170 } 1171 vprintf(const char * _format,va_list _argList)1172 int32_t vprintf(const char* _format, va_list _argList) 1173 { 1174 Error err; 1175 va_list argListCopy; 1176 va_copy(argListCopy, _argList); 1177 int32_t total = write(getStdOut(), _format, argListCopy, &err); 1178 va_end(argListCopy); 1179 1180 return total; 1181 } 1182 printf(const char * _format,...)1183 int32_t printf(const char* _format, ...) 1184 { 1185 va_list argList; 1186 va_start(argList, _format); 1187 int32_t total = vprintf(_format, argList); 1188 va_end(argList); 1189 1190 return total; 1191 } 1192 1193 static const char s_units[] = { 'B', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; 1194 1195 template<uint32_t Kilo, char KiloCh0, char KiloCh1, CharFn fn> prettify(char * _out,int32_t _count,uint64_t _value)1196 inline int32_t prettify(char* _out, int32_t _count, uint64_t _value) 1197 { 1198 uint8_t idx = 0; 1199 double value = double(_value); 1200 while (_value != (_value&0x7ff) 1201 && idx < BX_COUNTOF(s_units) ) 1202 { 1203 _value /= Kilo; 1204 value *= 1.0/double(Kilo); 1205 ++idx; 1206 } 1207 1208 return snprintf(_out, _count, "%0.2f %c%c%c", value 1209 , fn(s_units[idx]) 1210 , idx > 0 ? KiloCh0 : '\0' 1211 , KiloCh1 1212 ); 1213 } 1214 prettify(char * _out,int32_t _count,uint64_t _value,Units::Enum _units)1215 int32_t prettify(char* _out, int32_t _count, uint64_t _value, Units::Enum _units) 1216 { 1217 if (Units::Kilo == _units) 1218 { 1219 return prettify<1000, 'B', '\0', toNoop>(_out, _count, _value); 1220 } 1221 1222 return prettify<1024, 'i', 'B', toUpper>(_out, _count, _value); 1223 } 1224 1225 } // namespace bx 1226