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