1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
27 // File: vogl_strutils.cpp
28 #include "vogl_core.h"
29 #include "vogl_strutils.h"
33 char *vogl_strlwr(char *p)
39 *q++ = vogl_tolower(c);
44 char *vogl_strupr(char *p)
50 *q++ = vogl_toupper(c);
55 char *vogl_strdup(const char *pStr)
60 size_t l = strlen(pStr) + 1;
61 char *p = (char *)vogl_malloc(l);
68 char *strcpy_safe(char *pDst, uint dst_len, const char *pSrc)
70 VOGL_ASSERT(pDst && pSrc && dst_len);
74 char *pCur_dst = pDst;
92 VOGL_ASSERT((pCur_dst - pDst) <= (int)dst_len);
97 int vogl_sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...)
103 va_start(args, format);
104 int c = vsnprintf(buffer, sizeOfBuffer, format, args);
113 // Absolutely guarantee the buffer is null terminated.
114 buffer[sizeOfBuffer - 1] = '\0';
116 return VOGL_MIN(c, (int)sizeOfBuffer - 1);
119 int vogl_vsprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, va_list args)
124 int c = vsnprintf(buffer, sizeOfBuffer, format, args);
131 // Absolutely guarantee the buffer is null terminated.
132 buffer[sizeOfBuffer - 1] = '\0';
134 return VOGL_MIN(c, (int)sizeOfBuffer - 1);
137 int vogl_strcasecmp(const char *s1, const char *s2)
141 int c1 = vogl_tolower((unsigned char)*s1++);
142 int c2 = vogl_tolower((unsigned char)*s2++);
143 if ((c1 == 0) || (c1 != c2))
148 int vogl_strncasecmp(const char *s1, const char *s2, size_t n)
153 while ((n-- != 0) && (vogl_tolower(*s1) == vogl_tolower(*s2)))
155 if ((n == 0) || (*s1 == '\0') || (*s2 == '\0'))
161 return vogl_tolower(*(unsigned char *)s1) - vogl_tolower(*(unsigned char *)s2);
164 // http://leetcode.com/2010/10/implement-strstr-to-find-substring-in.html
165 const char *vogl_strstr(const char *str, const char *target)
170 const char *p1 = str;
173 const char *p1Begin = p1;
175 const char *p2 = target;
176 while ((*p1) && (*p2) && (*p1 == *p2))
191 int vogl_strcmp(const char *s1, const char *s2)
195 int c1 = (unsigned char)*s1++;
196 int c2 = (unsigned char)*s2++;
197 if ((c1 == 0) || (c1 != c2))
202 // Locale-independent integer<->string conversion.
203 bool int_to_string(int value, char *pDst, uint len)
207 const uint cBufSize = 16;
210 // -value could overflow here, but we cast to uint so it doesn't matter
211 uint j = static_cast<uint>((value < 0) ? -value : value);
213 char *p = buf + cBufSize - 1;
219 *p-- = static_cast<uint8>('0' + (j % 10U));
226 const size_t total_bytes = (buf + cBufSize - 1) - p;
227 if (total_bytes > len)
234 for (size_t i = 0; i < total_bytes; i++)
240 bool uint_to_string(uint value, char *pDst, uint len)
244 const uint cBufSize = 16;
247 char *p = buf + cBufSize - 1;
253 *p-- = static_cast<uint8>('0' + (value % 10U));
257 const size_t total_bytes = (buf + cBufSize - 1) - p;
258 if (total_bytes > len)
265 for (size_t i = 0; i < total_bytes; i++)
271 bool uint64_to_string(uint64_t value, char *pDst, uint len)
275 // really only 20 digits (plus \0 or 21), but whatever
276 const uint cBufSize = 32;
279 char *p = buf + cBufSize - 1;
285 *p-- = static_cast<uint8>('0' + (value % 10U));
289 const size_t total_bytes = (buf + cBufSize - 1) - p;
290 if (total_bytes > len)
297 for (size_t i = 0; i < total_bytes; i++)
303 dynamic_string int_to_string(int64_t value)
305 const uint cBufSize = 32;
307 int64_to_string(value, buf, cBufSize);
308 return dynamic_string(buf);
311 dynamic_string uint_to_string(uint64_t value)
313 const uint cBufSize = 32;
315 uint64_to_string(value, buf, cBufSize);
316 return dynamic_string(buf);
319 bool uint64_to_string_with_commas(uint64_t value, char *pDst, uint len)
323 const uint cBufSize = 32;
326 char *p = buf + cBufSize - 1;
329 uint num_chars_until_comma = 3;
333 if (!num_chars_until_comma)
336 num_chars_until_comma = 3;
339 *p-- = static_cast<uint8>('0' + (value % 10U));
340 num_chars_until_comma--;
345 const size_t total_bytes = (buf + cBufSize - 1) - p;
346 if (total_bytes > len)
349 for (size_t i = 0; i < total_bytes; i++)
355 dynamic_string uint64_to_string_with_commas(uint64_t value)
357 const uint cBufSize = 32;
359 uint64_to_string_with_commas(value, buf, sizeof(buf));
360 return dynamic_string(buf);
363 bool int64_to_string(int64_t value, char *pDst, uint len)
367 const uint cBufSize = 32;
370 // -value could overflow here, but we cast to uint64_t so it doesn't matter
371 uint64_t j = static_cast<uint64_t>((value < 0) ? -value : value);
373 char *p = buf + cBufSize - 1;
379 *p-- = static_cast<uint8>('0' + (j % 10U));
386 const size_t total_bytes = (buf + cBufSize - 1) - p;
387 if (total_bytes > len)
394 for (size_t i = 0; i < total_bytes; i++)
400 bool string_ptr_to_int(const char *&pBuf, int &value)
405 const char *p = pBuf;
407 while (*p && vogl_isspace(*p))
411 bool negative = false;
413 if ((p[0] == '0') && (p[1] == 'x'))
417 if (!vogl_isxdigit(*p))
422 int v = utils::from_hex(*p);
426 if (result & 0xF0000000UL)
429 result = (result << 4) | static_cast<uint>(v);
436 if (!vogl_isdigit(*p))
447 while (*p && vogl_isdigit(*p))
449 if (result & 0xE0000000U)
452 const uint result8 = result << 3U;
453 const uint result2 = result << 1U;
455 if (result2 > (0xFFFFFFFFU - result8))
458 result = result8 + result2;
461 if (c > (0xFFFFFFFFU - result))
472 if (result > 0x80000000U)
474 value = -static_cast<int>(result);
478 if (result > 0x7FFFFFFFU)
480 value = static_cast<int>(result);
488 bool string_ptr_to_int64(const char *&pBuf, int64_t &value)
493 const char *p = pBuf;
495 while (*p && vogl_isspace(*p))
499 bool negative = false;
501 if ((p[0] == '0') && (p[1] == 'x'))
505 if (!vogl_isxdigit(*p))
510 int v = utils::from_hex(*p);
514 if (result & 0xF000000000000000ULL)
517 result = (result << 4) | static_cast<uint64_t>(v);
524 if (!vogl_isdigit(*p))
535 while (*p && vogl_isdigit(*p))
537 if (result & 0xE000000000000000ULL)
540 const uint64_t result8 = result << 3U;
541 const uint64_t result2 = result << 1U;
543 if (result2 > (0xFFFFFFFFFFFFFFFFULL - result8))
546 result = result8 + result2;
549 if (c > (0xFFFFFFFFFFFFFFFFULL - result))
560 if (result > 0x8000000000000000ULL)
562 value = -static_cast<int64_t>(result);
566 if (result > 0x7FFFFFFFFFFFFFFFULL)
568 value = static_cast<int64_t>(result);
576 bool string_ptr_to_uint(const char *&pBuf, uint &value)
581 const char *p = pBuf;
583 while (*p && vogl_isspace(*p))
587 if ((p[0] == '0') && (p[1] == 'x'))
591 if (!vogl_isxdigit(*p))
596 int v = utils::from_hex(*p);
600 if (result & 0xF0000000UL)
603 result = (result << 4) | static_cast<uint>(v);
610 if (!vogl_isdigit(*p))
613 while (*p && vogl_isdigit(*p))
615 if (result & 0xE0000000U)
618 const uint result8 = result << 3U;
619 const uint result2 = result << 1U;
621 if (result2 > (0xFFFFFFFFU - result8))
624 result = result8 + result2;
627 if (c > (0xFFFFFFFFU - result))
643 bool string_ptr_to_uint64(const char *&pBuf, uint64_t &value)
648 const char *p = pBuf;
650 while (*p && vogl_isspace(*p))
655 if ((p[0] == '0') && (p[1] == 'x'))
659 if (!vogl_isxdigit(*p))
664 int v = utils::from_hex(*p);
668 if (result & 0xF000000000000000ULL)
671 result = (result << 4) | static_cast<uint64_t>(v);
678 if (!vogl_isdigit(*p))
681 while (*p && vogl_isdigit(*p))
683 if (result & 0xE000000000000000ULL)
686 const uint64_t result8 = result << 3U;
687 const uint64_t result2 = result << 1U;
689 if (result2 > (0xFFFFFFFFFFFFFFFFULL - result8))
692 result = result8 + result2;
695 if (c > (0xFFFFFFFFFFFFFFFFULL - result))
711 bool string_ptr_to_bool(const char *&p, bool &value)
717 if (vogl_stricmp(p, "false") == 0)
723 if (vogl_stricmp(p, "true") == 0)
731 if (string_ptr_to_uint(p, v))
745 bool string_ptr_to_float(const char *&p, float &value, uint round_digit)
748 if (!string_ptr_to_double(p, d, round_digit))
753 value = static_cast<float>(d);
757 bool string_ptr_to_double(const char *&p, double &value, uint round_digit)
759 return string_ptr_to_double(p, p + 128, value, round_digit);
762 // I wrote this approx. 20 years ago in C/assembly using a limited FP emulator package, so it's a bit crude.
763 bool string_ptr_to_double(const char *&p, const char *pEnd, double &value, uint round_digit)
783 int got_sign_flag = 0, got_dp_flag = 0, got_num_flag = 0;
784 int got_e_flag = 0, got_e_sign_flag = 0, e_sign = 0;
785 uint whole_count = 0, frac_count = 0;
787 double whole = 0, frac = 0, scale = 1, exponent = 1;
791 status = AF_NODIGITS;
797 if (!vogl_isspace(*buf))
801 status = AF_NODIGITS;
826 if ((got_num_flag) || (got_sign_flag))
838 if ((got_num_flag) || (got_sign_flag))
862 if ((i < '0') || (i > '9'))
872 if (frac_count < round_digit)
874 frac = frac * 10.0f + i;
876 scale = scale * 10.0f;
878 else if (frac_count == round_digit)
880 if (i >= 5) /* check for round */
888 whole = whole * 10.0f + i;
909 if ((got_num_flag == 0) && (got_dp_flag))
911 status = AF_EXPONENT;
930 if ((got_num_flag) || (got_e_sign_flag))
932 status = AF_EXPONENT;
941 if ((got_num_flag) || (got_e_sign_flag))
943 status = AF_EXPONENT;
950 else if ((i >= '0') && (i <= '9'))
954 if ((e = (e * 10) + (i - 48)) > 100)
956 status = AF_EXPONENT;
964 for (int i = 1; i <= e; i++) /* compute 10^e */
965 exponent = exponent * 10.0f;
968 if (((whole_count + frac_count) == 0) && (got_e_flag == 0))
970 status = AF_NODIGITS;
975 whole = whole + (frac / scale);
980 whole = whole * exponent;
982 whole = whole / exponent;
985 if (got_sign_flag < 0)
991 return (status == 0);
994 dynamic_string to_hex_string(int value)
996 return dynamic_string(cVarArg, "0x%X", value);
999 dynamic_string to_hex_string(uint value)
1001 return dynamic_string(cVarArg, "0x%X", value);
1004 dynamic_string to_hex_string(int64_t value)
1006 return dynamic_string(cVarArg, "0x%" PRIX64, value);
1009 dynamic_string to_hex_string(uint64_t value)
1011 return dynamic_string(cVarArg, "0x%" PRIX64, value);
1014 bool strutils_test()
1021 VOGL_ASSERT_ALWAYS; \
1025 for (uint i = 0; i < 256; i++)
1027 CHECK((isspace(i) != 0) == vogl_isspace(i));
1028 CHECK((isdigit(i) != 0) == vogl_isdigit(i));
1029 CHECK((isxdigit(i) != 0) == vogl_isxdigit(i));
1030 CHECK(toupper(i) == vogl_toupper(i));
1031 CHECK(tolower(i) == vogl_tolower(i));
1032 CHECK((isupper(i) != 0) == vogl_isupper(i));
1033 CHECK((islower(i) != 0) == vogl_islower(i));
1034 CHECK((isalpha(i) != 0) == vogl_isalpha(i));
1038 for (uint t = 0; t < 0x1FFFFFF; t++)
1040 int64_t i = r.urand64();
1041 if (r.irand(0, 100) == 0)
1042 i = static_cast<int>(r.urand32());
1069 vogl::int64_to_string(i, buf, sizeof(buf));
1071 const char *pBuf = buf;
1072 CHECK(vogl::string_ptr_to_int64(pBuf, v64));
1077 bool success = vogl::string_ptr_to_int(pBuf, v32);
1080 CHECK((i < cINT32_MIN) || (i > cINT32_MAX));
1087 vogl::uint64_to_string(static_cast<uint64_t>(i), buf, sizeof(buf));
1090 CHECK(vogl::string_ptr_to_uint64(pBuf, uv64));
1091 CHECK(uv64 == static_cast<uint64_t>(i));
1093 if ((t & 32767) == 32767)
1094 printf("0x%08X\n", t);