string.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. /*
  2. * Copyright 2010-2017 Branimir Karadzic. All rights reserved.
  3. * License: https://github.com/bkaradzic/bx#license-bsd-2-clause
  4. */
  5. #include <bx/allocator.h>
  6. #include <bx/hash.h>
  7. #include <bx/readerwriter.h>
  8. #include <bx/string.h>
  9. #if !BX_CRT_NONE
  10. # include <stdio.h> // vsnprintf, vsnwprintf
  11. #endif // !BX_CRT_NONE
  12. namespace bx
  13. {
  14. bool isSpace(char _ch)
  15. {
  16. return ' ' == _ch
  17. || '\t' == _ch
  18. || '\n' == _ch
  19. || '\v' == _ch
  20. || '\f' == _ch
  21. || '\r' == _ch
  22. ;
  23. }
  24. inline bool isInRange(char _ch, char _from, char _to)
  25. {
  26. return unsigned(_ch - _from) <= unsigned(_to-_from);
  27. }
  28. bool isUpper(char _ch)
  29. {
  30. return isInRange(_ch, 'A', 'Z');
  31. }
  32. bool isLower(char _ch)
  33. {
  34. return isInRange(_ch, 'a', 'z');
  35. }
  36. bool isAlpha(char _ch)
  37. {
  38. return isLower(_ch) || isUpper(_ch);
  39. }
  40. bool isNumeric(char _ch)
  41. {
  42. return isInRange(_ch, '0', '9');
  43. }
  44. bool isAlphaNum(char _ch)
  45. {
  46. return isAlpha(_ch) || isNumeric(_ch);
  47. }
  48. bool isPrint(char _ch)
  49. {
  50. return isInRange(_ch, ' ', '~');
  51. }
  52. char toLower(char _ch)
  53. {
  54. return _ch + (isUpper(_ch) ? 0x20 : 0);
  55. }
  56. void toLowerUnsafe(char* _inOutStr, int32_t _len)
  57. {
  58. for (int32_t ii = 0; ii < _len; ++ii)
  59. {
  60. *_inOutStr = toLower(*_inOutStr);
  61. }
  62. }
  63. void toLower(char* _inOutStr, int32_t _max)
  64. {
  65. const int32_t len = strLen(_inOutStr, _max);
  66. toLowerUnsafe(_inOutStr, len);
  67. }
  68. char toUpper(char _ch)
  69. {
  70. return _ch - (isLower(_ch) ? 0x20 : 0);
  71. }
  72. void toUpperUnsafe(char* _inOutStr, int32_t _len)
  73. {
  74. for (int32_t ii = 0; ii < _len; ++ii)
  75. {
  76. *_inOutStr = toUpper(*_inOutStr);
  77. }
  78. }
  79. void toUpper(char* _inOutStr, int32_t _max)
  80. {
  81. const int32_t len = strLen(_inOutStr, _max);
  82. toUpperUnsafe(_inOutStr, len);
  83. }
  84. bool toBool(const char* _str)
  85. {
  86. char ch = toLower(_str[0]);
  87. return ch == 't' || ch == '1';
  88. }
  89. typedef char (*CharFn)(char _ch);
  90. inline char toNoop(char _ch)
  91. {
  92. return _ch;
  93. }
  94. template<CharFn fn>
  95. int32_t strCmp(const char* _lhs, const char* _rhs, int32_t _max)
  96. {
  97. for (
  98. ; 0 < _max && fn(*_lhs) == fn(*_rhs)
  99. ; ++_lhs, ++_rhs, --_max
  100. )
  101. {
  102. if (*_lhs == '\0'
  103. || *_rhs == '\0')
  104. {
  105. break;
  106. }
  107. }
  108. return 0 == _max ? 0 : fn(*_lhs) - fn(*_rhs);
  109. }
  110. int32_t strCmp(const char* _lhs, const char* _rhs, int32_t _max)
  111. {
  112. return strCmp<toNoop>(_lhs, _rhs, _max);
  113. }
  114. int32_t strCmpI(const char* _lhs, const char* _rhs, int32_t _max)
  115. {
  116. return strCmp<toLower>(_lhs, _rhs, _max);
  117. }
  118. int32_t strLen(const char* _str, int32_t _max)
  119. {
  120. if (NULL == _str)
  121. {
  122. return 0;
  123. }
  124. const char* ptr = _str;
  125. for (; 0 < _max && *ptr != '\0'; ++ptr, --_max) {};
  126. return int32_t(ptr - _str);
  127. }
  128. int32_t strCopy(char* _dst, int32_t _dstSize, const char* _src, int32_t _num)
  129. {
  130. BX_CHECK(NULL != _dst, "_dst can't be NULL!");
  131. BX_CHECK(NULL != _src, "_src can't be NULL!");
  132. BX_CHECK(0 < _dstSize, "_dstSize can't be 0!");
  133. const int32_t len = strLen(_src, _num);
  134. const int32_t max = _dstSize-1;
  135. const int32_t num = (len < max ? len : max);
  136. memCopy(_dst, _src, num);
  137. _dst[num] = '\0';
  138. return num;
  139. }
  140. int32_t strCat(char* _dst, int32_t _dstSize, const char* _src, int32_t _num)
  141. {
  142. BX_CHECK(NULL != _dst, "_dst can't be NULL!");
  143. BX_CHECK(NULL != _src, "_src can't be NULL!");
  144. BX_CHECK(0 < _dstSize, "_dstSize can't be 0!");
  145. const int32_t max = _dstSize;
  146. const int32_t len = strLen(_dst, max);
  147. return strCopy(&_dst[len], max-len, _src, _num);
  148. }
  149. const char* strFind(const char* _str, char _ch, int32_t _max)
  150. {
  151. for (int32_t ii = 0, len = strLen(_str, _max); ii < len; ++ii)
  152. {
  153. if (_str[ii] == _ch)
  154. {
  155. return &_str[ii];
  156. }
  157. }
  158. return NULL;
  159. }
  160. const char* strRFind(const char* _str, char _ch, int32_t _max)
  161. {
  162. for (int32_t ii = strLen(_str, _max); 0 < ii; --ii)
  163. {
  164. if (_str[ii] == _ch)
  165. {
  166. return &_str[ii];
  167. }
  168. }
  169. return NULL;
  170. }
  171. template<CharFn fn>
  172. static const char* strStr(const char* _str, int32_t _strMax, const char* _find, int32_t _findMax)
  173. {
  174. const char* ptr = _str;
  175. int32_t stringLen = strLen(_str, _strMax);
  176. const int32_t findLen = strLen(_find, _findMax);
  177. for (; stringLen >= findLen; ++ptr, --stringLen)
  178. {
  179. // Find start of the string.
  180. while (fn(*ptr) != fn(*_find) )
  181. {
  182. ++ptr;
  183. --stringLen;
  184. // Search pattern lenght can't be longer than the string.
  185. if (findLen > stringLen)
  186. {
  187. return NULL;
  188. }
  189. }
  190. // Set pointers.
  191. const char* string = ptr;
  192. const char* search = _find;
  193. // Start comparing.
  194. while (fn(*string++) == fn(*search++) )
  195. {
  196. // If end of the 'search' string is reached, all characters match.
  197. if ('\0' == *search)
  198. {
  199. return ptr;
  200. }
  201. }
  202. }
  203. return NULL;
  204. }
  205. const char* strFind(const char* _str, const char* _find, int32_t _max)
  206. {
  207. return strStr<toNoop>(_str, _max, _find, INT32_MAX);
  208. }
  209. const char* strFindI(const char* _str, const char* _find, int32_t _max)
  210. {
  211. return strStr<toLower>(_str, _max, _find, INT32_MAX);
  212. }
  213. const char* strnl(const char* _str)
  214. {
  215. for (; '\0' != *_str; _str += strLen(_str, 1024) )
  216. {
  217. const char* eol = strFind(_str, "\r\n", 1024);
  218. if (NULL != eol)
  219. {
  220. return eol + 2;
  221. }
  222. eol = strFind(_str, "\n", 1024);
  223. if (NULL != eol)
  224. {
  225. return eol + 1;
  226. }
  227. }
  228. return _str;
  229. }
  230. const char* streol(const char* _str)
  231. {
  232. for (; '\0' != *_str; _str += strLen(_str, 1024) )
  233. {
  234. const char* eol = strFind(_str, "\r\n", 1024);
  235. if (NULL != eol)
  236. {
  237. return eol;
  238. }
  239. eol = strFind(_str, "\n", 1024);
  240. if (NULL != eol)
  241. {
  242. return eol;
  243. }
  244. }
  245. return _str;
  246. }
  247. const char* strws(const char* _str)
  248. {
  249. for (; isSpace(*_str); ++_str) {};
  250. return _str;
  251. }
  252. const char* strnws(const char* _str)
  253. {
  254. for (; !isSpace(*_str); ++_str) {};
  255. return _str;
  256. }
  257. const char* strword(const char* _str)
  258. {
  259. for (char ch = *_str++; isAlphaNum(ch) || '_' == ch; ch = *_str++) {};
  260. return _str-1;
  261. }
  262. const char* strmb(const char* _str, char _open, char _close)
  263. {
  264. int count = 0;
  265. for (char ch = *_str++; ch != '\0' && count >= 0; ch = *_str++)
  266. {
  267. if (ch == _open)
  268. {
  269. count++;
  270. }
  271. else if (ch == _close)
  272. {
  273. count--;
  274. if (0 == count)
  275. {
  276. return _str-1;
  277. }
  278. }
  279. }
  280. return NULL;
  281. }
  282. void eolLF(char* _out, int32_t _size, const char* _str)
  283. {
  284. if (0 < _size)
  285. {
  286. char* end = _out + _size - 1;
  287. for (char ch = *_str++; ch != '\0' && _out < end; ch = *_str++)
  288. {
  289. if ('\r' != ch)
  290. {
  291. *_out++ = ch;
  292. }
  293. }
  294. *_out = '\0';
  295. }
  296. }
  297. const char* findIdentifierMatch(const char* _str, const char* _word)
  298. {
  299. int32_t len = strLen(_word);
  300. const char* ptr = strFind(_str, _word);
  301. for (; NULL != ptr; ptr = strFind(ptr + len, _word) )
  302. {
  303. if (ptr != _str)
  304. {
  305. char ch = *(ptr - 1);
  306. if (isAlphaNum(ch) || '_' == ch)
  307. {
  308. continue;
  309. }
  310. }
  311. char ch = ptr[len];
  312. if (isAlphaNum(ch) || '_' == ch)
  313. {
  314. continue;
  315. }
  316. return ptr;
  317. }
  318. return ptr;
  319. }
  320. const char* findIdentifierMatch(const char* _str, const char* _words[])
  321. {
  322. for (const char* word = *_words; NULL != word; ++_words, word = *_words)
  323. {
  324. const char* match = findIdentifierMatch(_str, word);
  325. if (NULL != match)
  326. {
  327. return match;
  328. }
  329. }
  330. return NULL;
  331. }
  332. namespace
  333. {
  334. struct Param
  335. {
  336. Param()
  337. : width(0)
  338. , base(10)
  339. , prec(6)
  340. , fill(' ')
  341. , bits(0)
  342. , left(false)
  343. , upper(false)
  344. , spec(false)
  345. , sign(false)
  346. {
  347. }
  348. int32_t width;
  349. uint32_t base;
  350. uint32_t prec;
  351. char fill;
  352. uint8_t bits;
  353. bool left;
  354. bool upper;
  355. bool spec;
  356. bool sign;
  357. };
  358. static int32_t write(WriterI* _writer, const char* _str, int32_t _len, const Param& _param, Error* _err)
  359. {
  360. int32_t size = 0;
  361. int32_t len = (int32_t)strLen(_str, _len);
  362. int32_t padding = _param.width > len ? _param.width - len : 0;
  363. bool sign = _param.sign && len > 1 && _str[0] != '-';
  364. padding = padding > 0 ? padding - sign : 0;
  365. if (!_param.left)
  366. {
  367. size += writeRep(_writer, _param.fill, padding, _err);
  368. }
  369. if (NULL == _str)
  370. {
  371. size += write(_writer, "(null)", 6, _err);
  372. }
  373. else if (_param.upper)
  374. {
  375. for (int32_t ii = 0; ii < len; ++ii)
  376. {
  377. size += write(_writer, toUpper(_str[ii]), _err);
  378. }
  379. }
  380. else if (sign)
  381. {
  382. size += write(_writer, '+', _err);
  383. size += write(_writer, _str, len, _err);
  384. }
  385. else
  386. {
  387. size += write(_writer, _str, len, _err);
  388. }
  389. if (_param.left)
  390. {
  391. size += writeRep(_writer, _param.fill, padding, _err);
  392. }
  393. return size;
  394. }
  395. static int32_t write(WriterI* _writer, char _ch, const Param& _param, Error* _err)
  396. {
  397. return write(_writer, &_ch, 1, _param, _err);
  398. }
  399. static int32_t write(WriterI* _writer, const char* _str, const Param& _param, Error* _err)
  400. {
  401. return write(_writer, _str, INT32_MAX, _param, _err);
  402. }
  403. static int32_t write(WriterI* _writer, int32_t _i, const Param& _param, Error* _err)
  404. {
  405. char str[33];
  406. int32_t len = toString(str, sizeof(str), _i, _param.base);
  407. if (len == 0)
  408. {
  409. return 0;
  410. }
  411. return write(_writer, str, len, _param, _err);
  412. }
  413. static int32_t write(WriterI* _writer, int64_t _i, const Param& _param, Error* _err)
  414. {
  415. char str[33];
  416. int32_t len = toString(str, sizeof(str), _i, _param.base);
  417. if (len == 0)
  418. {
  419. return 0;
  420. }
  421. return write(_writer, str, len, _param, _err);
  422. }
  423. static int32_t write(WriterI* _writer, uint32_t _u, const Param& _param, Error* _err)
  424. {
  425. char str[33];
  426. int32_t len = toString(str, sizeof(str), _u, _param.base);
  427. if (len == 0)
  428. {
  429. return 0;
  430. }
  431. return write(_writer, str, len, _param, _err);
  432. }
  433. static int32_t write(WriterI* _writer, uint64_t _u, const Param& _param, Error* _err)
  434. {
  435. char str[33];
  436. int32_t len = toString(str, sizeof(str), _u, _param.base);
  437. if (len == 0)
  438. {
  439. return 0;
  440. }
  441. return write(_writer, str, len, _param, _err);
  442. }
  443. static int32_t write(WriterI* _writer, double _d, const Param& _param, Error* _err)
  444. {
  445. char str[1024];
  446. int32_t len = toString(str, sizeof(str), _d);
  447. if (len == 0)
  448. {
  449. return 0;
  450. }
  451. if (_param.upper)
  452. {
  453. toUpperUnsafe(str, len);
  454. }
  455. const char* dot = strFind(str, '.');
  456. if (NULL != dot)
  457. {
  458. const int32_t precLen = int32_t(
  459. dot
  460. + uint32_min(_param.prec + _param.spec, 1)
  461. + _param.prec
  462. - str
  463. );
  464. if (precLen > len)
  465. {
  466. for (int32_t ii = len; ii < precLen; ++ii)
  467. {
  468. str[ii] = '0';
  469. }
  470. str[precLen] = '\0';
  471. }
  472. len = precLen;
  473. }
  474. return write(_writer, str, len, _param, _err);
  475. }
  476. static int32_t write(WriterI* _writer, const void* _ptr, const Param& _param, Error* _err)
  477. {
  478. char str[35] = "0x";
  479. int32_t len = toString(str + 2, sizeof(str) - 2, uint32_t(uintptr_t(_ptr) ), 16);
  480. if (len == 0)
  481. {
  482. return 0;
  483. }
  484. len += 2;
  485. return write(_writer, str, len, _param, _err);
  486. }
  487. } // anonymous namespace
  488. int32_t write(WriterI* _writer, const char* _format, va_list _argList, Error* _err)
  489. {
  490. MemoryReader reader(_format, uint32_t(strLen(_format) ) );
  491. int32_t size = 0;
  492. while (_err->isOk() )
  493. {
  494. char ch = '\0';
  495. Error err;
  496. read(&reader, ch, &err);
  497. if (!_err->isOk()
  498. || !err.isOk() )
  499. {
  500. break;
  501. }
  502. else if ('%' == ch)
  503. {
  504. // %[flags][width][.precision][length sub-specifier]specifier
  505. read(&reader, ch);
  506. Param param;
  507. // flags
  508. while (' ' == ch
  509. || '-' == ch
  510. || '+' == ch
  511. || '0' == ch
  512. || '#' == ch)
  513. {
  514. switch (ch)
  515. {
  516. default:
  517. case ' ': param.fill = ' '; break;
  518. case '-': param.left = true; break;
  519. case '+': param.sign = true; break;
  520. case '0': param.fill = '0'; break;
  521. case '#': param.spec = true; break;
  522. }
  523. read(&reader, ch);
  524. }
  525. if (param.left)
  526. {
  527. param.fill = ' ';
  528. }
  529. // width
  530. if ('*' == ch)
  531. {
  532. read(&reader, ch);
  533. param.width = va_arg(_argList, int32_t);
  534. if (0 > param.width)
  535. {
  536. param.left = true;
  537. param.width = -param.width;
  538. }
  539. }
  540. else
  541. {
  542. while (isNumeric(ch) )
  543. {
  544. param.width = param.width * 10 + ch - '0';
  545. read(&reader, ch);
  546. }
  547. }
  548. // .precision
  549. if ('.' == ch)
  550. {
  551. read(&reader, ch);
  552. if ('*' == ch)
  553. {
  554. read(&reader, ch);
  555. param.prec = va_arg(_argList, int32_t);
  556. }
  557. else
  558. {
  559. param.prec = 0;
  560. while (isNumeric(ch) )
  561. {
  562. param.prec = param.prec * 10 + ch - '0';
  563. read(&reader, ch);
  564. }
  565. }
  566. }
  567. // length sub-specifier
  568. while ('h' == ch
  569. || 'I' == ch
  570. || 'l' == ch
  571. || 'j' == ch
  572. || 't' == ch
  573. || 'z' == ch)
  574. {
  575. switch (ch)
  576. {
  577. default: break;
  578. case 'j': param.bits = sizeof(intmax_t )*8; break;
  579. case 't': param.bits = sizeof(size_t )*8; break;
  580. case 'z': param.bits = sizeof(ptrdiff_t)*8; break;
  581. case 'h': case 'I': case 'l':
  582. switch (ch)
  583. {
  584. case 'h': param.bits = sizeof(short int)*8; break;
  585. case 'l': param.bits = sizeof(long int )*8; break;
  586. default: break;
  587. }
  588. read(&reader, ch);
  589. switch (ch)
  590. {
  591. case 'h': param.bits = sizeof(signed char )*8; break;
  592. case 'l': param.bits = sizeof(long long int)*8; break;
  593. case '3':
  594. case '6':
  595. read(&reader, ch);
  596. switch (ch)
  597. {
  598. case '2': param.bits = sizeof(int32_t)*8; break;
  599. case '4': param.bits = sizeof(int64_t)*8; break;
  600. default: break;
  601. }
  602. break;
  603. default: seek(&reader, -1); break;
  604. }
  605. break;
  606. }
  607. read(&reader, ch);
  608. }
  609. // specifier
  610. switch (toLower(ch) )
  611. {
  612. case 'c':
  613. size += write(_writer, char(va_arg(_argList, int32_t) ), param, _err);
  614. break;
  615. case 's':
  616. size += write(_writer, va_arg(_argList, const char*), param, _err);
  617. break;
  618. case 'o':
  619. param.base = 8;
  620. switch (param.bits)
  621. {
  622. default: size += write(_writer, va_arg(_argList, int32_t), param, _err); break;
  623. case 64: size += write(_writer, va_arg(_argList, int64_t), param, _err); break;
  624. }
  625. break;
  626. case 'i':
  627. case 'd':
  628. param.base = 10;
  629. switch (param.bits)
  630. {
  631. default: size += write(_writer, va_arg(_argList, int32_t), param, _err); break;
  632. case 64: size += write(_writer, va_arg(_argList, int64_t), param, _err); break;
  633. };
  634. break;
  635. case 'e':
  636. case 'f':
  637. case 'g':
  638. param.upper = isUpper(ch);
  639. size += write(_writer, va_arg(_argList, double), param, _err);
  640. break;
  641. case 'p':
  642. size += write(_writer, va_arg(_argList, void*), param, _err);
  643. break;
  644. case 'x':
  645. param.base = 16;
  646. param.upper = isUpper(ch);
  647. switch (param.bits)
  648. {
  649. default: size += write(_writer, va_arg(_argList, uint32_t), param, _err); break;
  650. case 64: size += write(_writer, va_arg(_argList, uint64_t), param, _err); break;
  651. }
  652. break;
  653. case 'u':
  654. param.base = 10;
  655. switch (param.bits)
  656. {
  657. default: size += write(_writer, va_arg(_argList, uint32_t), param, _err); break;
  658. case 64: size += write(_writer, va_arg(_argList, uint64_t), param, _err); break;
  659. }
  660. break;
  661. default:
  662. size += write(_writer, ch, _err);
  663. break;
  664. }
  665. }
  666. else
  667. {
  668. size += write(_writer, ch, _err);
  669. }
  670. }
  671. size += write(_writer, '\0', _err);
  672. return size;
  673. }
  674. int32_t write(WriterI* _writer, Error* _err, const char* _format, ...)
  675. {
  676. va_list argList;
  677. va_start(argList, _format);
  678. int32_t size = write(_writer, _format, argList, _err);
  679. va_end(argList);
  680. return size;
  681. }
  682. int32_t vsnprintfRef(char* _out, int32_t _max, const char* _format, va_list _argList)
  683. {
  684. if (1 < _max)
  685. {
  686. StaticMemoryBlockWriter writer(_out, uint32_t(_max-1) );
  687. _out[_max-1] = '\0';
  688. Error err;
  689. va_list argListCopy;
  690. va_copy(argListCopy, _argList);
  691. int32_t size = write(&writer, _format, argListCopy, &err);
  692. va_end(argListCopy);
  693. if (err.isOk() )
  694. {
  695. return size;
  696. }
  697. }
  698. Error err;
  699. SizerWriter sizer;
  700. va_list argListCopy;
  701. va_copy(argListCopy, _argList);
  702. int32_t size = write(&sizer, _format, argListCopy, &err);
  703. va_end(argListCopy);
  704. return size - 1 /* size without '\0' terminator */;
  705. }
  706. int32_t vsnprintf(char* _out, int32_t _max, const char* _format, va_list _argList)
  707. {
  708. #if BX_CRT_NONE
  709. return vsnprintfRef(_out, _max, _format, _argList);
  710. #elif BX_CRT_MSVC
  711. int32_t len = -1;
  712. if (NULL != _out)
  713. {
  714. va_list argListCopy;
  715. va_copy(argListCopy, _argList);
  716. len = ::vsnprintf_s(_out, _max, size_t(-1), _format, argListCopy);
  717. va_end(argListCopy);
  718. }
  719. return -1 == len ? ::_vscprintf(_format, _argList) : len;
  720. #else
  721. return ::vsnprintf(_out, _max, _format, _argList);
  722. #endif // BX_COMPILER_MSVC
  723. }
  724. int32_t snprintf(char* _out, int32_t _max, const char* _format, ...)
  725. {
  726. va_list argList;
  727. va_start(argList, _format);
  728. int32_t len = vsnprintf(_out, _max, _format, argList);
  729. va_end(argList);
  730. return len;
  731. }
  732. int32_t vsnwprintf(wchar_t* _out, int32_t _max, const wchar_t* _format, va_list _argList)
  733. {
  734. #if BX_CRT_NONE
  735. BX_UNUSED(_out, _max, _format, _argList);
  736. return 0;
  737. #elif BX_CRT_MSVC
  738. int32_t len = -1;
  739. if (NULL != _out)
  740. {
  741. va_list argListCopy;
  742. va_copy(argListCopy, _argList);
  743. len = ::_vsnwprintf_s(_out, _max, size_t(-1), _format, argListCopy);
  744. va_end(argListCopy);
  745. }
  746. return -1 == len ? ::_vscwprintf(_format, _argList) : len;
  747. #elif BX_CRT_MINGW
  748. return ::vsnwprintf(_out, _max, _format, _argList);
  749. #else
  750. return ::vswprintf(_out, _max, _format, _argList);
  751. #endif // BX_COMPILER_MSVC
  752. }
  753. int32_t swnprintf(wchar_t* _out, int32_t _max, const wchar_t* _format, ...)
  754. {
  755. va_list argList;
  756. va_start(argList, _format);
  757. int32_t len = vsnwprintf(_out, _max, _format, argList);
  758. va_end(argList);
  759. return len;
  760. }
  761. const char* baseName(const char* _filePath)
  762. {
  763. const char* bs = strRFind(_filePath, '\\');
  764. const char* fs = strRFind(_filePath, '/');
  765. const char* slash = (bs > fs ? bs : fs);
  766. const char* colon = strRFind(_filePath, ':');
  767. const char* basename = slash > colon ? slash : colon;
  768. if (NULL != basename)
  769. {
  770. return basename+1;
  771. }
  772. return _filePath;
  773. }
  774. void prettify(char* _out, int32_t _count, uint64_t _size)
  775. {
  776. uint8_t idx = 0;
  777. double size = double(_size);
  778. while (_size != (_size&0x7ff)
  779. && idx < 9)
  780. {
  781. _size >>= 10;
  782. size *= 1.0/1024.0;
  783. ++idx;
  784. }
  785. snprintf(_out, _count, "%0.2f %c%c", size, "BkMGTPEZY"[idx], idx > 0 ? 'B' : '\0');
  786. }
  787. } // namespace bx