summaryrefslogtreecommitdiff
path: root/src/fstring.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/fstring.h')
-rw-r--r--src/fstring.h1304
1 files changed, 778 insertions, 526 deletions
diff --git a/src/fstring.h b/src/fstring.h
index c5397cc..1e1fc02 100644
--- a/src/fstring.h
+++ b/src/fstring.h
@@ -1,651 +1,903 @@
1#ifndef F_STRING_H 1#ifndef BU_F_STRING_H
2#define F_STRING_H 2#define BU_F_STRING_H
3 3
4#include <stdint.h> 4#include <stdint.h>
5#include <memory> 5#include <memory>
6#include "serializable.h" 6#include "bu/archival.h"
7#include "serializer.h" 7#include "bu/archive.h"
8#include "bu/hash.h"
8 9
9template< typename chr > 10#define min( a, b ) ((a<b)?(a):(b))
10struct FStringChunk 11
11{ 12namespace Bu
12 long nLength;
13 chr *pData;
14 FStringChunk *pNext;
15};
16
17/**
18 * Flexible String class. This class was designed with string passing and
19 * generation in mind. Like the standard string class you can specify what
20 * datatype to use for each character. Unlike the standard string class,
21 * collection of appended and prepended terms is done lazily, making long
22 * operations that involve many appends very inexpensive. In addition internal
23 * ref-counting means that if you pass strings around between functions there's
24 * almost no overhead in time or memory since a reference is created and no
25 * data is actually copied. This also means that you never need to put any
26 * FBasicString into a ref-counting container class.
27 */
28template< typename chr, typename chralloc=std::allocator<chr>, typename chunkalloc=std::allocator<struct FStringChunk<chr> > >
29class FBasicString : public Serializable
30{ 13{
14 template< typename chr >
15 struct FStringChunk
16 {
17 long nLength;
18 chr *pData;
19 FStringChunk *pNext;
20 };
21
22 /**
23 * Flexible String class. This class was designed with string passing and
24 * generation in mind. Like the standard string class you can specify what
25 * datatype to use for each character. Unlike the standard string class,
26 * collection of appended and prepended terms is done lazily, making long
27 * operations that involve many appends very inexpensive. In addition internal
28 * ref-counting means that if you pass strings around between functions there's
29 * almost no overhead in time or memory since a reference is created and no
30 * data is actually copied. This also means that you never need to put any
31 * FBasicString into a ref-counting container class.
32 *
33 *@param chr (typename) Type of character (i.e. char)
34 *@param nMinSize (int) Chunk size (default: 256)
35 *@param chralloc (typename) Memory Allocator for chr
36 *@param chunkalloc (typename) Memory Allocator for chr chunks
37 */
38 template< typename chr, int nMinSize=256, typename chralloc=std::allocator<chr>, typename chunkalloc=std::allocator<struct FStringChunk<chr> > >
39 class FBasicString : public Archival
40 {
31#ifndef VALTEST 41#ifndef VALTEST
32#define cpy( dest, src, size ) memcpy( dest, src, size*sizeof(chr) ) 42#define cpy( dest, src, size ) memcpy( dest, src, size*sizeof(chr) )
33#endif 43#endif
34private: 44 private:
35 typedef struct FStringChunk<chr> Chunk; 45 typedef struct FStringChunk<chr> Chunk;
36 typedef struct FBasicString<chr, chralloc, chunkalloc> MyType; 46 typedef struct FBasicString<chr, nMinSize, chralloc, chunkalloc> MyType;
37 47
38public: 48 public:
39 FBasicString() : 49 FBasicString() :
40 nLength( 0 ), 50 nLength( 0 ),
41 pnRefs( NULL ), 51 pnRefs( NULL ),
42 pFirst( NULL ), 52 pFirst( NULL ),
43 pLast( NULL ) 53 pLast( NULL )
44 { 54 {
45 } 55 }
46 56
47 FBasicString( const chr *pData ) : 57 FBasicString( const chr *pData ) :
48 nLength( 0 ), 58 nLength( 0 ),
49 pnRefs( NULL ), 59 pnRefs( NULL ),
50 pFirst( NULL ), 60 pFirst( NULL ),
51 pLast( NULL ) 61 pLast( NULL )
52 { 62 {
53 append( pData ); 63 append( pData );
54 } 64 }
55
56 FBasicString( const chr *pData, long nLength ) :
57 nLength( 0 ),
58 pnRefs( NULL ),
59 pFirst( NULL ),
60 pLast( NULL )
61 {
62 append( pData, nLength );
63 }
64
65 FBasicString( const MyType &rSrc ) :
66 nLength( 0 ),
67 pnRefs( NULL ),
68 pFirst( NULL ),
69 pLast( NULL )
70 {
71 // Here we have no choice but to copy, since the other guy is a const.
72 // In the case that the source were flat, we could get a reference, it
73 // would make some things faster, but not matter in many other cases.
74
75 joinShare( rSrc );
76 //copyFrom( rSrc );
77 }
78
79 FBasicString( const MyType &rSrc, long nLength ) :
80 nLength( 0 ),
81 pnRefs( NULL ),
82 pFirst( NULL ),
83 pLast( NULL )
84 {
85 append( rSrc.pFirst->pData, nLength );
86 }
87
88 FBasicString( const MyType &rSrc, long nStart, long nLength ) :
89 nLength( 0 ),
90 pnRefs( NULL ),
91 pFirst( NULL ),
92 pLast( NULL )
93 {
94 append( rSrc.pFirst->pData+nStart, nLength );
95 }
96
97 FBasicString( long nSize ) :
98 nLength( nSize ),
99 pnRefs( NULL ),
100 pFirst( NULL ),
101 pLast( NULL )
102 {
103 pFirst = pLast = newChunk( nSize );
104 }
105 65
106 virtual ~FBasicString() 66 FBasicString( const chr *pData, long nLength ) :
107 { 67 nLength( 0 ),
108 clear(); 68 pnRefs( NULL ),
109 } 69 pFirst( NULL ),
70 pLast( NULL )
71 {
72 append( pData, nLength );
73 }
110 74
111 void append( const chr *pData ) 75 FBasicString( const MyType &rSrc ) :
112 { 76 nLength( 0 ),
113 long nLen; 77 pnRefs( NULL ),
114 for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); 78 pFirst( NULL ),
115 79 pLast( NULL )
116 Chunk *pNew = newChunk( nLen ); 80 {
117 cpy( pNew->pData, pData, nLen ); 81 // Here we have no choice but to copy, since the other guy is a const.
82 // In the case that the source were flat, we could get a reference, it
83 // would make some things faster, but not matter in many other cases.
118 84
119 appendChunk( pNew ); 85 joinShare( rSrc );
120 } 86 //copyFrom( rSrc );
87 }
121 88
122 void append( const chr *pData, long nLen ) 89 FBasicString( const MyType &rSrc, long nLength ) :
123 { 90 nLength( 0 ),
124 Chunk *pNew = newChunk( nLen ); 91 pnRefs( NULL ),
92 pFirst( NULL ),
93 pLast( NULL )
94 {
95 append( rSrc.pFirst->pData, nLength );
96 }
125 97
126 cpy( pNew->pData, pData, nLen ); 98 FBasicString( const MyType &rSrc, long nStart, long nLength ) :
99 nLength( 0 ),
100 pnRefs( NULL ),
101 pFirst( NULL ),
102 pLast( NULL )
103 {
104 append( rSrc.pFirst->pData+nStart, nLength );
105 }
127 106
128 appendChunk( pNew ); 107 FBasicString( long nSize ) :
129 } 108 nLength( nSize ),
109 pnRefs( NULL ),
110 pFirst( NULL ),
111 pLast( NULL )
112 {
113 pFirst = pLast = newChunk( nSize );
114 }
130 115
131 void prepend( const chr *pData ) 116 virtual ~FBasicString()
132 { 117 {
133 long nLen; 118 clear();
134 for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); 119 }
135
136 Chunk *pNew = newChunk( nLen );
137 cpy( pNew->pData, pData, nLen );
138 120
139 prependChunk( pNew ); 121 /**
140 } 122 *@todo void append( const MyType & sData )
123 */
141 124
142 void prepend( const chr *pData, long nLen ) 125 /**
143 { 126 * Append data to your string.
144 Chunk *pNew = newChunk( nLen ); 127 *@param pData (const chr *) The data to append.
145 128 */
146 cpy( pNew->pData, pData, nLen ); 129 void append( const chr *pData )
130 {
131 if( !pData ) return;
132 long nLen;
133 for( nLen = 0; pData[nLen] != (chr)0; nLen++ );
134 if( nLen == 0 )
135 return;
136
137 Chunk *pNew = newChunk( nLen );
138 cpy( pNew->pData, pData, nLen );
147 139
148 prependChunk( pNew ); 140 appendChunk( pNew );
149 } 141 }
150 142
151 void clear() 143 /**
152 { 144 * Append data to your string.
153 realClear(); 145 *@param pData (const chr *) The data to append.
154 } 146 *@param nLen (long) The length of the data to append.
147 */
148 void append( const chr *pData, long nLen )
149 {
150 if( nLen == 0 )
151 return;
155 152
156 void resize( long nNewSize ) 153 Chunk *pNew = newChunk( nLen );
157 { 154
158 if( nLength == nNewSize ) 155 cpy( pNew->pData, pData, nLen );
159 return;
160
161 flatten();
162
163 Chunk *pNew = newChunk( nNewSize );
164 long nNewLen = (nNewSize<nLength)?(nNewSize):(nLength);
165 cpy( pNew->pData, pFirst->pData, nNewLen );
166 pNew->pData[nNewLen] = (chr)0;
167 aChr.deallocate( pFirst->pData, pFirst->nLength+1 );
168 aChunk.deallocate( pFirst, 1 );
169 pFirst = pLast = pNew;
170 nLength = nNewSize;
171 }
172
173 long getSize() const
174 {
175 return nLength;
176 }
177
178 chr *getStr()
179 {
180 if( pFirst == NULL )
181 return NULL;
182
183 flatten();
184 return pFirst->pData;
185 }
186
187 const chr *getStr() const
188 {
189 if( pFirst == NULL )
190 return NULL;
191 156
192 flatten(); 157 appendChunk( pNew );
193 return pFirst->pData; 158 }
194 }
195 159
196 chr *c_str() 160 /**
197 { 161 * Append a single chr to your string.
198 if( pFirst == NULL ) 162 *@param cData (const chr &) The character to append.
199 return NULL; 163 */
200 164 void append( const chr &cData )
201 flatten(); 165 {
202 return pFirst->pData; 166 append( &cData, 1 );
203 } 167 }
204
205 const chr *c_str() const
206 {
207 if( pFirst == NULL )
208 return NULL;
209 168
210 flatten(); 169 /**
211 return pFirst->pData; 170 * Prepend another FString to this one.
212 } 171 *@param sData (MyType &) The FString to prepend.
172 */
173 void prepend( const MyType & sData )
174 {
175 prepend( sData.getStr(), sData.getSize() );
176 }
213 177
214 MyType &operator +=( const chr *pData ) 178 /**
215 { 179 * Prepend data to your string.
216 append( pData ); 180 *@param pData (const chr *) The data to prepend.
181 */
182 void prepend( const chr *pData )
183 {
184 long nLen;
185 for( nLen = 0; pData[nLen] != (chr)0; nLen++ );
186
187 Chunk *pNew = newChunk( nLen );
188 cpy( pNew->pData, pData, nLen );
217 189
218 return (*this); 190 prependChunk( pNew );
219 } 191 }
220
221 MyType &operator +=( const MyType &rSrc )
222 {
223 rSrc.flatten();
224 append( rSrc.pFirst->pData, rSrc.nLength );
225 192
226 return (*this); 193 /**
227 } 194 * Prepend data to your string.
195 *@param pData (const chr *) The data to prepend.
196 *@param nLen (long) The length of the data to prepend.
197 */
198 void prepend( const chr *pData, long nLen )
199 {
200 Chunk *pNew = newChunk( nLen );
201
202 cpy( pNew->pData, pData, nLen );
228 203
229 MyType &operator +=( const chr pData ) 204 prependChunk( pNew );
230 { 205 }
231 chr tmp[2] = { pData, (chr)0 };
232 append( tmp );
233 206
234 return (*this); 207 /**
235 } 208 *@todo void prepend( const chr &cData )
209 */
236 210
237 MyType &operator =( const chr *pData ) 211 /**
238 { 212 * Clear all data from the string.
239 clear(); 213 */
240 append( pData ); 214 void clear()
215 {
216 realClear();
217 }
241 218
242 return (*this); 219 /**
243 } 220 * Force the string to resize
221 *@param nNewSize (long) The new size of the string.
222 */
223 void resize( long nNewSize )
224 {
225 if( nLength == nNewSize )
226 return;
244 227
245 MyType &operator =( const MyType &rSrc ) 228 flatten();
246 { 229
247 //if( rSrc.isFlat() ) 230 Chunk *pNew = newChunk( nNewSize );
248 //{ 231 long nNewLen = (nNewSize<nLength)?(nNewSize):(nLength);
249 joinShare( rSrc ); 232 cpy( pNew->pData, pFirst->pData, nNewLen );
250 //} 233 pNew->pData[nNewLen] = (chr)0;
251 //else 234 aChr.deallocate( pFirst->pData, pFirst->nLength+1 );
252 //{ 235 aChunk.deallocate( pFirst, 1 );
253 // copyFrom( rSrc ); 236 pFirst = pLast = pNew;
254 //} 237 nLength = nNewSize;
255 //
256
257 return (*this);
258 }
259
260 bool operator ==( const chr *pData ) const
261 {
262 if( pFirst == NULL ) {
263 if( pData == NULL )
264 return true;
265 return false;
266 } 238 }
267 239
268 flatten(); 240 /**
269 const chr *a = pData; 241 * Get the current size of the string.
270 chr *b = pFirst->pData; 242 *@returns (long) The current size of the string.
271 for( ; *a!=(chr)0; a++, b++ ) 243 */
244 long getSize() const
272 { 245 {
273 if( *a != *b ) 246 return nLength;
274 return false;
275 } 247 }
248
249 /**
250 * Get a pointer to the string array.
251 *@returns (chr *) The string data.
252 */
253 chr *getStr()
254 {
255 if( pFirst == NULL )
256 return NULL;
276 257
277 return true; 258 flatten();
278 } 259 return pFirst->pData;
279 260 }
280 bool operator ==( const MyType &pData ) const 261
281 { 262 /**
282 if( pFirst == pData.pFirst ) 263 * Get a const pointer to the string array.
283 return true; 264 *@returns (const chr *) The string data.
284 if( pFirst == NULL ) 265 */
285 return false; 266 const chr *getStr() const
267 {
268 if( pFirst == NULL )
269 return NULL;
270
271 flatten();
272 return pFirst->pData;
273 }
286 274
287 flatten(); 275 /**
288 pData.flatten(); 276 * (std::string compatability) Get a pointer to the string array.
289 const chr *a = pData.pFirst->pData; 277 *@returns (chr *) The string data.
290 chr *b = pFirst->pData; 278 */
291 for( ; *a!=(chr)0; a++, b++ ) 279 chr *c_str()
292 { 280 {
293 if( *a != *b ) 281 if( pFirst == NULL )
294 return false; 282 return NULL;
283
284 flatten();
285 return pFirst->pData;
295 } 286 }
287
288 /**
289 * (std::string compatability) Get a const pointer to the string array.
290 *@returns (const chr *) The string data.
291 */
292 const chr *c_str() const
293 {
294 if( pFirst == NULL )
295 return NULL;
296 296
297 return true; 297 flatten();
298 } 298 return pFirst->pData;
299 }
299 300
300 bool operator !=(const chr *pData ) const 301 /**
301 { 302 * Plus equals operator for FString.
302 return !(*this == pData); 303 *@param pData (const chr *) The data to append to your FString.
303 } 304 */
304 305 MyType &operator +=( const chr *pData )
305 bool operator !=(const MyType &pData ) const 306 {
306 { 307 append( pData );
307 return !(*this == pData);
308 }
309 308
310 chr &operator[]( long nIndex ) 309 return (*this);
311 { 310 }
312 flatten(); 311
312 /**
313 * Plus equals operator for FString.
314 *@param pData (const MyType &) The FString to append to your FString.
315 */
316 MyType &operator +=( const MyType &rSrc )
317 {
318 if( rSrc.nLength == 0 )
319 return (*this);
320 rSrc.flatten();
321 append( rSrc.pFirst->pData, rSrc.nLength );
313 322
314 return pFirst->pData[nIndex]; 323 return (*this);
315 } 324 }
316
317 const chr &operator[]( long nIndex ) const
318 {
319 flatten();
320 325
321 return pFirst->pData[nIndex]; 326 /**
322 } 327 * Plus equals operator for FString.
328 *@param pData (const chr) The character to append to your FString.
329 */
330 MyType &operator +=( const chr pData )
331 {
332 append( &pData, 1 );
323 333
324 bool isWS( long nIndex ) const 334 return (*this);
325 { 335 }
326 flatten();
327 336
328 return pFirst->pData[nIndex]==' ' || pFirst->pData[nIndex]=='\t' 337 /**
329 || pFirst->pData[nIndex]=='\r' || pFirst->pData[nIndex]=='\n'; 338 * Assignment operator.
330 } 339 *@param pData (const chr *) The character array to append to your
340 * FString.
341 */
342 MyType &operator =( const chr *pData )
343 {
344 clear();
345 append( pData );
331 346
332 bool isAlpha( long nIndex ) const 347 return (*this);
333 { 348 }
334 flatten();
335 349
336 return (pFirst->pData[nIndex] >= 'a' && pFirst->pData[nIndex] <= 'z') 350 /**
337 || (pFirst->pData[nIndex] >= 'A' && pFirst->pData[nIndex] <= 'Z'); 351 * Reset your FString to this character array.
338 } 352 *@param pData (const chr *) The character array to set your FString to.
353 */
354 void set( const chr *pData )
355 {
356 clear();
357 append( pData );
358 }
339 359
340 void toLower() 360 /**
341 { 361 * Reset your FString to this character array.
342 flatten(); 362 *@param pData (const chr *) The character array to set your FString to.
343 unShare(); 363 *@param nSize (long) The length of the inputted character array.
364 */
365 void set( const chr *pData, long nSize )
366 {
367 clear();
368 append( pData, nSize );
369 }
344 370
345 for( long j = 0; j < nLength; j++ ) 371 /**
372 * Assignment operator.
373 *@param rSrc (const MyType &) The FString to set your FString to.
374 */
375 MyType &operator =( const MyType &rSrc )
346 { 376 {
347 if( pFirst->pData[j] >= 'A' && pFirst->pData[j] <= 'Z' ) 377 //if( rSrc.isFlat() )
348 pFirst->pData[j] -= 'A'-'a'; 378 //{
379 joinShare( rSrc );
380 //}
381 //else
382 //{
383 // copyFrom( rSrc );
384 //}
385 //
386
387 return (*this);
349 } 388 }
350 } 389
390 /**
391 * Equals comparison operator.
392 *@param pData (const chr *) The character array to compare your FString
393 * to.
394 */
395 bool operator ==( const chr *pData ) const
396 {
397 if( pFirst == NULL ) {
398 if( pData == NULL )
399 return true;
400 return false;
401 }
351 402
352 void toUpper() 403 flatten();
353 { 404 const chr *a = pData;
354 flatten(); 405 chr *b = pFirst->pData;
355 unShare(); 406 for( ; *a!=(chr)0; a++, b++ )
407 {
408 if( *a != *b )
409 return false;
410 }
356 411
357 for( long j = 0; j < nLength; j++ ) 412 return true;
413 }
414
415 /**
416 * Equals comparison operator.
417 *@param pData (const MyType &) The FString to compare your FString to.
418 */
419 bool operator ==( const MyType &pData ) const
358 { 420 {
359 if( pFirst->pData[j] >= 'a' && pFirst->pData[j] <= 'z' ) 421 if( pFirst == pData.pFirst )
360 pFirst->pData[j] += 'A'-'a'; 422 return true;
423 if( pFirst == NULL )
424 return false;
425
426 flatten();
427 pData.flatten();
428 const chr *a = pData.pFirst->pData;
429 chr *b = pFirst->pData;
430 for( ; *a!=(chr)0; a++, b++ )
431 {
432 if( *a != *b )
433 return false;
434 }
435
436 return true;
361 } 437 }
362 }
363 438
364 void serialize( class Serializer &ar ) 439 /**
365 { 440 * Not equals comparison operator.
366 if( ar.isLoading() ) 441 *@param pData (const chr *) The character array to compare your FString
442 * to.
443 */
444 bool operator !=(const chr *pData ) const
367 { 445 {
368 clear(); 446 return !(*this == pData);
369 long nLen; 447 }
370 ar >> nLen;
371 448
372 Chunk *pNew = newChunk( nLen ); 449 /**
373 ar.read( pNew->pData, nLen*sizeof(chr) ); 450 * Not equals comparison operator.
374 appendChunk( pNew ); 451 *@param pData (const MyType &) The FString to compare your FString to.
452 */
453 bool operator !=(const MyType &pData ) const
454 {
455 return !(*this == pData);
375 } 456 }
376 else 457
458 /**
459 * Indexing operator
460 *@param nIndex (long) The index of the character you want.
461 *@returns (chr &) The character at position (nIndex).
462 */
463 chr &operator[]( long nIndex )
377 { 464 {
378 flatten(); 465 flatten();
379 466
380 ar << nLength; 467 return pFirst->pData[nIndex];
381 ar.write( pFirst->pData, nLength*sizeof(chr) );
382 } 468 }
383 } 469
470 /**
471 * Const indexing operator
472 *@param nIndex (long) The index of the character you want.
473 *@returns (const chr &) The character at position (nIndex).
474 */
475 const chr &operator[]( long nIndex ) const
476 {
477 flatten();
384 478
385private: 479 return pFirst->pData[nIndex];
386 void flatten() const 480 }
387 { 481/*
388 if( isFlat() ) 482 operator const chr *() const
389 return; 483 {
484 if( !pFirst ) return NULL;
485 flatten();
486 return pFirst->pData;
487 }
488 */
390 489
391 if( pFirst == NULL ) 490 operator bool() const
392 return; 491 {
492 return (pFirst != NULL);
493 }
393 494
394 unShare(); 495 bool isSet() const
496 {
497 return (pFirst != NULL);
498 }
395 499
396 Chunk *pNew = newChunk( nLength ); 500 /**
397 chr *pos = pNew->pData; 501 * Is the character at index (nIndex) white space?
398 Chunk *i = pFirst; 502 *@param nIndex (long) The index of the character you want to check.
399 for(;;) 503 *@returns (bool) Is it white space?
504 */
505 bool isWS( long nIndex ) const
400 { 506 {
401 cpy( pos, i->pData, i->nLength ); 507 flatten();
402 pos += i->nLength; 508
403 i = i->pNext; 509 return pFirst->pData[nIndex]==' ' || pFirst->pData[nIndex]=='\t'
404 if( i == NULL ) 510 || pFirst->pData[nIndex]=='\r' || pFirst->pData[nIndex]=='\n';
405 break;
406 } 511 }
407 realClear();
408 512
409 pLast = pFirst = pNew; 513 /**
410 nLength = pNew->nLength; 514 * Is the character at index (nIndex) a letter?
411 } 515 *@param nIndex (long) The index of the character you want to check.
412 516 *@returns (bool) Is it a letter?
413 void realClear() const 517 */
414 { 518 bool isAlpha( long nIndex ) const
415 if( pFirst == NULL ) 519 {
416 return; 520 flatten();
417 521
418 if( isShared() ) 522 return (pFirst->pData[nIndex] >= 'a' && pFirst->pData[nIndex] <= 'z')
523 || (pFirst->pData[nIndex] >= 'A' && pFirst->pData[nIndex] <= 'Z');
524 }
525
526 /**
527 * Convert your alpha characters to lower case.
528 */
529 void toLower()
419 { 530 {
420 decRefs(); 531 flatten();
532 unShare();
533
534 for( long j = 0; j < nLength; j++ )
535 {
536 if( pFirst->pData[j] >= 'A' && pFirst->pData[j] <= 'Z' )
537 pFirst->pData[j] -= 'A'-'a';
538 }
539 }
540
541 /**
542 * Convert your alpha characters to upper case.
543 */
544 void toUpper()
545 {
546 flatten();
547 unShare();
548
549 for( long j = 0; j < nLength; j++ )
550 {
551 if( pFirst->pData[j] >= 'a' && pFirst->pData[j] <= 'z' )
552 pFirst->pData[j] += 'A'-'a';
553 }
554 }
555
556 /**
557 * Find the index of the first occurrance of (sText)
558 *@param sText (const char *) The string to search for.
559 *@returns (long) The index of the first occurrance. -1 for not found.
560 */
561 long find( const char *sText )
562 {
563 long nTLen = strlen( sText );
564 flatten();
565 for( long j = 0; j < pFirst->nLength-nTLen; j++ )
566 {
567 if( !strncmp( sText, pFirst->pData+j, nTLen ) )
568 return j;
569 }
570 return -1;
571 }
572
573 /**
574 * Do a reverse search for (sText)
575 *@param sText (const char *) The string to search for.
576 *@returns (long) The index of the last occurrance. -1 for not found.
577 */
578 long rfind( const char *sText )
579 {
580 long nTLen = strlen( sText );
581 flatten();
582 for( long j = pFirst->nLength-nTLen-1; j >= 0; j-- )
583 {
584 if( !strncmp( sText, pFirst->pData+j, nTLen ) )
585 return j;
586 }
587 return -1;
588 }
589
590 /**
591 * Remove nAmnt bytes from the front of the string. This function
592 * operates in O(n) time and should be used sparingly.
593 */
594 void trimFront( long nAmnt )
595 {
596 long nNewLen = nLength - nAmnt;
597 flatten();
598 Chunk *pNew = newChunk( nNewLen );
599 cpy( pNew->pData, pFirst->pData, nNewLen );
600 clear();
601 appendChunk( pNew );
602 }
603
604 /**
605 * Function the archiver calls to archive your FString.
606 *@param ar (Archive) The archive which is archiving your FString.
607 */
608 void archive( class Archive &ar )
609 {
610 if( ar.isLoading() )
611 {
612 clear();
613 long nLen;
614 ar >> nLen;
615
616 if( nLen > 0 )
617 {
618 Chunk *pNew = newChunk( nLen );
619 ar.read( pNew->pData, nLen*sizeof(chr) );
620 appendChunk( pNew );
621 }
622 }
623 else
624 {
625 flatten();
626
627 ar << nLength;
628 if( nLength )
629 ar.write( pFirst->pData, nLength*sizeof(chr) );
630 }
421 } 631 }
422 else 632
633 private:
634 void flatten() const
423 { 635 {
636 if( isFlat() )
637 return;
638
639 if( pFirst == NULL )
640 return;
641
642 unShare();
643
644 Chunk *pNew = newChunk( nLength );
645 chr *pos = pNew->pData;
424 Chunk *i = pFirst; 646 Chunk *i = pFirst;
425 for(;;) 647 for(;;)
426 { 648 {
427 Chunk *n = i->pNext; 649 cpy( pos, i->pData, i->nLength );
428 aChr.deallocate( i->pData, i->nLength+1 ); 650 pos += i->nLength;
429 aChunk.deallocate( i, 1 ); 651 i = i->pNext;
430 if( n == NULL ) 652 if( i == NULL )
431 break; 653 break;
432 i = n;
433 } 654 }
434 pFirst = pLast = NULL; 655 realClear();
435 nLength = 0; 656
657 pLast = pFirst = pNew;
658 nLength = pNew->nLength;
436 } 659 }
437 }
438
439 void copyFrom( const FBasicString<chr, chralloc, chunkalloc> &rSrc )
440 {
441 if( rSrc.pFirst == NULL )
442 return;
443 660
444 decRefs(); 661 void realClear() const
445
446 Chunk *pNew = newChunk( rSrc.nLength );
447 chr *pos = pNew->pData;
448 Chunk *i = rSrc.pFirst;
449 for(;;)
450 { 662 {
451 cpy( pos, i->pData, i->nLength ); 663 if( pFirst == NULL )
452 pos += i->nLength; 664 return;
453 i = i->pNext;
454 if( i == NULL )
455 break;
456 }
457 clear();
458 665
459 appendChunk( pNew ); 666 if( isShared() )
460 } 667 {
461 668 decRefs();
462 bool isFlat() const 669 }
463 { 670 else
464 return (pFirst == pLast); 671 {
465 } 672 Chunk *i = pFirst;
466 673 for(;;)
467 bool isShared() const 674 {
468 { 675 Chunk *n = i->pNext;
469 return (pnRefs != NULL); 676 aChr.deallocate( i->pData, i->nLength+1 );
470 } 677 aChunk.deallocate( i, 1 );
678 if( n == NULL )
679 break;
680 i = n;
681 }
682 pFirst = pLast = NULL;
683 nLength = 0;
684 }
685 }
686
687 void copyFrom( const FBasicString<chr, nMinSize, chralloc, chunkalloc> &rSrc )
688 {
689 if( rSrc.pFirst == NULL )
690 return;
691
692 decRefs();
471 693
472 Chunk *newChunk() const 694 Chunk *pNew = newChunk( rSrc.nLength );
473 { 695 chr *pos = pNew->pData;
474 Chunk *pNew = aChunk.allocate( 1 ); 696 Chunk *i = rSrc.pFirst;
475 pNew->pNext = NULL; 697 for(;;)
476 return pNew; 698 {
477 } 699 cpy( pos, i->pData, i->nLength );
478 700 pos += i->nLength;
479 Chunk *newChunk( long nLen ) const 701 i = i->pNext;
480 { 702 if( i == NULL )
481 Chunk *pNew = aChunk.allocate( 1 ); 703 break;
482 pNew->pNext = NULL; 704 }
483 pNew->nLength = nLen; 705 clear();
484 pNew->pData = aChr.allocate( nLen+1 );
485 pNew->pData[nLen] = (chr)0;
486 return pNew;
487 }
488
489 void appendChunk( Chunk *pNewChunk )
490 {
491 unShare();
492 706
493 if( pFirst == NULL ) 707 appendChunk( pNew );
494 pLast = pFirst = pNewChunk; 708 }
495 else 709
710 bool isFlat() const
496 { 711 {
497 pLast->pNext = pNewChunk; 712 return (pFirst == pLast);
498 pLast = pNewChunk;
499 } 713 }
500 714
501 nLength += pNewChunk->nLength; 715 bool isShared() const
502 } 716 {
503 717 return (pnRefs != NULL);
504 void prependChunk( Chunk *pNewChunk ) 718 }
505 {
506 unShare();
507 719
508 if( pFirst == NULL ) 720 Chunk *newChunk() const
509 pLast = pFirst = pNewChunk; 721 {
510 else 722 Chunk *pNew = aChunk.allocate( 1 );
723 pNew->pNext = NULL;
724 return pNew;
725 }
726
727 Chunk *newChunk( long nLen ) const
511 { 728 {
512 pNewChunk->pNext = pFirst; 729 Chunk *pNew = aChunk.allocate( 1 );
513 pFirst = pNewChunk; 730 pNew->pNext = NULL;
731 pNew->nLength = nLen;
732 pNew->pData = aChr.allocate( nLen+1 );
733 pNew->pData[nLen] = (chr)0;
734 return pNew;
514 } 735 }
515 736
516 nLength += pNewChunk->nLength; 737 void appendChunk( Chunk *pNewChunk )
517 } 738 {
739 unShare();
518 740
519 void joinShare( MyType &rSrc ) 741 if( pFirst == NULL )
520 { 742 pLast = pFirst = pNewChunk;
521 clear(); 743 else
744 {
745 pLast->pNext = pNewChunk;
746 pLast = pNewChunk;
747 }
522 748
523 if( !rSrc.isFlat() ) 749 nLength += pNewChunk->nLength;
524 rSrc.flatten(); 750 }
751
752 void prependChunk( Chunk *pNewChunk )
753 {
754 unShare();
525 755
526 rSrc.initCount(); 756 if( pFirst == NULL )
527 pnRefs = rSrc.pnRefs; 757 pLast = pFirst = pNewChunk;
528 (*pnRefs)++; 758 else
529 nLength = rSrc.nLength; 759 {
530 pFirst = rSrc.pFirst; 760 pNewChunk->pNext = pFirst;
531 pLast = rSrc.pLast; 761 pFirst = pNewChunk;
532 } 762 }
533
534 void joinShare( const MyType &rSrc )
535 {
536 clear();
537 763
538 rSrc.flatten(); 764 nLength += pNewChunk->nLength;
765 }
539 766
540 if( !rSrc.isShared() ) 767 void joinShare( MyType &rSrc )
541 { 768 {
542 rSrc.pnRefs = new uint32_t; 769 clear();
543 (*rSrc.pnRefs) = 1; 770
771 if( !rSrc.isFlat() )
772 rSrc.flatten();
773
774 rSrc.initCount();
775 pnRefs = rSrc.pnRefs;
776 (*pnRefs)++;
777 nLength = rSrc.nLength;
778 pFirst = rSrc.pFirst;
779 pLast = rSrc.pLast;
544 } 780 }
545 pnRefs = rSrc.pnRefs; 781
546 (*pnRefs)++; 782 void joinShare( const MyType &rSrc )
547 nLength = rSrc.nLength; 783 {
548 pFirst = rSrc.pFirst; 784 clear();
549 pLast = rSrc.pLast;
550 }
551 785
552 /** 786 rSrc.flatten();
553 * This takes an object that was shared and makes a copy of the base data
554 * that was being shared so that this copy can be changed. This should be
555 * added before any call that will change this object;
556 */
557 void unShare() const
558 {
559 if( isShared() == false )
560 return;
561 787
562 Chunk *pNew = newChunk( nLength ); 788 if( !rSrc.isShared() )
563 chr *pos = pNew->pData; 789 {
564 Chunk *i = pFirst; 790 rSrc.pnRefs = new uint32_t;
565 for(;;) 791 (*rSrc.pnRefs) = 1;
792 }
793 pnRefs = rSrc.pnRefs;
794 (*pnRefs)++;
795 nLength = rSrc.nLength;
796 pFirst = rSrc.pFirst;
797 pLast = rSrc.pLast;
798 }
799
800 /**
801 * This takes an object that was shared and makes a copy of the base data
802 * that was being shared so that this copy can be changed. This should be
803 * added before any call that will change this object;
804 */
805 void unShare() const
566 { 806 {
567 cpy( pos, i->pData, i->nLength ); 807 if( isShared() == false )
568 pos += i->nLength; 808 return;
569 i = i->pNext; 809 if( pFirst == NULL )
570 if( i == NULL ) 810 return;
571 break; 811
812 Chunk *pNew = newChunk( nLength );
813 chr *pos = pNew->pData;
814 Chunk *i = pFirst;
815 for(;;)
816 {
817 cpy( pos, i->pData, i->nLength );
818 pos += i->nLength;
819 i = i->pNext;
820 if( i == NULL )
821 break;
822 }
823 decRefs();
824 pLast = pFirst = pNew;
825 nLength = pNew->nLength;
572 } 826 }
573 decRefs();
574 pLast = pFirst = pNew;
575 nLength = pNew->nLength;
576 }
577 827
578 /** 828 /**
579 * This decrements our ref count and pulls us out of the share. If the ref 829 * This decrements our ref count and pulls us out of the share. If the ref
580 * count hits zero because of this, it destroys the share. This is not 830 * count hits zero because of this, it destroys the share. This is not
581 * safe to call on it's own, it's much better to call unShare. 831 * safe to call on it's own, it's much better to call unShare.
582 */ 832 */
583 void decRefs() const 833 void decRefs() const
584 {
585 if( isShared() )
586 { 834 {
587 (*pnRefs)--; 835 if( isShared() )
588 if( (*pnRefs) == 0 )
589 destroyShare();
590 else
591 { 836 {
592 pnRefs = NULL; 837 (*pnRefs)--;
593 pFirst = NULL; 838 if( (*pnRefs) == 0 )
594 pLast = NULL; 839 destroyShare();
595 nLength = 0; 840 else
841 {
842 pnRefs = NULL;
843 pFirst = NULL;
844 pLast = NULL;
845 nLength = 0;
846 }
596 } 847 }
597 } 848 }
598 }
599 849
600 /** 850 /**
601 * While the unShare function removes an instance from a share, this 851 * While the unShare function removes an instance from a share, this
602 * function destroys the data that was in the share, removing the share 852 * function destroys the data that was in the share, removing the share
603 * itself. This should only be called when the refcount for the share has 853 * itself. This should only be called when the refcount for the share has
604 * or is about to reach zero. 854 * or is about to reach zero.
605 */ 855 */
606 void destroyShare() const 856 void destroyShare() const
607 { 857 {
608 delete pnRefs; 858 delete pnRefs;
609 pnRefs = NULL; 859 pnRefs = NULL;
610 realClear(); 860 realClear();
611 } 861 }
612 862
613#ifdef VALTEST 863#ifdef VALTEST
614 void cpy( chr *dest, const chr *src, long count ) const 864 void cpy( chr *dest, const chr *src, long count ) const
615 {
616 for( int j = 0; j < count; j++ )
617 { 865 {
618 *dest = *src; 866 for( int j = 0; j < count; j++ )
619 dest++; 867 {
620 src++; 868 *dest = *src;
869 dest++;
870 src++;
871 }
621 } 872 }
622 }
623#endif 873#endif
624 874
625 void initCount() const 875 void initCount() const
626 {
627 if( !isShared() )
628 { 876 {
629 pnRefs = new uint32_t; 877 if( !isShared() )
630 (*pnRefs) = 1; 878 {
879 pnRefs = new uint32_t;
880 (*pnRefs) = 1;
881 }
631 } 882 }
632 }
633 883
634private: 884 private:
635 mutable long nLength; 885 mutable long nLength;
636 mutable uint32_t *pnRefs; 886 mutable uint32_t *pnRefs;
637 mutable Chunk *pFirst; 887 mutable Chunk *pFirst;
638 mutable Chunk *pLast; 888 mutable Chunk *pLast;
639 889
640 mutable chralloc aChr; 890 mutable chralloc aChr;
641 mutable chunkalloc aChunk; 891 mutable chunkalloc aChunk;
642}; 892 };
643 893
644typedef FBasicString<char> FString; 894 typedef FBasicString<char> FString;
645 895
646#include "hash.h" 896 template<> uint32_t __calcHashCode<FString>( const FString &k );
647template<> uint32_t __calcHashCode<FString>( const FString &k ); 897 template<> bool __cmpHashKeys<FString>( const FString &a, const FString &b );
648template<> bool __cmpHashKeys<FString>( const FString &a, const FString &b ); 898}
649 899
900#include <ostream>
901std::basic_ostream<char>& operator<< (std::basic_ostream<char> &os, const Bu::FString &val );
650 902
651#endif 903#endif