]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/string/string.h
various: reindent classes
[xonotic/netradiant.git] / libs / string / string.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #if !defined( INCLUDED_STRING_STRING_H )
23 #define INCLUDED_STRING_STRING_H
24
25 #include "globaldefs.h"
26
27 /// \file
28 /// C-style null-terminated-character-array string library.
29
30 #include <cstring>
31 #include <cctype>
32 #include <algorithm>
33
34 #include "memory/allocator.h"
35 #include "generic/arrayrange.h"
36
37 /// \brief Returns true if \p string length is zero.
38 /// O(1)
39 inline bool string_empty( const char* string ){
40         return *string == '\0';
41 }
42
43 /// \brief Returns true if \p string length is not zero.
44 /// O(1)
45 inline bool string_not_empty( const char* string ){
46         return !string_empty( string );
47 }
48
49 /// \brief Returns <0 if \p string is lexicographically less than \p other.
50 /// Returns >0 if \p string is lexicographically greater than \p other.
51 /// Returns 0 if \p string is lexicographically equal to \p other.
52 /// O(n)
53 inline int string_compare( const char* string, const char* other ){
54         return std::strcmp( string, other );
55 }
56
57 /// \brief Returns true if \p string is lexicographically equal to \p other.
58 /// O(n)
59 inline bool string_equal( const char* string, const char* other ){
60         return string_compare( string, other ) == 0;
61 }
62
63 /// \brief Returns true if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n).
64 /// O(n)
65 inline bool string_equal_n( const char* string, const char* other, std::size_t n ){
66         return std::strncmp( string, other, n ) == 0;
67 }
68
69 /// \brief Returns true if \p string is lexicographically less than \p other.
70 /// O(n)
71 inline bool string_less( const char* string, const char* other ){
72         return string_compare( string, other ) < 0;
73 }
74
75 /// \brief Returns true if \p string is lexicographically greater than \p other.
76 /// O(n)
77 inline bool string_greater( const char* string, const char* other ){
78         return string_compare( string, other ) > 0;
79 }
80
81 /// \brief Returns <0 if \p string is lexicographically less than \p other after converting both to lower-case.
82 /// Returns >0 if \p string is lexicographically greater than \p other after converting both to lower-case.
83 /// Returns 0 if \p string is lexicographically equal to \p other after converting both to lower-case.
84 /// O(n)
85 inline int string_compare_nocase( const char* string, const char* other ){
86 #if GDEF_OS_WINDOWS
87         return _stricmp( string, other );
88 #else
89         return strcasecmp( string, other );
90 #endif
91 }
92
93 /// \brief Returns <0 if [\p string, \p string + \p n) is lexicographically less than [\p other, \p other + \p n).
94 /// Returns >0 if [\p string, \p string + \p n) is lexicographically greater than [\p other, \p other + \p n).
95 /// Returns 0 if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n).
96 /// Treats all ascii characters as lower-case during comparisons.
97 /// O(n)
98 inline int string_compare_nocase_n( const char* string, const char* other, std::size_t n ){
99 #if GDEF_OS_WINDOWS
100         return _strnicmp( string, other, n );
101 #else
102         return strncasecmp( string, other, n );
103 #endif
104 }
105
106 /// \brief Returns true if \p string is lexicographically equal to \p other.
107 /// Treats all ascii characters as lower-case during comparisons.
108 /// O(n)
109 inline bool string_equal_nocase( const char* string, const char* other ){
110         return string_compare_nocase( string, other ) == 0;
111 }
112
113 /// \brief Returns true if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n).
114 /// Treats all ascii characters as lower-case during comparisons.
115 /// O(n)
116 inline bool string_equal_nocase_n( const char* string, const char* other, std::size_t n ){
117         return string_compare_nocase_n( string, other, n ) == 0;
118 }
119
120 /// \brief Returns true if \p string is lexicographically less than \p other.
121 /// Treats all ascii characters as lower-case during comparisons.
122 /// O(n)
123 inline bool string_less_nocase( const char* string, const char* other ){
124         return string_compare_nocase( string, other ) < 0;
125 }
126
127 /// \brief Returns true if \p string is lexicographically greater than \p other.
128 /// Treats all ascii characters as lower-case during comparisons.
129 /// O(n)
130 inline bool string_greater_nocase( const char* string, const char* other ){
131         return string_compare_nocase( string, other ) > 0;
132 }
133
134 /// \brief Returns the number of non-null characters in \p string.
135 /// O(n)
136 inline std::size_t string_length( const char* string ){
137         return std::strlen( string );
138 }
139
140 /// \brief Returns true if the beginning of \p string is equal to \p prefix.
141 /// O(n)
142 inline bool string_equal_prefix( const char* string, const char* prefix ){
143         return string_equal_n( string, prefix, string_length( prefix ) );
144 }
145
146 /// \brief Returns true if the ending of \p string is equal to \p suffix.
147 /// O(n)
148 inline bool string_equal_suffix( const char* string, const char* suffix){
149         const char *s = string + string_length( string ) - string_length( suffix );
150         return string_equal_n( s , suffix, string_length( suffix ) );
151 }
152
153 /// \brief Copies \p other into \p string and returns \p string.
154 /// Assumes that the space allocated for \p string is at least string_length(other) + 1.
155 /// O(n)
156 inline char* string_copy( char* string, const char* other ){
157         return std::strcpy( string, other );
158 }
159
160 /// \brief Allocates a string buffer large enough to hold \p length characters, using \p allocator.
161 /// The returned buffer must be released with \c string_release using a matching \p allocator.
162 template<typename Allocator>
163 inline char* string_new( std::size_t length, Allocator& allocator ){
164         return allocator.allocate( length + 1 );
165 }
166
167 /// \brief Deallocates the \p buffer large enough to hold \p length characters, using \p allocator.
168 template<typename Allocator>
169 inline void string_release( char* buffer, std::size_t length, Allocator& allocator ){
170         allocator.deallocate( buffer, length + 1 );
171 }
172
173 /// \brief Returns a newly-allocated string which is a clone of \p other, using \p allocator.
174 /// The returned buffer must be released with \c string_release using a matching \p allocator.
175 template<typename Allocator>
176 inline char* string_clone( const char* other, Allocator& allocator ){
177         char* copied = string_new( string_length( other ), allocator );
178         std::strcpy( copied, other );
179         return copied;
180 }
181
182 /// \brief Returns a newly-allocated string which is a clone of [\p first, \p last), using \p allocator.
183 /// The returned buffer must be released with \c string_release using a matching \p allocator.
184 template<typename Allocator>
185 inline char* string_clone_range( StringRange range, Allocator& allocator ){
186         std::size_t length = range.last - range.first;
187         char* copied = strncpy( string_new( length, allocator ), range.first, length );
188         copied[length] = '\0';
189         return copied;
190 }
191
192 /// \brief Allocates a string buffer large enough to hold \p length characters.
193 /// The returned buffer must be released with \c string_release.
194 inline char* string_new( std::size_t length ){
195         DefaultAllocator<char> allocator;
196         return string_new( length, allocator );
197 }
198
199 /// \brief Allocates a new buffer large enough to hold two concatenated strings and fills it with strings.
200 inline char* string_new_concat( const char* a, const char* b ){
201         char* str = string_new( string_length( a ) + string_length( b ) );
202         strcpy( str, a );
203         strcat( str, b );
204         return str;
205 }
206
207 /// \brief Deallocates the \p buffer large enough to hold \p length characters.
208 inline void string_release( char* string, std::size_t length ){
209         DefaultAllocator<char> allocator;
210         string_release( string, length, allocator );
211 }
212
213 /// \brief Returns a newly-allocated string which is a clone of \p other.
214 /// The returned buffer must be released with \c string_release.
215 inline char* string_clone( const char* other ){
216         DefaultAllocator<char> allocator;
217         return string_clone( other, allocator );
218 }
219
220 /// \brief Returns a newly-allocated string which is a clone of [\p first, \p last).
221 /// The returned buffer must be released with \c string_release.
222 inline char* string_clone_range( StringRange range ){
223         DefaultAllocator<char> allocator;
224         return string_clone_range( range, allocator );
225 }
226
227 typedef char* char_pointer;
228 /// \brief Swaps the values of \p string and \p other.
229 inline void string_swap( char_pointer& string, char_pointer& other ){
230         std::swap( string, other );
231 }
232
233 typedef const char* char_const_pointer;
234 /// \brief Swaps the values of \p string and \p other.
235 inline void string_swap( char_const_pointer& string, char_const_pointer& other ){
236         std::swap( string, other );
237 }
238
239 /// \brief Converts each character of \p string to lower-case and returns \p string.
240 /// O(n)
241 inline char* string_to_lowercase( char* string ){
242         for ( char* p = string; *p != '\0'; ++p )
243         {
244                 *p = (char)std::tolower( *p );
245         }
246         return string;
247 }
248
249 /// \brief Converts each character of \p string to upper-case and returns \p string.
250 /// O(n)
251 inline char* string_to_uppercase( char* string ){
252         for ( char* p = string; *p != '\0'; ++p )
253         {
254                 *p = (char)std::toupper( *p );
255         }
256         return string;
257 }
258
259 /// \brief A re-entrant string tokeniser similar to strchr.
260 class StringTokeniser
261 {
262 private:
263         bool istoken( char c ) const {
264                 if ( strchr( m_delimiters, c ) != 0 ) {
265                         return false;
266                 }
267                 return true;
268         }
269         const char* advance(){
270                 const char* token = m_pos;
271                 bool intoken = true;
272                 while ( !string_empty( m_pos ) )
273                 {
274                         if ( !istoken( *m_pos ) ) {
275                                 *m_pos = '\0';
276                                 intoken = false;
277                         }
278                         else if ( !intoken ) {
279                                 return token;
280                         }
281                         ++m_pos;
282                 }
283                 return token;
284         }
285         std::size_t m_length;
286         char* m_string;
287         char* m_pos;
288         const char* m_delimiters;
289
290 public:
291         StringTokeniser( const char* string, const char* delimiters = " \n\r\t\v" ) :
292                 m_length( string_length( string ) ),
293                 m_string( string_copy( string_new( m_length ), string ) ),
294                 m_pos( m_string ),
295                 m_delimiters( delimiters ){
296                 while ( !string_empty( m_pos ) && !istoken( *m_pos ) )
297                 {
298                         ++m_pos;
299                 }
300         }
301         ~StringTokeniser(){
302                 string_release( m_string, m_length );
303         }
304         /// \brief Returns the next token or "" if there are no more tokens available.
305         const char* getToken(){
306                 return advance();
307         }
308 };
309
310 /// \brief A non-mutable c-style string.
311 ///
312 /// \param Buffer The string storage implementation. Must be DefaultConstructible, CopyConstructible and Assignable. Must implement:
313 /// \li Buffer(const char* string) - constructor which copies a c-style \p string.
314 /// \li Buffer(const char* first, const char*) - constructor which copies a c-style string range [\p first, \p last).
315 /// \li void swap(Buffer& other) - swaps contents with \p other.
316 /// \li const char* c_str() - returns the stored non-mutable c-style string.
317 template<typename Buffer>
318 class String : public Buffer
319 {
320 public:
321         String()
322                 : Buffer(){
323         }
324         String( const char* string )
325                 : Buffer( string ){
326         }
327         String( StringRange range )
328                 : Buffer( range ){
329         }
330
331         String& operator=( const String& other ){
332                 String temp( other );
333                 temp.swap( *this );
334                 return *this;
335         }
336         String& operator=( const char* string ){
337                 String temp( string );
338                 temp.swap( *this );
339                 return *this;
340         }
341         String& operator=( StringRange range ){
342                 String temp( range );
343                 temp.swap( *this );
344                 return *this;
345         }
346
347         void swap( String& other ){
348                 Buffer::swap( other );
349         }
350
351         bool empty() const {
352                 return string_empty( Buffer::c_str() );
353         }
354 };
355
356 template<typename Buffer>
357 inline bool operator<( const String<Buffer>& self, const String<Buffer>& other ){
358         return string_less( self.c_str(), other.c_str() );
359 }
360
361 template<typename Buffer>
362 inline bool operator>( const String<Buffer>& self, const String<Buffer>& other ){
363         return string_greater( self.c_str(), other.c_str() );
364 }
365
366 template<typename Buffer>
367 inline bool operator==( const String<Buffer>& self, const String<Buffer>& other ){
368         return string_equal( self.c_str(), other.c_str() );
369 }
370
371 template<typename Buffer>
372 inline bool operator!=( const String<Buffer>& self, const String<Buffer>& other ){
373         return !string_equal( self.c_str(), other.c_str() );
374 }
375
376 template<typename Buffer>
377 inline bool operator==( const String<Buffer>& self, const char* other ){
378         return string_equal( self.c_str(), other );
379 }
380
381 template<typename Buffer>
382 inline bool operator!=( const String<Buffer>& self, const char* other ){
383         return !string_equal( self.c_str(), other );
384 }
385
386 namespace std
387 {
388 /// \brief Swaps the values of \p self and \p other.
389 /// Overloads std::swap.
390 template<typename Buffer>
391 inline void swap( String<Buffer>& self, String<Buffer>& other ){
392         self.swap( other );
393 }
394 }
395
396
397 /// \brief A non-mutable string buffer which manages memory allocation.
398 template<typename Allocator>
399 class CopiedBuffer : private Allocator
400 {
401         char* m_string;
402
403         char* copy_range( StringRange range ){
404                 return string_clone_range( range, static_cast<Allocator&>( *this ) );
405         }
406         char* copy( const char* other ){
407                 return string_clone( other, static_cast<Allocator&>( *this ) );
408         }
409         void destroy( char* string ){
410                 string_release( string, string_length( string ), static_cast<Allocator&>( *this ) );
411         }
412
413 protected:
414         ~CopiedBuffer(){
415                 destroy( m_string );
416         }
417
418 public:
419         CopiedBuffer()
420                 : m_string( copy( "" ) ){
421         }
422         explicit CopiedBuffer( const Allocator& allocator )
423                 : Allocator( allocator ), m_string( copy( "" ) ){
424         }
425         CopiedBuffer( const CopiedBuffer& other )
426                 : Allocator( other ), m_string( copy( other.m_string ) ){
427         }
428         CopiedBuffer( const char* string, const Allocator& allocator = Allocator() )
429                 : Allocator( allocator ), m_string( copy( string ) ){
430         }
431         CopiedBuffer( StringRange range, const Allocator& allocator = Allocator() )
432                 : Allocator( allocator ), m_string( copy_range( range ) ){
433         }
434         const char* c_str() const {
435                 return m_string;
436         }
437         void swap( CopiedBuffer& other ){
438                 string_swap( m_string, other.m_string );
439         }
440 };
441
442 /// \brief A non-mutable string which uses copy-by-value for assignment.
443 typedef String< CopiedBuffer< DefaultAllocator<char> > > CopiedString;
444
445
446 /// \brief A non-mutable string buffer which uses reference-counting to avoid unnecessary allocations.
447 template<typename Allocator>
448 class SmartBuffer : private Allocator
449 {
450 private:
451         char* m_buffer;
452
453         char* copy_range( StringRange range ){
454                 char* buffer = Allocator::allocate( sizeof( std::size_t ) + ( range.last - range.first ) + 1 );
455                 strncpy( buffer + sizeof( std::size_t ), range.first, range.last - range.first );
456                 buffer[sizeof( std::size_t ) + ( range.last - range.first )] = '\0';
457                 *reinterpret_cast<std::size_t*>( buffer ) = 0;
458                 return buffer;
459         }
460         char* copy( const char* string ){
461                 char* buffer = Allocator::allocate( sizeof( std::size_t ) + string_length( string ) + 1 );
462                 strcpy( buffer + sizeof( std::size_t ), string );
463                 *reinterpret_cast<std::size_t*>( buffer ) = 0;
464                 return buffer;
465         }
466         void destroy( char* buffer ){
467                 Allocator::deallocate( buffer, sizeof( std::size_t ) + string_length( c_str() ) + 1 );
468         }
469
470         void incref( char* buffer ){
471                 ++( *reinterpret_cast<std::size_t*>( buffer ) );
472         }
473         void decref( char* buffer ){
474                 if ( --( *reinterpret_cast<std::size_t*>( buffer ) ) == 0 ) {
475                         destroy( buffer );
476                 }
477         }
478
479 protected:
480         ~SmartBuffer(){
481                 decref( m_buffer );
482         }
483
484 public:
485         SmartBuffer()
486                 : m_buffer( copy( "" ) ){
487                 incref( m_buffer );
488         }
489         explicit SmartBuffer( const Allocator& allocator )
490                 : Allocator( allocator ), m_buffer( copy( "" ) ){
491                 incref( m_buffer );
492         }
493         SmartBuffer( const SmartBuffer& other )
494                 : Allocator( other ), m_buffer( other.m_buffer ){
495                 incref( m_buffer );
496         }
497         SmartBuffer( const char* string, const Allocator& allocator = Allocator() )
498                 : Allocator( allocator ), m_buffer( copy( string ) ){
499                 incref( m_buffer );
500         }
501         SmartBuffer( StringRange range, const Allocator& allocator = Allocator() )
502                 : Allocator( allocator ), m_buffer( copy_range( range ) ){
503                 incref( m_buffer );
504         }
505         const char* c_str() const {
506                 return m_buffer + sizeof( std::size_t );
507         }
508         void swap( SmartBuffer& other ){
509                 string_swap( m_buffer, other.m_buffer );
510         }
511 };
512
513 /// \brief A non-mutable string which uses copy-by-reference for assignment of SmartString.
514 typedef String< SmartBuffer< DefaultAllocator<char> > > SmartString;
515
516 class StringEqualNoCase
517 {
518 public:
519         bool operator()( const CopiedString& key, const CopiedString& other ) const {
520                 return string_equal_nocase( key.c_str(), other.c_str() );
521         }
522 };
523
524 struct StringLessNoCase
525 {
526         bool operator()( const CopiedString& x, const CopiedString& y ) const {
527                 return string_less_nocase( x.c_str(), y.c_str() );
528         }
529 };
530
531 struct RawStringEqual
532 {
533         bool operator()( const char* x, const char* y ) const {
534                 return string_equal( x, y );
535         }
536 };
537
538 struct RawStringLess
539 {
540         bool operator()( const char* x, const char* y ) const {
541                 return string_less( x, y );
542         }
543 };
544
545 struct RawStringLessNoCase
546 {
547         bool operator()( const char* x, const char* y ) const {
548                 return string_less_nocase( x, y );
549         }
550 };
551
552 #endif