]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_fixed_string.h
Initial vogl checkin
[vogl] / src / voglcore / vogl_fixed_string.h
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: fixed_string.h
28 // Copyright (C) 2008-2009 Tenacious Software LLC, All rights reserved.
29 #pragma once
30
31 #include "vogl_core.h"
32 #include "vogl_fixed_array.h"
33 #include "vogl_hash.h"
34 #include "vogl_strutils.h"
35
36 namespace vogl
37 {
38     // Simple hybrid C/Pascal style fixed string class, limited to a max buffer size of 65535 chars.
39     // Contains 16-bit length at beginning, followed by the zero terminated C-style string.
40     //
41     // TODO: Probably should delete this guy. This class is not nearly used as much as dynamic_string,
42     // which has the small string optimization so the value add here isn't high. This class
43     // never touches the heap, though, which could be useful.
44     // TODO: Create unit tests, this class has been barely used.
45     //
46     // Important note: For historical reasons the comparisons operators in this class are
47     // case insensitive (using locale independent helper functions) by default, mostly because
48     // the last X engines I've worked on used this default and I got used to it. Its the day after
49     // and now I'm regretting this idea.
50     template <uint BufSize>
51     class fixed_string
52     {
53     public:
54         enum
55         {
56             cBufSize = BufSize,
57             cMaxLen = cBufSize - 1,
58         };
59
60         inline fixed_string()
61             : m_len(0)
62         {
63             VOGL_ASSUME(BufSize < static_cast<uint>(cINT32_MAX));
64             m_buf[0] = 0;
65         }
66
67         inline fixed_string(eVarArg dummy, const char *p, ...) VOGL_ATTRIBUTE_PRINTF(3, 4)
68         {
69             VOGL_NOTE_UNUSED(dummy);
70             VOGL_ASSUME(BufSize < static_cast<uint>(cINT32_MAX));
71
72             va_list args;
73             va_start(args, p);
74             format_args(p, args);
75             va_end(args);
76         }
77
78         inline fixed_string(const char *p)
79         {
80             VOGL_ASSUME(BufSize < static_cast<uint>(cINT32_MAX));
81
82             set(p);
83         }
84
85         template <uint OtherBufSize>
86         explicit inline fixed_string(const fixed_string<OtherBufSize> &other)
87             : m_len(other.get_len())
88         {
89             memcpy(m_buf.get_ptr(), other.get_ptr(), other.get_len() + 1);
90         }
91
92         inline fixed_string &clear(bool full = false)
93         {
94             if (full)
95                 m_buf.bitwise_zero();
96             else
97                 m_buf[0] = 0;
98
99             m_len = 0;
100
101             return *this;
102         }
103
104         inline uint get_buf_size() const
105         {
106             return cBufSize;
107         }
108
109         inline uint get_max_len() const
110         {
111             return cMaxLen;
112         }
113
114         inline uint get_len() const
115         {
116             return m_len;
117         }
118
119         inline uint size() const
120         {
121             return m_len;
122         }
123
124         // Truncates the string to 0 chars, but does not free the buffer.
125         inline void empty()
126         {
127             return clear(false);
128         }
129
130         inline bool is_empty() const
131         {
132             return !m_len;
133         }
134
135         inline bool has_content() const
136         {
137             return m_len != 0;
138         }
139
140         inline const char *get_ptr() const
141         {
142             return m_buf.get_ptr();
143         }
144
145         inline char front() const
146         {
147             return m_len ? get_ptr()[0] : '\0';
148         }
149         inline char back() const
150         {
151             return m_len ? get_ptr()[m_len - 1] : '\0';
152         }
153
154         inline char operator[](uint i) const
155         {
156             return m_buf[i];
157         }
158
159         // Index string, beginning from back, 0 = last character, 1 = next to last char, etc.
160         inline char get_char_at_end(uint i) const
161         {
162             VOGL_ASSERT(i <= m_len);
163             return (i < m_len) ? get_ptr()[static_cast<int>(m_len) - 1 - static_cast<int>(i)] : '\0';
164         }
165
166         inline string_hash get_hash() const
167         {
168             return string_hash(get_ptr(), m_len);
169         }
170
171         inline fixed_string get_clone() const
172         {
173             return fixed_string(*this);
174         }
175
176         // IMPORTANT: These comparison methods are all CASE INSENSITIVE by default!
177         inline int compare(const fixed_string &rhs, bool case_sensitive = false) const
178         {
179             return compare(rhs.get_ptr(), case_sensitive);
180         }
181
182         inline int compare(const char *p, bool case_sensitive = false) const
183         {
184             VOGL_ASSERT(p);
185
186             uint l_len = m_len;
187
188             int result = 0;
189             if (case_sensitive)
190                 result = strncmp(get_ptr(), p, l_len);
191             else
192                 result = vogl_strnicmp(get_ptr(), p, l_len);
193
194             return (result < 0) ? -1 : ((result > 0) ? 1 : 0);
195         }
196
197         // IMPORTANT: These comparison methods are all CASE INSENSITIVE by default!
198         inline bool operator==(const fixed_string &rhs) const
199         {
200             return compare(rhs) == 0;
201         }
202         inline bool operator==(const char *p) const
203         {
204             return compare(p) == 0;
205         }
206
207         inline bool operator<(const fixed_string &rhs) const
208         {
209             return compare(rhs) < 0;
210         }
211         inline bool operator<(const char *p) const
212         {
213             return compare(p) < 0;
214         }
215
216         inline bool operator>(const fixed_string &rhs) const
217         {
218             return compare(rhs) > 0;
219         }
220         inline bool operator>(const char *p) const
221         {
222             return compare(p) > 0;
223         }
224
225         inline bool operator<=(const fixed_string &rhs) const
226         {
227             return compare(rhs) <= 0;
228         }
229         inline bool operator<=(const char *p) const
230         {
231             return compare(p) <= 0;
232         }
233
234         inline bool operator>=(const fixed_string &rhs) const
235         {
236             return compare(rhs) >= 0;
237         }
238         inline bool operator>=(const char *p) const
239         {
240             return compare(p) >= 0;
241         }
242
243         friend inline bool operator==(const char *p, const fixed_string &rhs)
244         {
245             return rhs.compare(p) == 0;
246         }
247
248         inline bool set(const char *p, uint max_len = UINT_MAX)
249         {
250             VOGL_ASSERT(p);
251
252             uint len = vogl_strlen(p);
253
254             const uint copy_len = math::minimum<uint>(max_len, len, cMaxLen);
255
256             memmove(m_buf.get_ptr(), p, copy_len * sizeof(char));
257
258             m_buf[len] = 0;
259
260             m_len = len;
261
262             check();
263
264             return copy_len == len;
265         }
266
267         template <uint OtherBufSize>
268         inline fixed_string &operator=(const fixed_string<OtherBufSize> &other)
269         {
270             if ((void *)this != (void *)&other)
271             {
272                 memcpy(m_buf.get_ptr(), other.get_ptr(), other.get_len() + 1);
273                 m_len = other.get_len();
274
275                 check();
276             }
277
278             return *this;
279         }
280
281         inline fixed_string &operator=(const char *p)
282         {
283             set(p);
284             return *this;
285         }
286
287         inline fixed_string &set_char(uint index, char c)
288         {
289             VOGL_ASSERT(index <= m_len);
290             VOGL_ASSERT(c != 0);
291             if (!c)
292                 return *this;
293
294             if (index <= m_len)
295             {
296                 m_buf[index] = c;
297                 if (index == m_len)
298                 {
299                     m_buf[index + 1] = 0;
300                     m_len = math::minimum<uint>(cMaxLen, m_len + 1);
301                 }
302
303                 check();
304             }
305
306             return *this;
307         }
308
309         inline fixed_string &append_char(char c)
310         {
311             return set_char(m_len, c);
312         }
313
314         inline fixed_string &truncate(uint new_len)
315         {
316             VOGL_ASSERT(new_len <= m_len);
317             if (new_len < m_len)
318             {
319                 m_buf[new_len] = 0;
320                 m_len = new_len;
321             }
322
323             return *this;
324         }
325
326         inline fixed_string &tolower()
327         {
328             vogl_strlwr(m_buf);
329             return *this;
330         }
331
332         inline fixed_string &toupper()
333         {
334             vogl_strupr(m_buf);
335             return *this;
336         }
337
338         inline fixed_string &append(const char *p)
339         {
340             uint p_len = math::minimum<uint>(cMaxLen - m_len, vogl_strlen(p));
341
342             if (p_len)
343             {
344                 memmove(m_buf.get_ptr() + m_len, p, p_len);
345
346                 m_len = m_len + p_len;
347
348                 m_buf[m_len] = 0;
349
350                 check();
351             }
352
353             return *this;
354         }
355
356         template <uint OtherBufSize>
357         inline fixed_string &append(const fixed_string<OtherBufSize> &b)
358         {
359             uint b_len = math::minimum<uint>(cMaxLen - m_len, b.get_len());
360
361             if (b_len)
362             {
363                 memmove(m_buf.get_ptr() + m_len, b.get_ptr(), b_len);
364
365                 m_len = m_len + b_len;
366
367                 m_buf[m_len] = 0;
368
369                 check();
370             }
371
372             return *this;
373         }
374
375         inline fixed_string &operator+=(const char *p)
376         {
377             return append(p);
378         }
379
380         template <uint OtherBufSize>
381         inline fixed_string &operator+=(const fixed_string<OtherBufSize> &b)
382         {
383             return append(b);
384         }
385
386         friend inline fixed_string operator+(const fixed_string &a, const fixed_string &b)
387         {
388             return fixed_string(a).append(b);
389         }
390
391         inline fixed_string &format_args(const char *p, va_list args)
392         {
393             int l = vogl_vsprintf_s(m_buf.get_ptr(), BufSize, p, args);
394
395             if (l < 0)
396                 truncate(0);
397             else
398                 m_len = l;
399
400             check();
401
402             return *this;
403         }
404
405         inline fixed_string &format(const char *p, ...) VOGL_ATTRIBUTE_PRINTF(2, 3)
406         {
407             va_list args;
408             va_start(args, p);
409             format_args(p, args);
410             va_end(args);
411             return *this;
412         }
413
414         // Note many of these ops are IN-PLACE string operations to avoid constructing new objects, which can make them inconvienent.
415         inline fixed_string &crop(uint start, uint len)
416         {
417             if (start >= m_len)
418             {
419                 clear();
420                 return *this;
421             }
422
423             len = math::minimum<uint>(len, m_len - start);
424
425             if (start)
426                 memmove(m_buf.get_ptr(), m_buf.get_ptr() + start, len);
427
428             m_buf[len] = 0;
429
430             m_len = len;
431
432             check();
433
434             return *this;
435         }
436
437         // half-open interval
438         inline fixed_string &substring(uint start, uint end)
439         {
440             VOGL_ASSERT(start <= end);
441             if (start > end)
442                 return *this;
443             return crop(start, end - start);
444         }
445
446         inline fixed_string &left(uint len)
447         {
448             return substring(0, len);
449         }
450
451         inline fixed_string &mid(uint start, uint len)
452         {
453             return crop(start, len);
454         }
455
456         inline fixed_string &right(uint start)
457         {
458             return substring(start, get_len());
459         }
460
461         inline int find_left(const char *p, bool case_sensitive = false) const
462         {
463             VOGL_ASSERT(p);
464
465             const int p_len = vogl_strlen(p);
466
467             for (int i = 0; i <= (static_cast<int>(m_len) - p_len); i++)
468                 if ((case_sensitive ? strncmp : vogl_strnicmp)(p, &m_buf[i], p_len) == 0)
469                     return i;
470
471             return -1;
472         }
473
474         inline int find_left(char c) const
475         {
476             for (uint i = 0; i < m_len; i++)
477                 if (m_buf[i] == c)
478                     return i;
479             return -1;
480         }
481
482         inline int find_right(char c) const
483         {
484             for (int i = (int)m_len - 1; i >= 0; i--)
485                 if (m_buf[i] == c)
486                     return i;
487             return -1;
488         }
489
490         inline int find_right(const char *p, bool case_sensitive = false) const
491         {
492             VOGL_ASSERT(p);
493             const int p_len = vogl_strlen(p);
494
495             for (int i = m_len - p_len; i >= 0; i--)
496                 if ((case_sensitive ? strncmp : vogl_strnicmp)(p, &m_buf[i], p_len) == 0)
497                     return i;
498
499             return -1;
500         }
501
502         inline fixed_string &trim()
503         {
504             int s, e;
505             for (s = 0; s < (int)m_len; s++)
506                 if (!vogl_isspace(m_buf[s]))
507                     break;
508
509             for (e = m_len - 1; e > s; e--)
510                 if (!vogl_isspace(m_buf[e]))
511                     break;
512
513             crop(s, e - s + 1);
514             return *this;
515         }
516
517     private:
518         uint m_len;
519         fixed_array<char, BufSize> m_buf;
520
521         inline void check() const
522         {
523             VOGL_ASSERT(m_len <= cMaxLen);
524             VOGL_ASSERT(vogl_strlen(m_buf.get_ptr()) == m_len);
525         }
526     };
527
528     typedef fixed_string<32> fixed_string32;
529     typedef fixed_string<64> fixed_string64;
530     typedef fixed_string<128> fixed_string128;
531     typedef fixed_string<256> fixed_string256;
532
533     template <uint N>
534     struct bitwise_movable<fixed_string<N> >
535     {
536         enum
537         {
538             cFlag = true
539         };
540     };
541
542     template <uint N>
543     struct hasher<fixed_string<N> >
544     {
545         inline size_t operator()(const fixed_string<N> &key) const
546         {
547             return key.get_hash();
548         }
549     };
550
551 } // namespace vogl