]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_strutils.cpp
Initial vogl checkin
[vogl] / src / voglcore / vogl_strutils.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
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
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_strutils.cpp
28 #include "vogl_core.h"
29 #include "vogl_strutils.h"
30
31 namespace vogl
32 {
33     char *vogl_strlwr(char *p)
34     {
35         char *q = p;
36         while (*q)
37         {
38             char c = *q;
39             *q++ = vogl_tolower(c);
40         }
41         return p;
42     }
43
44     char *vogl_strupr(char *p)
45     {
46         char *q = p;
47         while (*q)
48         {
49             char c = *q;
50             *q++ = vogl_toupper(c);
51         }
52         return p;
53     }
54
55     char *vogl_strdup(const char *pStr)
56     {
57         if (!pStr)
58             pStr = "";
59
60         size_t l = strlen(pStr) + 1;
61         char *p = (char *)vogl_malloc(l);
62         if (p)
63             memcpy(p, pStr, l);
64
65         return p;
66     }
67
68     char *strcpy_safe(char *pDst, uint dst_len, const char *pSrc)
69     {
70         VOGL_ASSERT(pDst && pSrc && dst_len);
71         if (!dst_len)
72             return pDst;
73
74         char *pCur_dst = pDst;
75         char c;
76
77         do
78         {
79             if (dst_len == 1)
80             {
81                 *pCur_dst++ = '\0';
82                 break;
83             }
84
85             c = *pSrc++;
86             *pCur_dst++ = c;
87
88             dst_len--;
89
90         } while (c);
91
92         VOGL_ASSERT((pCur_dst - pDst) <= (int)dst_len);
93
94         return pDst;
95     }
96
97     int vogl_sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...)
98     {
99         if (!sizeOfBuffer)
100             return 0;
101
102         va_list args;
103         va_start(args, format);
104         int c = vsnprintf(buffer, sizeOfBuffer, format, args);
105         va_end(args);
106
107         if (c < 0)
108         {
109             buffer[0] = '\0';
110             return 0;
111         }
112
113         // Absolutely guarantee the buffer is null terminated.
114         buffer[sizeOfBuffer - 1] = '\0';
115
116         return VOGL_MIN(c, (int)sizeOfBuffer - 1);
117     }
118
119     int vogl_vsprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, va_list args)
120     {
121         if (!sizeOfBuffer)
122             return 0;
123
124         int c = vsnprintf(buffer, sizeOfBuffer, format, args);
125         if (c < 0)
126         {
127             buffer[0] = '\0';
128             return 0;
129         }
130
131         // Absolutely guarantee the buffer is null terminated.
132         buffer[sizeOfBuffer - 1] = '\0';
133
134         return VOGL_MIN(c, (int)sizeOfBuffer - 1);
135     }
136
137     int vogl_strcasecmp(const char *s1, const char *s2)
138     {
139         for (;;)
140         {
141             int c1 = vogl_tolower((unsigned char)*s1++);
142             int c2 = vogl_tolower((unsigned char)*s2++);
143             if ((c1 == 0) || (c1 != c2))
144                 return c1 - c2;
145         }
146     }
147
148     int vogl_strncasecmp(const char *s1, const char *s2, size_t n)
149     {
150         if (!n)
151             return 0;
152
153         while ((n-- != 0) && (vogl_tolower(*s1) == vogl_tolower(*s2)))
154         {
155             if ((n == 0) || (*s1 == '\0') || (*s2 == '\0'))
156                 break;
157             ++s1;
158             ++s2;
159         }
160
161         return vogl_tolower(*(unsigned char *)s1) - vogl_tolower(*(unsigned char *)s2);
162     }
163
164     // http://leetcode.com/2010/10/implement-strstr-to-find-substring-in.html
165     const char *vogl_strstr(const char *str, const char *target)
166     {
167         if (!*target)
168             return str;
169
170         const char *p1 = str;
171         while (*p1)
172         {
173             const char *p1Begin = p1;
174
175             const char *p2 = target;
176             while ((*p1) && (*p2) && (*p1 == *p2))
177             {
178                 ++p1;
179                 ++p2;
180             }
181
182             if (!*p2)
183                 return p1Begin;
184
185             p1 = p1Begin + 1;
186         }
187
188         return NULL;
189     }
190
191     int vogl_strcmp(const char *s1, const char *s2)
192     {
193         for (;;)
194         {
195             int c1 = (unsigned char)*s1++;
196             int c2 = (unsigned char)*s2++;
197             if ((c1 == 0) || (c1 != c2))
198                 return c1 - c2;
199         }
200     }
201
202     // Locale-independent integer<->string conversion.
203     bool int_to_string(int value, char *pDst, uint len)
204     {
205         VOGL_ASSERT(pDst);
206
207         const uint cBufSize = 16;
208         char buf[cBufSize];
209
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);
212
213         char *p = buf + cBufSize - 1;
214
215         *p-- = '\0';
216
217         do
218         {
219             *p-- = static_cast<uint8>('0' + (j % 10U));
220             j /= 10U;
221         } while (j);
222
223         if (value < 0)
224             *p-- = '-';
225
226         const size_t total_bytes = (buf + cBufSize - 1) - p;
227         if (total_bytes > len)
228         {
229             if ((pDst) && (len))
230                 *pDst = '\0';
231             return false;
232         }
233
234         for (size_t i = 0; i < total_bytes; i++)
235             pDst[i] = p[1 + i];
236
237         return true;
238     }
239
240     bool uint_to_string(uint value, char *pDst, uint len)
241     {
242         VOGL_ASSERT(pDst);
243
244         const uint cBufSize = 16;
245         char buf[cBufSize];
246
247         char *p = buf + cBufSize - 1;
248
249         *p-- = '\0';
250
251         do
252         {
253             *p-- = static_cast<uint8>('0' + (value % 10U));
254             value /= 10U;
255         } while (value);
256
257         const size_t total_bytes = (buf + cBufSize - 1) - p;
258         if (total_bytes > len)
259         {
260             if ((pDst) && (len))
261                 *pDst = '\0';
262             return false;
263         }
264
265         for (size_t i = 0; i < total_bytes; i++)
266             pDst[i] = p[1 + i];
267
268         return true;
269     }
270
271     bool uint64_to_string(uint64_t value, char *pDst, uint len)
272     {
273         VOGL_ASSERT(pDst);
274
275         // really only 20 digits (plus \0 or 21), but whatever
276         const uint cBufSize = 32;
277         char buf[cBufSize];
278
279         char *p = buf + cBufSize - 1;
280
281         *p-- = '\0';
282
283         do
284         {
285             *p-- = static_cast<uint8>('0' + (value % 10U));
286             value /= 10U;
287         } while (value);
288
289         const size_t total_bytes = (buf + cBufSize - 1) - p;
290         if (total_bytes > len)
291         {
292             if ((pDst) && (len))
293                 *pDst = '\0';
294             return false;
295         }
296
297         for (size_t i = 0; i < total_bytes; i++)
298             pDst[i] = p[1 + i];
299
300         return true;
301     }
302
303     dynamic_string int_to_string(int64_t value)
304     {
305         const uint cBufSize = 32;
306         char buf[cBufSize];
307         int64_to_string(value, buf, cBufSize);
308         return dynamic_string(buf);
309     }
310
311     dynamic_string uint_to_string(uint64_t value)
312     {
313         const uint cBufSize = 32;
314         char buf[cBufSize];
315         uint64_to_string(value, buf, cBufSize);
316         return dynamic_string(buf);
317     }
318
319     bool uint64_to_string_with_commas(uint64_t value, char *pDst, uint len)
320     {
321         VOGL_ASSERT(pDst);
322
323         const uint cBufSize = 32;
324         char buf[cBufSize];
325
326         char *p = buf + cBufSize - 1;
327
328         *p-- = '\0';
329         uint num_chars_until_comma = 3;
330
331         do
332         {
333             if (!num_chars_until_comma)
334             {
335                 *p-- = ',';
336                 num_chars_until_comma = 3;
337             }
338
339             *p-- = static_cast<uint8>('0' + (value % 10U));
340             num_chars_until_comma--;
341
342             value /= 10U;
343         } while (value);
344
345         const size_t total_bytes = (buf + cBufSize - 1) - p;
346         if (total_bytes > len)
347             return false;
348
349         for (size_t i = 0; i < total_bytes; i++)
350             pDst[i] = p[1 + i];
351
352         return true;
353     }
354
355     dynamic_string uint64_to_string_with_commas(uint64_t value)
356     {
357         const uint cBufSize = 32;
358         char buf[cBufSize];
359         uint64_to_string_with_commas(value, buf, sizeof(buf));
360         return dynamic_string(buf);
361     }
362
363     bool int64_to_string(int64_t value, char *pDst, uint len)
364     {
365         VOGL_ASSERT(pDst);
366
367         const uint cBufSize = 32;
368         char buf[cBufSize];
369
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);
372
373         char *p = buf + cBufSize - 1;
374
375         *p-- = '\0';
376
377         do
378         {
379             *p-- = static_cast<uint8>('0' + (j % 10U));
380             j /= 10U;
381         } while (j);
382
383         if (value < 0)
384             *p-- = '-';
385
386         const size_t total_bytes = (buf + cBufSize - 1) - p;
387         if (total_bytes > len)
388         {
389             if ((pDst) && (len))
390                 *pDst = '\0';
391             return false;
392         }
393
394         for (size_t i = 0; i < total_bytes; i++)
395             pDst[i] = p[1 + i];
396
397         return true;
398     }
399
400     bool string_ptr_to_int(const char *&pBuf, int &value)
401     {
402         value = 0;
403
404         VOGL_ASSERT(pBuf);
405         const char *p = pBuf;
406
407         while (*p && vogl_isspace(*p))
408             p++;
409
410         uint result = 0;
411         bool negative = false;
412
413         if ((p[0] == '0') && (p[1] == 'x'))
414         {
415             p += 2;
416
417             if (!vogl_isxdigit(*p))
418                 return false;
419
420             while (*p)
421             {
422                 int v = utils::from_hex(*p);
423                 if (v < 0)
424                     break;
425
426                 if (result & 0xF0000000UL)
427                     return false;
428
429                 result = (result << 4) | static_cast<uint>(v);
430
431                 p++;
432             }
433         }
434         else
435         {
436             if (!vogl_isdigit(*p))
437             {
438                 if (p[0] == '-')
439                 {
440                     negative = true;
441                     p++;
442                 }
443                 else
444                     return false;
445             }
446
447             while (*p && vogl_isdigit(*p))
448             {
449                 if (result & 0xE0000000U)
450                     return false;
451
452                 const uint result8 = result << 3U;
453                 const uint result2 = result << 1U;
454
455                 if (result2 > (0xFFFFFFFFU - result8))
456                     return false;
457
458                 result = result8 + result2;
459
460                 uint c = p[0] - '0';
461                 if (c > (0xFFFFFFFFU - result))
462                     return false;
463
464                 result += c;
465
466                 p++;
467             }
468         }
469
470         if (negative)
471         {
472             if (result > 0x80000000U)
473                 return false;
474             value = -static_cast<int>(result);
475         }
476         else
477         {
478             if (result > 0x7FFFFFFFU)
479                 return false;
480             value = static_cast<int>(result);
481         }
482
483         pBuf = p;
484
485         return true;
486     }
487
488     bool string_ptr_to_int64(const char *&pBuf, int64_t &value)
489     {
490         value = 0;
491
492         VOGL_ASSERT(pBuf);
493         const char *p = pBuf;
494
495         while (*p && vogl_isspace(*p))
496             p++;
497
498         uint64_t result = 0;
499         bool negative = false;
500
501         if ((p[0] == '0') && (p[1] == 'x'))
502         {
503             p += 2;
504
505             if (!vogl_isxdigit(*p))
506                 return false;
507
508             while (*p)
509             {
510                 int v = utils::from_hex(*p);
511                 if (v < 0)
512                     break;
513
514                 if (result & 0xF000000000000000ULL)
515                     return false;
516
517                 result = (result << 4) | static_cast<uint64_t>(v);
518
519                 p++;
520             }
521         }
522         else
523         {
524             if (!vogl_isdigit(*p))
525             {
526                 if (p[0] == '-')
527                 {
528                     negative = true;
529                     p++;
530                 }
531                 else
532                     return false;
533             }
534
535             while (*p && vogl_isdigit(*p))
536             {
537                 if (result & 0xE000000000000000ULL)
538                     return false;
539
540                 const uint64_t result8 = result << 3U;
541                 const uint64_t result2 = result << 1U;
542
543                 if (result2 > (0xFFFFFFFFFFFFFFFFULL - result8))
544                     return false;
545
546                 result = result8 + result2;
547
548                 uint c = p[0] - '0';
549                 if (c > (0xFFFFFFFFFFFFFFFFULL - result))
550                     return false;
551
552                 result += c;
553
554                 p++;
555             }
556         }
557
558         if (negative)
559         {
560             if (result > 0x8000000000000000ULL)
561                 return false;
562             value = -static_cast<int64_t>(result);
563         }
564         else
565         {
566             if (result > 0x7FFFFFFFFFFFFFFFULL)
567                 return false;
568             value = static_cast<int64_t>(result);
569         }
570
571         pBuf = p;
572
573         return true;
574     }
575
576     bool string_ptr_to_uint(const char *&pBuf, uint &value)
577     {
578         value = 0;
579
580         VOGL_ASSERT(pBuf);
581         const char *p = pBuf;
582
583         while (*p && vogl_isspace(*p))
584             p++;
585
586         uint result = 0;
587         if ((p[0] == '0') && (p[1] == 'x'))
588         {
589             p += 2;
590
591             if (!vogl_isxdigit(*p))
592                 return false;
593
594             while (*p)
595             {
596                 int v = utils::from_hex(*p);
597                 if (v < 0)
598                     break;
599
600                 if (result & 0xF0000000UL)
601                     return false;
602
603                 result = (result << 4) | static_cast<uint>(v);
604
605                 p++;
606             }
607         }
608         else
609         {
610             if (!vogl_isdigit(*p))
611                 return false;
612
613             while (*p && vogl_isdigit(*p))
614             {
615                 if (result & 0xE0000000U)
616                     return false;
617
618                 const uint result8 = result << 3U;
619                 const uint result2 = result << 1U;
620
621                 if (result2 > (0xFFFFFFFFU - result8))
622                     return false;
623
624                 result = result8 + result2;
625
626                 uint c = p[0] - '0';
627                 if (c > (0xFFFFFFFFU - result))
628                     return false;
629
630                 result += c;
631
632                 p++;
633             }
634         }
635
636         value = result;
637
638         pBuf = p;
639
640         return true;
641     }
642
643     bool string_ptr_to_uint64(const char *&pBuf, uint64_t &value)
644     {
645         value = 0;
646
647         VOGL_ASSERT(pBuf);
648         const char *p = pBuf;
649
650         while (*p && vogl_isspace(*p))
651             p++;
652
653         uint64_t result = 0;
654
655         if ((p[0] == '0') && (p[1] == 'x'))
656         {
657             p += 2;
658
659             if (!vogl_isxdigit(*p))
660                 return false;
661
662             while (*p)
663             {
664                 int v = utils::from_hex(*p);
665                 if (v < 0)
666                     break;
667
668                 if (result & 0xF000000000000000ULL)
669                     return false;
670
671                 result = (result << 4) | static_cast<uint64_t>(v);
672
673                 p++;
674             }
675         }
676         else
677         {
678             if (!vogl_isdigit(*p))
679                 return false;
680
681             while (*p && vogl_isdigit(*p))
682             {
683                 if (result & 0xE000000000000000ULL)
684                     return false;
685
686                 const uint64_t result8 = result << 3U;
687                 const uint64_t result2 = result << 1U;
688
689                 if (result2 > (0xFFFFFFFFFFFFFFFFULL - result8))
690                     return false;
691
692                 result = result8 + result2;
693
694                 uint c = p[0] - '0';
695                 if (c > (0xFFFFFFFFFFFFFFFFULL - result))
696                     return false;
697
698                 result += c;
699
700                 p++;
701             }
702         }
703
704         value = result;
705
706         pBuf = p;
707
708         return true;
709     }
710
711     bool string_ptr_to_bool(const char *&p, bool &value)
712     {
713         VOGL_ASSERT(p);
714
715         value = false;
716
717         if (vogl_stricmp(p, "false") == 0)
718         {
719             p += 5;
720             return true;
721         }
722
723         if (vogl_stricmp(p, "true") == 0)
724         {
725             p += 4;
726             value = true;
727             return true;
728         }
729
730         uint v;
731         if (string_ptr_to_uint(p, v))
732         {
733             if (!v)
734                 return true;
735             else if (v == 1)
736             {
737                 value = true;
738                 return true;
739             }
740         }
741
742         return false;
743     }
744
745     bool string_ptr_to_float(const char *&p, float &value, uint round_digit)
746     {
747         double d;
748         if (!string_ptr_to_double(p, d, round_digit))
749         {
750             value = 0;
751             return false;
752         }
753         value = static_cast<float>(d);
754         return true;
755     }
756
757     bool string_ptr_to_double(const char *&p, double &value, uint round_digit)
758     {
759         return string_ptr_to_double(p, p + 128, value, round_digit);
760     }
761
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)
764     {
765         VOGL_ASSERT(p);
766
767         value = 0;
768
769         enum
770         {
771             AF_BLANK = 1,
772             AF_SIGN = 2,
773             AF_DPOINT = 3,
774             AF_BADCHAR = 4,
775             AF_OVRFLOW = 5,
776             AF_EXPONENT = 6,
777             AF_NODIGITS = 7
778         };
779         int status = 0;
780
781         const char *buf = p;
782
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;
786
787         double whole = 0, frac = 0, scale = 1, exponent = 1;
788
789         if (p >= pEnd)
790         {
791             status = AF_NODIGITS;
792             goto af_exit;
793         }
794
795         while (*buf)
796         {
797             if (!vogl_isspace(*buf))
798                 break;
799             if (++buf >= pEnd)
800             {
801                 status = AF_NODIGITS;
802                 goto af_exit;
803             }
804         }
805
806         p = buf;
807
808         while (*buf)
809         {
810             p = buf;
811             if (buf >= pEnd)
812                 break;
813
814             int i = *buf++;
815
816             switch (i)
817             {
818                 case 'e':
819                 case 'E':
820                 {
821                     got_e_flag = 1;
822                     goto exit_while;
823                 }
824                 case '+':
825                 {
826                     if ((got_num_flag) || (got_sign_flag))
827                     {
828                         status = AF_SIGN;
829                         goto af_exit;
830                     }
831
832                     got_sign_flag = 1;
833
834                     break;
835                 }
836                 case '-':
837                 {
838                     if ((got_num_flag) || (got_sign_flag))
839                     {
840                         status = AF_SIGN;
841                         goto af_exit;
842                     }
843
844                     got_sign_flag = -1;
845
846                     break;
847                 }
848                 case '.':
849                 {
850                     if (got_dp_flag)
851                     {
852                         status = AF_DPOINT;
853                         goto af_exit;
854                     }
855
856                     got_dp_flag = 1;
857
858                     break;
859                 }
860                 default:
861                 {
862                     if ((i < '0') || (i > '9'))
863                         goto exit_while;
864                     else
865                     {
866                         i -= '0';
867
868                         got_num_flag = 1;
869
870                         if (got_dp_flag)
871                         {
872                             if (frac_count < round_digit)
873                             {
874                                 frac = frac * 10.0f + i;
875
876                                 scale = scale * 10.0f;
877                             }
878                             else if (frac_count == round_digit)
879                             {
880                                 if (i >= 5) /* check for round */
881                                     frac = frac + 1.0f;
882                             }
883
884                             frac_count++;
885                         }
886                         else
887                         {
888                             whole = whole * 10.0f + i;
889
890                             whole_count++;
891
892                             if (whole > 1e+100)
893                             {
894                                 status = AF_OVRFLOW;
895                                 goto af_exit;
896                             }
897                         }
898                     }
899
900                     break;
901                 }
902             }
903         }
904
905     exit_while:
906
907         if (got_e_flag)
908         {
909             if ((got_num_flag == 0) && (got_dp_flag))
910             {
911                 status = AF_EXPONENT;
912                 goto af_exit;
913             }
914
915             int e = 0;
916             e_sign = 1;
917             got_num_flag = 0;
918             got_e_sign_flag = 0;
919
920             while (*buf)
921             {
922                 p = buf;
923                 if (buf >= pEnd)
924                     break;
925
926                 int i = *buf++;
927
928                 if (i == '+')
929                 {
930                     if ((got_num_flag) || (got_e_sign_flag))
931                     {
932                         status = AF_EXPONENT;
933                         goto af_exit;
934                     }
935
936                     e_sign = 1;
937                     got_e_sign_flag = 1;
938                 }
939                 else if (i == '-')
940                 {
941                     if ((got_num_flag) || (got_e_sign_flag))
942                     {
943                         status = AF_EXPONENT;
944                         goto af_exit;
945                     }
946
947                     e_sign = -1;
948                     got_e_sign_flag = 1;
949                 }
950                 else if ((i >= '0') && (i <= '9'))
951                 {
952                     got_num_flag = 1;
953
954                     if ((e = (e * 10) + (i - 48)) > 100)
955                     {
956                         status = AF_EXPONENT;
957                         goto af_exit;
958                     }
959                 }
960                 else
961                     break;
962             }
963
964             for (int i = 1; i <= e; i++) /* compute 10^e */
965                 exponent = exponent * 10.0f;
966         }
967
968         if (((whole_count + frac_count) == 0) && (got_e_flag == 0))
969         {
970             status = AF_NODIGITS;
971             goto af_exit;
972         }
973
974         if (frac)
975             whole = whole + (frac / scale);
976
977         if (got_e_flag)
978         {
979             if (e_sign > 0)
980                 whole = whole * exponent;
981             else
982                 whole = whole / exponent;
983         }
984
985         if (got_sign_flag < 0)
986             whole = -whole;
987
988         value = whole;
989
990     af_exit:
991         return (status == 0);
992     }
993
994     dynamic_string to_hex_string(int value)
995     {
996         return dynamic_string(cVarArg, "0x%X", value);
997     }
998
999     dynamic_string to_hex_string(uint value)
1000     {
1001         return dynamic_string(cVarArg, "0x%X", value);
1002     }
1003
1004     dynamic_string to_hex_string(int64_t value)
1005     {
1006         return dynamic_string(cVarArg, "0x%" PRIX64, value);
1007     }
1008
1009     dynamic_string to_hex_string(uint64_t value)
1010     {
1011         return dynamic_string(cVarArg, "0x%" PRIX64, value);
1012     }
1013
1014     bool strutils_test()
1015     {
1016 #define CHECK(x)                  \
1017     do                            \
1018     {                             \
1019         if (!(x))                 \
1020         {                         \
1021             VOGL_ASSERT_ALWAYS; \
1022             return false;         \
1023         }                         \
1024     } while (0)
1025         for (uint i = 0; i < 256; i++)
1026         {
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));
1035         }
1036
1037         vogl::random r;
1038         for (uint t = 0; t < 0x1FFFFFF; t++)
1039         {
1040             int64_t i = r.urand64();
1041             if (r.irand(0, 100) == 0)
1042                 i = static_cast<int>(r.urand32());
1043
1044             if (t == 0)
1045                 i = cINT64_MIN;
1046             else if (t == 1)
1047                 i = cINT32_MIN;
1048             else if (t == 2)
1049                 i = cINT32_MAX;
1050             else if (t == 3)
1051                 i = cINT64_MAX;
1052             else if (t == 4)
1053                 i = cUINT64_MIN;
1054             else if (t == 5)
1055                 i = cUINT64_MAX;
1056             else if (t == 6)
1057                 i = cUINT32_MIN;
1058             else if (t == 7)
1059                 i = cUINT32_MAX;
1060             else if (t == 8)
1061                 i = -1;
1062             else if (t == 9)
1063                 i = 0;
1064             else if (t == 10)
1065                 i = 1;
1066
1067             char buf[256];
1068
1069             vogl::int64_to_string(i, buf, sizeof(buf));
1070             int64_t v64;
1071             const char *pBuf = buf;
1072             CHECK(vogl::string_ptr_to_int64(pBuf, v64));
1073             CHECK(v64 == i);
1074
1075             int v32;
1076             pBuf = buf;
1077             bool success = vogl::string_ptr_to_int(pBuf, v32);
1078             if (!success)
1079             {
1080                 CHECK((i < cINT32_MIN) || (i > cINT32_MAX));
1081             }
1082             else
1083             {
1084                 CHECK(v32 == i);
1085             }
1086
1087             vogl::uint64_to_string(static_cast<uint64_t>(i), buf, sizeof(buf));
1088             uint64_t uv64;
1089             pBuf = buf;
1090             CHECK(vogl::string_ptr_to_uint64(pBuf, uv64));
1091             CHECK(uv64 == static_cast<uint64_t>(i));
1092
1093             if ((t & 32767) == 32767)
1094                 printf("0x%08X\n", t);
1095         }
1096
1097         return true;
1098 #undef CHECK
1099     }
1100
1101 } // namespace vogl