]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_dynamic_string.cpp
Initial vogl checkin
[vogl] / src / voglcore / vogl_dynamic_string.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_dynamic_string.cpp
28 #include "vogl_core.h"
29 #include "vogl_strutils.h"
30 #include "vogl_json.h"
31
32 #if VOGL_SLOW_STRING_LEN_CHECKS
33 #warning vogl_dynamic_string.cpp: Slow string checking enabled
34 #endif
35
36 namespace vogl
37 {
38     dynamic_string g_empty_dynamic_string;
39
40     dynamic_string::dynamic_string(eVarArg dummy, const char *p, ...)
41     {
42         VOGL_NOTE_UNUSED(dummy);
43
44         set_to_empty_small_string();
45
46         VOGL_ASSERT(p);
47
48         va_list args;
49         va_start(args, p);
50         format_args(p, args);
51         va_end(args);
52     }
53
54     dynamic_string::dynamic_string(const char *p)
55     {
56         VOGL_ASSUME(cMaxDynamicStringBufSize <= cINT32_MAX);
57         // This class assumes little endian byte order, because the low bits of m_dyn.m_pStr must alias m_small.m_flag
58         VOGL_ASSUME(VOGL_LITTLE_ENDIAN_CPU);
59
60         VOGL_ASSERT(p);
61
62         set_to_empty_small_string();
63
64         set(p);
65     }
66
67     dynamic_string::dynamic_string(const char *p, uint len)
68     {
69         VOGL_ASSERT(p);
70
71         set_to_empty_small_string();
72
73         set_from_buf(p, len);
74     }
75
76     dynamic_string::dynamic_string(const dynamic_string &other)
77     {
78         set_to_empty_small_string();
79
80         set(other);
81     }
82
83     void dynamic_string::clear()
84     {
85         check();
86
87         if (is_dynamic())
88         {
89             vogl_delete_array(m_dyn.m_pStr);
90         }
91
92         set_to_empty_small_string();
93     }
94
95     void dynamic_string::empty()
96     {
97         truncate(0);
98     }
99
100     void dynamic_string::optimize()
101     {
102         if (!m_len)
103             clear();
104         else
105         {
106             if ((m_len + 1U) <= cSmallStringBufSize)
107             {
108                 if (is_dynamic())
109                 {
110                     char *pStr = m_dyn.m_pStr;
111
112                     memcpy(m_small.m_buf, pStr, m_len + 1);
113
114                     vogl_delete_array(pStr);
115
116                     set_small_string_flag();
117                 }
118             }
119             else
120             {
121                 if (!is_dynamic())
122                 {
123                     VOGL_ASSERT_ALWAYS;
124                     clear();
125                     return;
126                 }
127
128                 uint32 min_buf_size = m_len + 1;
129
130                 // TODO: In some cases it'll make no difference to try to shrink the block due to allocation alignment, etc. issues
131                 if (m_dyn.m_buf_size > min_buf_size)
132                 {
133                     char *p = vogl_new_array(char, min_buf_size);
134                     memcpy(p, m_dyn.m_pStr, m_len + 1);
135
136                     vogl_delete_array(m_dyn.m_pStr);
137
138                     set_dyn_string_ptr(p);
139
140                     m_dyn.m_buf_size = min_buf_size;
141                 }
142             }
143         }
144
145         check();
146     }
147
148     void dynamic_string::reserve(uint new_capacity)
149     {
150         ensure_buf(new_capacity, true);
151     }
152
153     int dynamic_string::compare(const char *p, bool case_sensitive) const
154     {
155         VOGL_ASSERT(p);
156
157         const int result = (case_sensitive ? strcmp : vogl_stricmp)(get_ptr_priv(), p);
158
159         if (result < 0)
160             return -1;
161         else if (result > 0)
162             return 1;
163
164         return 0;
165     }
166
167     int dynamic_string::compare(const dynamic_string &rhs, bool case_sensitive) const
168     {
169         return compare(rhs.get_ptr_priv(), case_sensitive);
170     }
171
172     int dynamic_string::compare_using_length(const char *p, bool case_sensitive) const
173     {
174         uint l_len = get_len();
175         uint r_len = vogl_strlen(p);
176
177         if (l_len < r_len)
178             return -1;
179         else if (l_len == r_len)
180             return compare(p, case_sensitive);
181         else
182             return 1;
183     }
184
185     int dynamic_string::compare_using_length(const dynamic_string &rhs, bool case_sensitive) const
186     {
187         return compare_using_length(rhs.get_ptr(), case_sensitive);
188     }
189
190     dynamic_string &dynamic_string::set(const char *p, uint max_len)
191     {
192         VOGL_ASSERT(p);
193
194         const uint len = math::minimum<uint>(max_len, vogl_strlen(p));
195         VOGL_ASSERT(len <= cMaxDynamicStringLen);
196
197         if ((!len) || (len > cMaxDynamicStringLen))
198             clear();
199         else
200         {
201             char *pStr = get_ptr_priv();
202             uint buf_size = get_buf_size();
203             if ((p >= pStr) && (p < (pStr + buf_size)))
204             {
205                 if (pStr != p)
206                     memmove(pStr, p, len);
207                 pStr[len] = '\0';
208
209                 m_len = len;
210             }
211             else if (ensure_buf(len, false))
212             {
213                 m_len = len;
214                 memcpy(get_ptr_priv(), p, m_len + 1);
215             }
216         }
217
218         check();
219
220         return *this;
221     }
222
223     dynamic_string &dynamic_string::set(const dynamic_string &other, uint max_len)
224     {
225         if (this == &other)
226         {
227             if (max_len < m_len)
228             {
229                 get_ptr_priv()[max_len] = '\0';
230                 m_len = max_len;
231             }
232         }
233         else
234         {
235             const uint len = math::minimum<uint>(max_len, other.m_len);
236
237             if (!len)
238                 clear();
239             else if (ensure_buf(len, false))
240             {
241                 char *pStr = get_ptr_priv();
242                 m_len = len;
243                 memcpy(pStr, other.get_ptr_priv(), m_len);
244                 pStr[len] = '\0';
245             }
246         }
247
248         check();
249
250         return *this;
251     }
252
253     bool dynamic_string::set_len(uint new_len, char fill_char)
254     {
255         if ((new_len > cMaxDynamicStringLen) || (!fill_char))
256         {
257             VOGL_ASSERT_ALWAYS;
258             return false;
259         }
260
261         uint cur_len = m_len;
262
263         if (ensure_buf(new_len, true))
264         {
265             char *pStr = get_ptr_priv();
266
267             if (new_len > cur_len)
268                 memset(pStr + cur_len, fill_char, new_len - cur_len);
269
270             pStr[new_len] = 0;
271
272             m_len = new_len;
273
274             check();
275         }
276
277         return true;
278     }
279
280     dynamic_string &dynamic_string::set_from_raw_buf_and_assume_ownership(char *pBuf, uint buf_size_in_bytes, uint len_in_chars)
281     {
282         VOGL_ASSERT(buf_size_in_bytes <= cMaxDynamicStringBufSize);
283         VOGL_ASSERT((len_in_chars + 1) <= buf_size_in_bytes);
284         VOGL_ASSERT(len_in_chars <= cMaxDynamicStringLen);
285         VOGL_ASSERT(!pBuf || !pBuf[len_in_chars]);
286         VOGL_ASSERT(pBuf || ((!buf_size_in_bytes) && (!len_in_chars)));
287
288         clear();
289
290         if (!pBuf)
291             return *this;
292
293         set_dyn_string_ptr(pBuf);
294         m_dyn.m_buf_size = buf_size_in_bytes;
295         m_len = len_in_chars;
296
297         check();
298
299         return *this;
300     }
301
302     dynamic_string &dynamic_string::set_from_buf(const void *pBuf, uint buf_len_in_chars)
303     {
304         if (!pBuf)
305         {
306             VOGL_ASSERT(!buf_len_in_chars);
307             truncate(0);
308             return *this;
309         }
310
311         if (buf_len_in_chars > cMaxDynamicStringLen)
312         {
313             VOGL_ASSERT_ALWAYS;
314             clear();
315             return *this;
316         }
317
318 #ifdef VOGL_BUILD_DEBUG
319         if ((buf_len_in_chars) && (memchr(pBuf, 0, buf_len_in_chars) != NULL))
320         {
321             VOGL_ASSERT_ALWAYS;
322             clear();
323             return *this;
324         }
325 #endif
326
327         if (ensure_buf(buf_len_in_chars, false))
328         {
329             char *pStr = get_ptr_priv();
330
331             if (buf_len_in_chars)
332                 memcpy(pStr, pBuf, buf_len_in_chars);
333
334             pStr[buf_len_in_chars] = 0;
335
336             m_len = buf_len_in_chars;
337
338             check();
339         }
340
341         return *this;
342     }
343
344     dynamic_string &dynamic_string::set_char(uint index, char c)
345     {
346         VOGL_ASSERT(index <= m_len);
347
348         if (!c)
349             truncate(index);
350         else if (index < m_len)
351         {
352             get_ptr_priv()[index] = c;
353
354             check();
355         }
356         else if (index == m_len)
357             append_char(c);
358
359         return *this;
360     }
361
362     dynamic_string &dynamic_string::append_char(char c)
363     {
364         if (!c)
365         {
366             // Can't append a zero terminator - there's already one there.
367             VOGL_ASSERT_ALWAYS;
368             return *this;
369         }
370
371         if (m_len == cMaxDynamicStringLen)
372         {
373             VOGL_ASSERT_ALWAYS;
374             return *this;
375         }
376
377         if (ensure_buf(m_len + 1))
378         {
379             char *pStr = get_ptr_priv();
380
381             pStr[m_len] = c;
382             pStr[m_len + 1] = '\0';
383             m_len++;
384             check();
385         }
386
387         return *this;
388     }
389
390     dynamic_string &dynamic_string::truncate(uint new_len)
391     {
392         if (new_len < m_len)
393         {
394             get_ptr_priv()[new_len] = '\0';
395             m_len = new_len;
396             check();
397         }
398         return *this;
399     }
400
401     dynamic_string &dynamic_string::shorten(uint chars_to_remove)
402     {
403         VOGL_ASSERT(m_len >= chars_to_remove);
404         if (m_len < chars_to_remove)
405             return *this;
406         return truncate(m_len - chars_to_remove);
407     }
408
409     dynamic_string &dynamic_string::tolower()
410     {
411         if (m_len)
412         {
413             vogl_strlwr(get_ptr_priv());
414         }
415         return *this;
416     }
417
418     dynamic_string &dynamic_string::toupper()
419     {
420         if (m_len)
421         {
422             vogl_strupr(get_ptr_priv());
423         }
424         return *this;
425     }
426
427     dynamic_string &dynamic_string::append(const char *p, uint len)
428     {
429         VOGL_ASSERT(p);
430         VOGL_ASSERT(len <= vogl_strlen(p));
431
432         if (!len)
433             return *this;
434
435         if (ptr_refers_to_self(p))
436         {
437             dynamic_string temp(*this);
438             temp.append(p, len);
439             swap(temp);
440             return *this;
441         }
442
443         if (len > (cMaxDynamicStringLen - m_len))
444         {
445             VOGL_ASSERT_ALWAYS;
446             return *this;
447         }
448
449         uint new_total_len = m_len + len;
450         VOGL_ASSERT(new_total_len <= cMaxDynamicStringLen);
451         if ((new_total_len) && (ensure_buf(new_total_len)))
452         {
453             char *pStr = get_ptr_priv();
454
455             memcpy(pStr + m_len, p, len);
456             pStr[m_len + len] = '\0';
457             m_len += len;
458             check();
459         }
460
461         return *this;
462     }
463
464     dynamic_string &dynamic_string::append(const char *p)
465     {
466         VOGL_ASSERT(p);
467
468         if (ptr_refers_to_self(p))
469         {
470             dynamic_string temp(*this);
471             temp.append(p);
472             swap(temp);
473             return *this;
474         }
475
476         uint len = vogl_strlen(p);
477         if (len > (cMaxDynamicStringLen - m_len))
478         {
479             VOGL_ASSERT_ALWAYS;
480             return *this;
481         }
482
483         uint new_total_len = m_len + len;
484         VOGL_ASSERT(new_total_len <= cMaxDynamicStringLen);
485         if ((new_total_len) && (ensure_buf(new_total_len)))
486         {
487             memcpy(get_ptr_priv() + m_len, p, len + 1);
488             m_len += len;
489             check();
490         }
491
492         return *this;
493     }
494
495     dynamic_string &dynamic_string::append(const dynamic_string &other)
496     {
497         if (this == &other)
498         {
499             dynamic_string temp(*this);
500             temp.append(other);
501             swap(temp);
502             return *this;
503         }
504
505         uint len = other.m_len;
506
507         if (len > (cMaxDynamicStringLen - m_len))
508         {
509             VOGL_ASSERT_ALWAYS;
510             return *this;
511         }
512
513         uint new_total_len = m_len + len;
514         VOGL_ASSERT(new_total_len <= cMaxDynamicStringLen);
515         if ((new_total_len) && ensure_buf(new_total_len))
516         {
517             memcpy(get_ptr_priv() + m_len, other.get_ptr_priv(), len + 1);
518             m_len = m_len + len;
519             check();
520         }
521
522         return *this;
523     }
524
525     dynamic_string operator+(const char *p, const dynamic_string &a)
526     {
527         return dynamic_string(p).append(a);
528     }
529
530     dynamic_string operator+(const dynamic_string &a, const char *p)
531     {
532         return dynamic_string(a).append(p);
533     }
534
535     dynamic_string operator+(const dynamic_string &a, const dynamic_string &b)
536     {
537         return dynamic_string(a).append(b);
538     }
539
540     dynamic_string &dynamic_string::format_args(const char *p, va_list args)
541     {
542         VOGL_ASSERT(p);
543
544         const uint cBufSize = 4096;
545         int buf_size = cBufSize;
546         char buf[cBufSize];
547         char *pBuf = buf;
548
549 #ifdef _MSC_VER
550         int l = vsnprintf_s(pBuf, buf_size, _TRUNCATE, p, args);
551 #else
552         int l = vsnprintf(pBuf, buf_size, p, args);
553 #endif
554
555         if (l >= buf_size)
556         {
557             buf_size = l + 1;
558             pBuf = static_cast<char *>(vogl_malloc(buf_size));
559
560 #ifdef _MSC_VER
561             l = vsnprintf_s(pBuf, buf_size, _TRUNCATE, p, args);
562 #else
563             l = vsnprintf(pBuf, buf_size, p, args);
564 #endif
565         }
566
567         if (l > cMaxDynamicStringLen)
568         {
569             VOGL_ASSERT_ALWAYS;
570             clear();
571         }
572         else if (l <= 0)
573             clear();
574         else if (ensure_buf(l, false))
575         {
576             memcpy(get_ptr_priv(), pBuf, l + 1);
577
578             m_len = l;
579
580             check();
581         }
582         else
583         {
584             clear();
585         }
586
587         if (pBuf != buf)
588             vogl_free(pBuf);
589
590         return *this;
591     }
592
593     dynamic_string &dynamic_string::format(const char *p, ...)
594     {
595         VOGL_ASSERT(p);
596
597         va_list args;
598         va_start(args, p);
599         format_args(p, args);
600         va_end(args);
601         return *this;
602     }
603
604     dynamic_string &dynamic_string::format_append(const char *p, ...)
605     {
606         VOGL_ASSERT(p);
607
608         dynamic_string temp;
609
610         va_list args;
611         va_start(args, p);
612         temp.format_args(p, args);
613         va_end(args);
614
615         append(temp);
616
617         return *this;
618     }
619
620     dynamic_string &dynamic_string::crop(uint start, uint len)
621     {
622         if (start >= m_len)
623         {
624             clear();
625             return *this;
626         }
627
628         len = math::minimum<uint>(len, m_len - start);
629
630         char *pStr = get_ptr_priv();
631
632         if (start)
633             memmove(pStr, pStr + start, len);
634
635         pStr[len] = '\0';
636
637         m_len = len;
638
639         check();
640
641         return *this;
642     }
643
644     dynamic_string &dynamic_string::remove(uint start, uint len)
645     {
646         VOGL_ASSERT(start < m_len);
647
648         if ((start >= m_len) || (!len))
649             return *this;
650
651         uint max_len = m_len - start;
652         VOGL_ASSERT(len <= max_len);
653         if (len > max_len)
654             return *this;
655
656         uint num_chars_remaining = m_len - (start + len);
657
658         // + 1 to move terminator
659         memmove(get_ptr_priv() + start, get_ptr_priv() + start + len, num_chars_remaining + 1);
660
661         m_len = start + num_chars_remaining;
662
663         check();
664
665         return *this;
666     }
667
668     dynamic_string &dynamic_string::substring(uint start, uint end)
669     {
670         VOGL_ASSERT(start <= end);
671         if (start > end)
672             return *this;
673         return crop(start, end - start);
674     }
675
676     dynamic_string &dynamic_string::left(uint len)
677     {
678         return substring(0, len);
679     }
680
681     dynamic_string &dynamic_string::mid(uint start, uint len)
682     {
683         return crop(start, len);
684     }
685
686     dynamic_string &dynamic_string::right(uint start)
687     {
688         return substring(start, get_len());
689     }
690
691     dynamic_string &dynamic_string::tail(uint num)
692     {
693         return substring(math::maximum<int>(static_cast<int>(get_len()) - static_cast<int>(num), 0), get_len());
694     }
695
696     // This is not particularly efficient. Just here as a one-off utility method.
697     dynamic_string &dynamic_string::replace(const char *pFind, const char *pReplacement, bool case_sensitive, uint *pNum_found, uint max_replacements)
698     {
699         VOGL_ASSERT(pFind);
700
701         if (pNum_found)
702             *pNum_found = 0;
703
704         VOGL_ASSERT(max_replacements);
705         if (!max_replacements)
706             return *this;
707
708         uint num_found = 0;
709
710         uint find_len = vogl_strlen(pFind);
711         uint replacement_len = pReplacement ? vogl_strlen(pReplacement) : 0;
712
713         dynamic_string temp;
714
715         int cur_ofs = 0;
716         for (;;)
717         {
718             int find_ofs = find_left(pFind, case_sensitive, cur_ofs);
719             if (find_ofs < 0)
720                 break;
721
722             temp = *this;
723             temp.truncate(find_ofs);
724             if (pReplacement)
725                 temp += pReplacement;
726             temp += right(find_ofs + find_len);
727             temp.swap(*this);
728
729             cur_ofs = find_ofs + replacement_len;
730
731             num_found++;
732             if (num_found >= max_replacements)
733                 break;
734         }
735
736         if (pNum_found)
737             *pNum_found = num_found;
738
739         return *this;
740     }
741
742     dynamic_string &dynamic_string::unquote()
743     {
744         if (m_len >= 2)
745         {
746             if (((*this)[0] == '\"') && ((*this)[m_len - 1] == '\"'))
747             {
748                 return mid(1, m_len - 2);
749             }
750         }
751
752         return *this;
753     }
754
755     int dynamic_string::find_left(const char *p, bool case_sensitive, uint start_ofs) const
756     {
757         VOGL_ASSERT(p);
758         VOGL_ASSERT(start_ofs <= m_len);
759
760         const uint p_len = vogl_strlen(p);
761
762         if (p_len > m_len)
763             return -1;
764
765         const char *pStr = get_ptr_priv();
766
767         for (uint i = start_ofs; i <= m_len - p_len; i++)
768             if ((case_sensitive ? strncmp : vogl_strnicmp)(p, pStr + i, p_len) == 0)
769                 return i;
770
771         return -1;
772     }
773
774     bool dynamic_string::contains(const char *p, bool case_sensitive) const
775     {
776         return find_left(p, case_sensitive) >= 0;
777     }
778
779     bool dynamic_string::contains(char c) const
780     {
781         return find_left(c) >= 0;
782     }
783
784     bool dynamic_string::begins_with(const char *p, bool case_sensitive) const
785     {
786         VOGL_ASSERT(p);
787
788         const uint p_len = vogl_strlen(p);
789         if ((!p_len) || (m_len < p_len))
790             return false;
791
792         return (case_sensitive ? strncmp : vogl_strnicmp)(p, get_ptr_priv(), p_len) == 0;
793     }
794
795     bool dynamic_string::ends_with(const char *p, bool case_sensitive) const
796     {
797         VOGL_ASSERT(p);
798
799         const uint p_len = vogl_strlen(p);
800         if ((!p_len) || (m_len < p_len))
801             return false;
802
803         return (case_sensitive ? strcmp : vogl_stricmp)(get_ptr_priv() + m_len - p_len, p) == 0;
804     }
805
806     uint dynamic_string::count_char(char c) const
807     {
808         const char *pStr = get_ptr_priv();
809
810         uint count = 0;
811         for (uint i = 0; i < m_len; i++)
812             if (pStr[i] == c)
813                 count++;
814         return count;
815     }
816
817     int dynamic_string::find_left(char c, int start_ofs) const
818     {
819         const char *pStr = get_ptr_priv();
820
821         for (uint i = start_ofs; i < m_len; i++)
822             if (pStr[i] == c)
823                 return i;
824         return -1;
825     }
826
827     int dynamic_string::find_right(char c) const
828     {
829         if (m_len)
830         {
831             const char *pStr = get_ptr_priv();
832
833             uint i = m_len - 1;
834             for (;;)
835             {
836                 if (pStr[i] == c)
837                     return i;
838                 if (!i)
839                     break;
840                 --i;
841             }
842         }
843
844         return -1;
845     }
846
847     int dynamic_string::find_right(char c, uint start_ofs) const
848     {
849         if (start_ofs >= m_len)
850         {
851             VOGL_ASSERT_ALWAYS;
852             return -1;
853         }
854
855         if (m_len)
856         {
857             const char *pStr = get_ptr_priv();
858
859             uint i = start_ofs;
860             for (;;)
861             {
862                 if (pStr[i] == c)
863                     return i;
864                 if (!i)
865                     break;
866                 --i;
867             }
868         }
869
870         return -1;
871     }
872
873     int dynamic_string::find_right(const char *p, bool case_sensitive) const
874     {
875         VOGL_ASSERT(p);
876         const uint p_len = vogl_strlen(p);
877
878         if (p_len > m_len)
879             return -1;
880
881         if (m_len)
882         {
883             const char *pStr = get_ptr_priv();
884
885             uint i = m_len - p_len;
886             for (;;)
887             {
888                 if ((case_sensitive ? strncmp : vogl_strnicmp)(p, &pStr[i], p_len) == 0)
889                     return i;
890                 if (!i)
891                     break;
892                 --i;
893             }
894         }
895
896         return -1;
897     }
898
899     dynamic_string &dynamic_string::trim()
900     {
901         VOGL_ASSERT(m_len <= cMaxDynamicStringLen);
902
903         const char *pStr = get_ptr_priv();
904
905         int s, e;
906         for (s = 0; s < static_cast<int>(m_len); s++)
907             if (!vogl_isspace(pStr[s]))
908                 break;
909
910         for (e = m_len - 1; e > s; e--)
911             if (!vogl_isspace(pStr[e]))
912                 break;
913
914         return crop(s, e - s + 1);
915     }
916
917     dynamic_string &dynamic_string::trim_end()
918     {
919         VOGL_ASSERT(m_len <= cMaxDynamicStringLen);
920
921         const char *pStr = get_ptr_priv();
922
923         int e;
924         for (e = static_cast<int>(m_len) - 1; e >= 0; e--)
925             if (!vogl_isspace(pStr[e]))
926                 break;
927
928         return crop(0, e + 1);
929     }
930
931     dynamic_string &dynamic_string::trim_crlf()
932     {
933         VOGL_ASSERT(m_len <= cMaxDynamicStringLen);
934
935         const char *pStr = get_ptr_priv();
936
937         int s = 0, e;
938
939         for (e = static_cast<int>(m_len) - 1; e > s; e--)
940             if ((pStr[e] != 13) && (pStr[e] != 10))
941                 break;
942
943         return crop(s, e - s + 1);
944     }
945
946     dynamic_string &dynamic_string::remap(int from_char, int to_char)
947     {
948         char *pStr = get_ptr_priv();
949
950         for (uint i = 0; i < m_len; i++)
951             if (pStr[i] == from_char)
952                 pStr[i] = static_cast<char>(to_char);
953
954         return *this;
955     }
956
957     bool dynamic_string::validate() const
958     {
959 #define CHECK(x)                           \
960     if (!(x))                              \
961     {                                      \
962         vogl_debug_break_if_debugging(); \
963         return false;                      \
964     }
965         CHECK(m_len < get_buf_size());
966         CHECK(m_len <= cMaxDynamicStringLen);
967
968         if (is_dynamic())
969         {
970             CHECK(m_dyn.m_pStr != NULL);
971             CHECK(m_dyn.m_buf_size);
972             CHECK(m_dyn.m_buf_size <= cMaxDynamicStringBufSize);
973
974             CHECK(((m_dyn.m_pStr + m_dyn.m_buf_size) <= (const char *)this) || (m_dyn.m_pStr >= (const char *)(this + 1)));
975             CHECK(((uint64_t)m_dyn.m_pStr & (VOGL_MIN_ALLOC_ALIGNMENT - 1)) == 0);
976             CHECK(vogl_msize_array(m_dyn.m_pStr) >= m_dyn.m_buf_size);
977         }
978
979         const char *pStr = get_ptr_priv();
980
981         CHECK(!pStr[m_len]);
982
983 #if VOGL_SLOW_STRING_LEN_CHECKS
984         CHECK(vogl_strlen(pStr) == m_len);
985 #endif
986
987 #undef CHECK
988         return true;
989     }
990
991     void dynamic_string::ensure_dynamic()
992     {
993         if (is_dynamic())
994             return;
995
996         uint new_buf_size = math::maximum(m_len + 1, 16U);
997
998         new_buf_size = math::minimum<uint>(new_buf_size, cMaxDynamicStringBufSize);
999
1000         char *p = vogl_new_array(char, new_buf_size);
1001
1002         VOGL_ASSERT(new_buf_size >= (m_len + 1));
1003         memcpy(p, m_small.m_buf, m_len + 1);
1004
1005         set_dyn_string_ptr(p);
1006
1007         m_dyn.m_buf_size = static_cast<uint32>(math::minimum<uint64_t>(cMaxDynamicStringBufSize, vogl_msize_array(m_dyn.m_pStr)));
1008         VOGL_ASSERT(m_dyn.m_buf_size >= new_buf_size);
1009
1010         check();
1011     }
1012
1013     bool dynamic_string::ensure_buf(uint len, bool preserve_contents)
1014     {
1015         uint buf_size_needed = len + 1;
1016         if (buf_size_needed > cMaxDynamicStringBufSize)
1017         {
1018             VOGL_ASSERT_ALWAYS;
1019             return false;
1020         }
1021
1022         if (buf_size_needed > get_buf_size())
1023             return expand_buf(buf_size_needed, preserve_contents);
1024
1025         return true;
1026     }
1027
1028     bool dynamic_string::expand_buf(uint new_buf_size, bool preserve_contents)
1029     {
1030         VOGL_ASSERT(new_buf_size <= cMaxDynamicStringBufSize);
1031
1032         new_buf_size = static_cast<uint>(math::minimum<uint64_t>(cMaxDynamicStringBufSize, math::next_pow2(new_buf_size)));
1033
1034         char *p = vogl_new_array(char, new_buf_size);
1035
1036         if (preserve_contents)
1037         {
1038             VOGL_ASSERT(new_buf_size >= (m_len + 1));
1039             memcpy(p, get_ptr_priv(), m_len + 1);
1040         }
1041
1042         if (is_dynamic())
1043         {
1044             vogl_delete_array(m_dyn.m_pStr);
1045         }
1046
1047         set_dyn_string_ptr(p);
1048         m_dyn.m_buf_size = static_cast<uint32>(math::minimum<uint64_t>(cMaxDynamicStringBufSize, vogl_msize_array(m_dyn.m_pStr)));
1049         VOGL_ASSERT(m_dyn.m_buf_size >= new_buf_size);
1050
1051         if (preserve_contents)
1052             check();
1053
1054         return get_buf_size() >= new_buf_size;
1055     }
1056
1057     void dynamic_string::swap(dynamic_string &other)
1058     {
1059         VOGL_ASSUME((sizeof(*this) & (sizeof(uint32) - 1)) == 0);
1060         uint32 *pA = reinterpret_cast<uint32 *>(this);
1061         uint32 *pB = reinterpret_cast<uint32 *>(&other);
1062
1063         uint num_uint32s = sizeof(*this) / sizeof(uint32);
1064         while (num_uint32s >= 4)
1065         {
1066             std::swap(reinterpret_cast<uint64_t *>(pA)[0], reinterpret_cast<uint64_t *>(pB)[0]);
1067             std::swap(reinterpret_cast<uint64_t *>(pA)[1], reinterpret_cast<uint64_t *>(pB)[1]);
1068             pA += 4;
1069             pB += 4;
1070             num_uint32s -= 4;
1071         }
1072
1073         while (num_uint32s >= 2)
1074         {
1075             std::swap(reinterpret_cast<uint64_t *>(pA)[0], reinterpret_cast<uint64_t *>(pB)[0]);
1076             pA += 2;
1077             pB += 2;
1078             num_uint32s -= 2;
1079         }
1080
1081         if (num_uint32s)
1082             std::swap(pA[0], pB[0]);
1083     }
1084
1085     int dynamic_string::serialize(void *pBuf, uint buf_size, bool little_endian) const
1086     {
1087         uint buf_left = buf_size;
1088
1089         VOGL_ASSUME(sizeof(m_len) == sizeof(uint32));
1090
1091         if (!utils::write_val(static_cast<uint32>(m_len), pBuf, buf_left, little_endian))
1092             return -1;
1093
1094         if (buf_left < m_len)
1095             return -1;
1096
1097         memcpy(pBuf, get_ptr_priv(), m_len);
1098
1099         buf_left -= m_len;
1100
1101         return buf_size - buf_left;
1102     }
1103
1104     int dynamic_string::deserialize(const void *pBuf, uint buf_size, bool little_endian)
1105     {
1106         uint buf_left = buf_size;
1107
1108         if (buf_left < sizeof(uint32))
1109             return -1;
1110
1111         uint32 len;
1112         if (!utils::read_obj(len, pBuf, buf_left, little_endian))
1113             return -1;
1114
1115         if (buf_left < len)
1116             return -1;
1117
1118         if (len > cMaxDynamicStringLen)
1119             return -1;
1120
1121         set_from_buf(pBuf, len);
1122
1123         buf_left -= len;
1124
1125         return buf_size - buf_left;
1126     }
1127
1128     void dynamic_string::translate_lf_to_crlf()
1129     {
1130         if (find_left(0x0A) < 0)
1131             return;
1132
1133         dynamic_string tmp;
1134         if (!tmp.ensure_buf(m_len + 2))
1135         {
1136             VOGL_ASSERT_ALWAYS;
1137             return;
1138         }
1139
1140         // normal sequence is 0x0D 0x0A (CR LF, \r\n)
1141
1142         int prev_char = -1;
1143         for (uint i = 0; i < get_len(); i++)
1144         {
1145             const int cur_char = (*this)[i];
1146
1147             if ((cur_char == 0x0A) && (prev_char != 0x0D))
1148                 tmp.append_char(0x0D);
1149
1150             tmp.append_char(cur_char);
1151
1152             prev_char = cur_char;
1153         }
1154
1155         swap(tmp);
1156     }
1157
1158     // simple strtok wrapper
1159     void dynamic_string::tokenize(const char *pDelims, dynamic_string_array &tokens, bool trim) const
1160     {
1161         if (!pDelims || !pDelims[0])
1162             return;
1163
1164         vogl::vector<char> tok_buf;
1165         tok_buf.append(get_ptr(), get_len());
1166
1167         tok_buf.push_back('\0');
1168
1169         char *pTok = strtok(tok_buf.get_ptr(), pDelims);
1170
1171         while (pTok)
1172         {
1173             dynamic_string tok(pTok);
1174             tokens.enlarge(1)->swap(tok);
1175
1176             pTok = strtok(NULL, pDelims);
1177         }
1178
1179         if (trim)
1180         {
1181             for (uint i = 0; i < tokens.size(); i++)
1182                 tokens[i].trim();
1183         }
1184     }
1185
1186     bool json_serialize(const dynamic_string &str, json_value &val)
1187     {
1188         val.set_value(str);
1189         return true;
1190     }
1191
1192     bool json_deserialize(dynamic_string &str, const json_value &val)
1193     {
1194         str = val.as_string();
1195         return true;
1196     }
1197
1198     bool dynamic_string_test()
1199     {
1200 #define CHECK(x) \
1201     if (!(x))    \
1202         return false;
1203
1204         {
1205             dynamic_string x;
1206             x = "This is a test";
1207             dynamic_string y(x);
1208             CHECK(x.compare(y, true) == 0);
1209             x.ensure_dynamic();
1210             CHECK(x.compare(y, true) == 0);
1211             x.optimize();
1212             CHECK(x.compare(y, true) == 0);
1213
1214             x.clear();
1215             x = y;
1216             CHECK(y.compare(x, true) == 0);
1217         }
1218
1219         {
1220             dynamic_string x("BlahCool");
1221             CHECK(x.contains("Cool"));
1222             CHECK(x.find_left("Cool") == 4);
1223             CHECK(x.find_left("hC") == 3);
1224             CHECK(x.find_right("o") == 6);
1225             CHECK(x.find_right("hC") == 3);
1226             CHECK(x.replace("Cool", "XXXX") == "BlahXXXX");
1227
1228             x = "BlahCool";
1229             CHECK(x.replace("Blah", "Z") == "ZCool");
1230
1231             x = "BlahCool";
1232             CHECK(x.replace("Blah", "BlahBlah") == "BlahBlahCool");
1233
1234             x = "ABCDEF";
1235
1236             x.tolower();
1237             CHECK(x.compare("abcdef") == 0);
1238             CHECK(x.compare("ABCDEF") == 0);
1239             CHECK(x.compare("abcdef", true) == 0);
1240             CHECK(x.compare("ABCDEF", true) > 0);
1241
1242             x.toupper();
1243             CHECK(x.compare("abcdef") == 0);
1244             CHECK(x.compare("ABCDEF") == 0);
1245             CHECK(x.compare("abcdef", true) < 0);
1246             CHECK(x.compare("ABCDEF", true) == 0);
1247         }
1248
1249         {
1250             dynamic_string x("Blah");
1251             x.append(x);
1252             CHECK(x == "BlahBlah");
1253             x.append(x.get_ptr());
1254             CHECK(x == "BlahBlahBlahBlah");
1255             x.append(x.get_ptr(), x.get_len());
1256             CHECK(x == "BlahBlahBlahBlahBlahBlahBlahBlah");
1257             x.append(x.get_ptr(), x.get_len());
1258             CHECK(x == "BlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlah");
1259             x.append(x.get_ptr(), x.get_len());
1260             CHECK(x == "BlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlah");
1261             x.append(x.get_ptr(), x.get_len());
1262             CHECK(x == "BlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlahBlah");
1263         }
1264
1265         {
1266             dynamic_string_array tokens;
1267             dynamic_string x("   This is,a, test   ");
1268             x.tokenize(" ,", tokens, true);
1269             CHECK(tokens.size() == 4);
1270             CHECK(tokens[0] == "This");
1271             CHECK(tokens[1] == "is");
1272             CHECK(tokens[2] == "a");
1273             CHECK(tokens[3] == "test");
1274         }
1275
1276         {
1277             const uint N = 10000;
1278             dynamic_string_array x(N);
1279             vogl::vector<uint64_t> y(N);
1280             for (uint i = 0; i < N; i++)
1281             {
1282                 uint64_t r = g_random.urand64();
1283                 x[i].format("%" PRIu64, r);
1284                 CHECK(string_to_uint64(x[i].get_ptr()) == r);
1285                 y[i] = r;
1286             }
1287
1288             for (uint i = 0; i < 100000; i++)
1289             {
1290                 uint k = g_random.irand(0, N);
1291                 uint l = g_random.irand(0, N);
1292                 CHECK(string_to_uint64(x[k].get_ptr()) == y[k]);
1293                 CHECK(string_to_uint64(x[l].get_ptr()) == y[l]);
1294
1295                 std::swap(x[k], x[l]);
1296                 std::swap(y[k], y[l]);
1297
1298                 CHECK(string_to_uint64(x[k].get_ptr()) == y[k]);
1299                 CHECK(string_to_uint64(x[l].get_ptr()) == y[l]);
1300             }
1301
1302             for (uint i = 0; i < N; i++)
1303                 x[i].ensure_dynamic();
1304             for (uint i = 0; i < N; i++)
1305                 CHECK(string_to_uint64(x[i].get_ptr()) == y[i]);
1306
1307             for (uint i = 0; i < N; i++)
1308                 x[i].optimize();
1309             for (uint i = 0; i < N; i++)
1310                 CHECK(string_to_uint64(x[i].get_ptr()) == y[i]);
1311
1312             for (uint i = 0; i < N; i++)
1313             {
1314                 dynamic_string temp(x[i]);
1315                 x[i] = temp;
1316             }
1317             for (uint i = 0; i < N; i++)
1318                 CHECK(string_to_uint64(x[i].get_ptr()) == y[i]);
1319
1320             for (uint i = 0; i < N; i++)
1321             {
1322                 dynamic_string temp(x[i]);
1323                 x[i] = temp.get_ptr();
1324             }
1325             for (uint i = 0; i < N; i++)
1326                 CHECK(string_to_uint64(x[i].get_ptr()) == y[i]);
1327         }
1328
1329         {
1330             dynamic_string x;
1331             vogl::vector<char> y;
1332             for (uint t = 0; t < 10000000; t++)
1333             {
1334                 if (g_random.irand(0, 1000) == 0)
1335                 {
1336                     x.clear();
1337                     y.clear();
1338                 }
1339
1340                 if (g_random.irand(0, 100) == 0)
1341                     x.optimize();
1342                 else if (g_random.irand(0, 100) == 0)
1343                     x.ensure_dynamic();
1344                 else if (g_random.irand(0, 10000) == 0)
1345                     x = dynamic_string(x);
1346                 else if (g_random.irand(0, 4000) == 0)
1347                 {
1348                     dynamic_string xx;
1349                     xx.set(x.get_ptr());
1350                     xx.swap(x);
1351                 }
1352                 else if (g_random.irand(0, 4000) == 0)
1353                 {
1354                     dynamic_string xx;
1355                     xx.set(x.get_ptr());
1356                     x = xx;
1357                 }
1358                 else if (g_random.irand(0, 4000) == 0)
1359                 {
1360                     x.truncate(0);
1361                     uint pos = 0;
1362                     uint left = y.size();
1363                     while (left)
1364                     {
1365                         uint n = g_random.irand_inclusive(1, left);
1366
1367                         if ((n == 1) && (g_random.get_bit()))
1368                             x.append_char(y[pos]);
1369                         else
1370                         {
1371                             vogl::vector<char> z;
1372                             z.append(&y[pos], n);
1373                             z.push_back(0);
1374                             x.append(z.get_ptr());
1375                         }
1376
1377                         pos += n;
1378                         left -= n;
1379                     }
1380                 }
1381
1382                 if (g_random.irand(0, 1000) == 0)
1383                 {
1384                     x.append(x);
1385                     y.append(vogl::vector<char>(y));
1386                 }
1387
1388                 if (x.get_len() > 1000000)
1389                 {
1390                     x.truncate(x.get_len() / 2);
1391                     y.resize(y.size() / 2);
1392                 }
1393
1394                 switch (g_random.irand(0, 6))
1395                 {
1396                     case 0:
1397                     {
1398                         uint n = (uint)fabs(g_random.gaussian(10, 10));
1399                         for (uint i = 0; i < n; i++)
1400                         {
1401                             int c = g_random.irand_inclusive(1, 255);
1402                             x.append_char(c);
1403                             y.push_back(c);
1404                         }
1405                         break;
1406                     }
1407                     case 1:
1408                     {
1409                         if (x.get_len())
1410                         {
1411                             uint p = g_random.irand(0, x.get_len());
1412                             uint n = (uint)fabs(g_random.gaussian(10, 10));
1413                             n = math::minimum(n, x.get_len() - p);
1414
1415                             if (g_random.get_bit())
1416                                 x = dynamic_string(x).left(p) + dynamic_string(x).right(p + n);
1417                             else
1418                                 x.remove(p, n);
1419
1420                             y.erase(p, n);
1421                         }
1422
1423                         break;
1424                     }
1425                     case 2:
1426                     {
1427                         int c = g_random.irand_inclusive(1, 255);
1428                         uint p = g_random.irand_inclusive(0, x.get_len());
1429
1430                         x = dynamic_string(x).left(p) + dynamic_string(cVarArg, "%c", c) + dynamic_string(x).right(p);
1431                         y.insert(p, c);
1432
1433                         break;
1434                     }
1435                     case 3:
1436                     {
1437                         if (x.size())
1438                         {
1439                             if (g_random.get_bit())
1440                                 x.shorten(1);
1441                             else
1442                                 x.truncate(x.get_len() - 1);
1443                             y.pop_back();
1444                         }
1445                         break;
1446                     }
1447                     case 4:
1448                     {
1449                         char buf[3] = {(char)g_random.irand_inclusive(1, 255), g_random.get_bit() ? (char)g_random.irand(1, 255) : (char)0, 0 };
1450                         uint l = vogl_strlen(buf);
1451
1452                         int p0 = x.find_left(buf, true);
1453
1454                         int p1 = -1;
1455                         for (int i = 0; i <= (int)y.size() - (int)l; i++)
1456                         {
1457                             if (strncmp(&y[i], buf, l) == 0)
1458                             {
1459                                 p1 = i;
1460                                 break;
1461                             }
1462                         }
1463
1464                         CHECK(p0 == p1);
1465
1466                         break;
1467                     }
1468                     case 5:
1469                     {
1470                         char buf[3] = {(char)g_random.irand_inclusive(1, 255), g_random.get_bit() ? (char)g_random.irand(1, 255) : (char)0, 0 };
1471                         uint l = vogl_strlen(buf);
1472
1473                         int p0 = x.find_right(buf, true);
1474
1475                         int p1 = -1;
1476                         for (int i = (int)y.size() - (int)l; i >= 0; --i)
1477                         {
1478                             if (strncmp(&y[i], buf, l) == 0)
1479                             {
1480                                 p1 = i;
1481                                 break;
1482                             }
1483                         }
1484
1485                         CHECK(p0 == p1);
1486
1487                         break;
1488                     }
1489                     default:
1490                         VOGL_ASSERT_ALWAYS;
1491                         break;
1492                 }
1493
1494                 CHECK(x.get_len() == y.size());
1495                 for (uint i = 0; i < x.get_len(); i++)
1496                 {
1497                     CHECK(x[i] == y[i]);
1498                 }
1499
1500                 printf("%u: %u\n", t, x.size());
1501             }
1502         }
1503
1504         {
1505             const uint N = 5;
1506             for (uint t = 0; t < N; t++)
1507             {
1508                 uint i = g_random.irand_inclusive(0, cMaxDynamicStringLen);
1509                 printf("Size: %u\n", i);
1510
1511                 dynamic_string k;
1512
1513                 int fill_char = g_random.irand_inclusive(1, 255);
1514                 CHECK(k.set_len(i, (char)fill_char));
1515
1516                 CHECK(k.validate());
1517
1518                 k.ensure_dynamic();
1519
1520                 CHECK(k.validate());
1521
1522                 k.optimize();
1523
1524                 CHECK(k.validate());
1525
1526                 CHECK(k.get_len() == i);
1527
1528                 for (uint i = 0; i < k.get_len(); i++)
1529                     CHECK(k[i] == (char)fill_char);
1530
1531                 //if ((t & 4095) == 4095)
1532                 printf("%3.3f%%\n", t * 100.0f / N);
1533             }
1534         }
1535
1536         for (uint i = 0; i < 1000000; i++)
1537         {
1538             uint k0 = g_random.irand(0, 20000);
1539             uint k1 = g_random.irand(0, 20000);
1540
1541             dynamic_string a0(cVarArg, "%u", k0);
1542             dynamic_string a1(cVarArg, "%u", k1);
1543
1544             vogl::vector<char> b0;
1545             b0.append(a0.get_ptr(), a0.get_len());
1546
1547             vogl::vector<char> b1;
1548             b1.append(a1.get_ptr(), a1.get_len());
1549
1550             bool c0 = b0 < b1;
1551             bool c1 = b0 <= b1;
1552             bool c2 = b0 > b1;
1553             bool c3 = b0 >= b1;
1554             bool c4 = b0 == b1;
1555             bool c5 = b0 != b1;
1556
1557             if ((a0 < a1) != c0)
1558                 return false;
1559             if ((a0 <= a1) != c1)
1560                 return false;
1561             if ((a0 > a1) != c2)
1562                 return false;
1563             if ((a0 >= a1) != c3)
1564                 return false;
1565             if ((a0 == a1) != c4)
1566                 return false;
1567             if ((a0 != a1) != c5)
1568                 return false;
1569         }
1570
1571         return true;
1572     }
1573
1574 } // namespace vogl