aboutsummaryrefslogtreecommitdiff
path: root/src/fstring.h
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2007-04-03 04:50:36 +0000
committerMike Buland <eichlan@xagasoft.com>2007-04-03 04:50:36 +0000
commitda89e6d30e57bd6dbb10b4d36b093ce9bbf5c666 (patch)
tree0c8d31d13521011dc52a3fbadbf9e7e27929308f /src/fstring.h
parentf4c20290509d7ed3a8fd5304577e7a4cc0b9d974 (diff)
downloadlibbu++-da89e6d30e57bd6dbb10b4d36b093ce9bbf5c666.tar.gz
libbu++-da89e6d30e57bd6dbb10b4d36b093ce9bbf5c666.tar.bz2
libbu++-da89e6d30e57bd6dbb10b4d36b093ce9bbf5c666.tar.xz
libbu++-da89e6d30e57bd6dbb10b4d36b093ce9bbf5c666.zip
The first batch seem to have made it alright. Unfortunately the Archive class
isn't done yet, I'm going to make it rely on streams, so those will be next, then we can make it work all sortsa' well.
Diffstat (limited to '')
-rw-r--r--src/fstring.h653
1 files changed, 653 insertions, 0 deletions
diff --git a/src/fstring.h b/src/fstring.h
new file mode 100644
index 0000000..717068f
--- /dev/null
+++ b/src/fstring.h
@@ -0,0 +1,653 @@
1#ifndef F_STRING_H
2#define F_STRING_H
3
4#include <stdint.h>
5#include <memory>
6#include "archable.h"
7#include "archive.h"
8#include "hash.h"
9
10namespace Bu
11{
12 template< typename chr >
13 struct FStringChunk
14 {
15 long nLength;
16 chr *pData;
17 FStringChunk *pNext;
18 };
19
20 /**
21 * Flexible String class. This class was designed with string passing and
22 * generation in mind. Like the standard string class you can specify what
23 * datatype to use for each character. Unlike the standard string class,
24 * collection of appended and prepended terms is done lazily, making long
25 * operations that involve many appends very inexpensive. In addition internal
26 * ref-counting means that if you pass strings around between functions there's
27 * almost no overhead in time or memory since a reference is created and no
28 * data is actually copied. This also means that you never need to put any
29 * FBasicString into a ref-counting container class.
30 */
31 template< typename chr, typename chralloc=std::allocator<chr>, typename chunkalloc=std::allocator<struct FStringChunk<chr> > >
32 class FBasicString : public Archable
33 {
34#ifndef VALTEST
35#define cpy( dest, src, size ) memcpy( dest, src, size*sizeof(chr) )
36#endif
37 private:
38 typedef struct FStringChunk<chr> Chunk;
39 typedef struct FBasicString<chr, chralloc, chunkalloc> MyType;
40
41 public:
42 FBasicString() :
43 nLength( 0 ),
44 pnRefs( NULL ),
45 pFirst( NULL ),
46 pLast( NULL )
47 {
48 }
49
50 FBasicString( const chr *pData ) :
51 nLength( 0 ),
52 pnRefs( NULL ),
53 pFirst( NULL ),
54 pLast( NULL )
55 {
56 append( pData );
57 }
58
59 FBasicString( const chr *pData, long nLength ) :
60 nLength( 0 ),
61 pnRefs( NULL ),
62 pFirst( NULL ),
63 pLast( NULL )
64 {
65 append( pData, nLength );
66 }
67
68 FBasicString( const MyType &rSrc ) :
69 nLength( 0 ),
70 pnRefs( NULL ),
71 pFirst( NULL ),
72 pLast( NULL )
73 {
74 // Here we have no choice but to copy, since the other guy is a const.
75 // In the case that the source were flat, we could get a reference, it
76 // would make some things faster, but not matter in many other cases.
77
78 joinShare( rSrc );
79 //copyFrom( rSrc );
80 }
81
82 FBasicString( const MyType &rSrc, long nLength ) :
83 nLength( 0 ),
84 pnRefs( NULL ),
85 pFirst( NULL ),
86 pLast( NULL )
87 {
88 append( rSrc.pFirst->pData, nLength );
89 }
90
91 FBasicString( const MyType &rSrc, long nStart, long nLength ) :
92 nLength( 0 ),
93 pnRefs( NULL ),
94 pFirst( NULL ),
95 pLast( NULL )
96 {
97 append( rSrc.pFirst->pData+nStart, nLength );
98 }
99
100 FBasicString( long nSize ) :
101 nLength( nSize ),
102 pnRefs( NULL ),
103 pFirst( NULL ),
104 pLast( NULL )
105 {
106 pFirst = pLast = newChunk( nSize );
107 }
108
109 virtual ~FBasicString()
110 {
111 clear();
112 }
113
114 void append( const chr *pData )
115 {
116 long nLen;
117 for( nLen = 0; pData[nLen] != (chr)0; nLen++ );
118
119 Chunk *pNew = newChunk( nLen );
120 cpy( pNew->pData, pData, nLen );
121
122 appendChunk( pNew );
123 }
124
125 void append( const chr *pData, long nLen )
126 {
127 Chunk *pNew = newChunk( nLen );
128
129 cpy( pNew->pData, pData, nLen );
130
131 appendChunk( pNew );
132 }
133
134 void prepend( const chr *pData )
135 {
136 long nLen;
137 for( nLen = 0; pData[nLen] != (chr)0; nLen++ );
138
139 Chunk *pNew = newChunk( nLen );
140 cpy( pNew->pData, pData, nLen );
141
142 prependChunk( pNew );
143 }
144
145 void prepend( const chr *pData, long nLen )
146 {
147 Chunk *pNew = newChunk( nLen );
148
149 cpy( pNew->pData, pData, nLen );
150
151 prependChunk( pNew );
152 }
153
154 void clear()
155 {
156 realClear();
157 }
158
159 void resize( long nNewSize )
160 {
161 if( nLength == nNewSize )
162 return;
163
164 flatten();
165
166 Chunk *pNew = newChunk( nNewSize );
167 long nNewLen = (nNewSize<nLength)?(nNewSize):(nLength);
168 cpy( pNew->pData, pFirst->pData, nNewLen );
169 pNew->pData[nNewLen] = (chr)0;
170 aChr.deallocate( pFirst->pData, pFirst->nLength+1 );
171 aChunk.deallocate( pFirst, 1 );
172 pFirst = pLast = pNew;
173 nLength = nNewSize;
174 }
175
176 long getSize() const
177 {
178 return nLength;
179 }
180
181 chr *getStr()
182 {
183 if( pFirst == NULL )
184 return NULL;
185
186 flatten();
187 return pFirst->pData;
188 }
189
190 const chr *getStr() const
191 {
192 if( pFirst == NULL )
193 return NULL;
194
195 flatten();
196 return pFirst->pData;
197 }
198
199 chr *c_str()
200 {
201 if( pFirst == NULL )
202 return NULL;
203
204 flatten();
205 return pFirst->pData;
206 }
207
208 const chr *c_str() const
209 {
210 if( pFirst == NULL )
211 return NULL;
212
213 flatten();
214 return pFirst->pData;
215 }
216
217 MyType &operator +=( const chr *pData )
218 {
219 append( pData );
220
221 return (*this);
222 }
223
224 MyType &operator +=( const MyType &rSrc )
225 {
226 rSrc.flatten();
227 append( rSrc.pFirst->pData, rSrc.nLength );
228
229 return (*this);
230 }
231
232 MyType &operator +=( const chr pData )
233 {
234 chr tmp[2] = { pData, (chr)0 };
235 append( tmp );
236
237 return (*this);
238 }
239
240 MyType &operator =( const chr *pData )
241 {
242 clear();
243 append( pData );
244
245 return (*this);
246 }
247
248 MyType &operator =( const MyType &rSrc )
249 {
250 //if( rSrc.isFlat() )
251 //{
252 joinShare( rSrc );
253 //}
254 //else
255 //{
256 // copyFrom( rSrc );
257 //}
258 //
259
260 return (*this);
261 }
262
263 bool operator ==( const chr *pData ) const
264 {
265 if( pFirst == NULL ) {
266 if( pData == NULL )
267 return true;
268 return false;
269 }
270
271 flatten();
272 const chr *a = pData;
273 chr *b = pFirst->pData;
274 for( ; *a!=(chr)0; a++, b++ )
275 {
276 if( *a != *b )
277 return false;
278 }
279
280 return true;
281 }
282
283 bool operator ==( const MyType &pData ) const
284 {
285 if( pFirst == pData.pFirst )
286 return true;
287 if( pFirst == NULL )
288 return false;
289
290 flatten();
291 pData.flatten();
292 const chr *a = pData.pFirst->pData;
293 chr *b = pFirst->pData;
294 for( ; *a!=(chr)0; a++, b++ )
295 {
296 if( *a != *b )
297 return false;
298 }
299
300 return true;
301 }
302
303 bool operator !=(const chr *pData ) const
304 {
305 return !(*this == pData);
306 }
307
308 bool operator !=(const MyType &pData ) const
309 {
310 return !(*this == pData);
311 }
312
313 chr &operator[]( long nIndex )
314 {
315 flatten();
316
317 return pFirst->pData[nIndex];
318 }
319
320 const chr &operator[]( long nIndex ) const
321 {
322 flatten();
323
324 return pFirst->pData[nIndex];
325 }
326
327 bool isWS( long nIndex ) const
328 {
329 flatten();
330
331 return pFirst->pData[nIndex]==' ' || pFirst->pData[nIndex]=='\t'
332 || pFirst->pData[nIndex]=='\r' || pFirst->pData[nIndex]=='\n';
333 }
334
335 bool isAlpha( long nIndex ) const
336 {
337 flatten();
338
339 return (pFirst->pData[nIndex] >= 'a' && pFirst->pData[nIndex] <= 'z')
340 || (pFirst->pData[nIndex] >= 'A' && pFirst->pData[nIndex] <= 'Z');
341 }
342
343 void toLower()
344 {
345 flatten();
346 unShare();
347
348 for( long j = 0; j < nLength; j++ )
349 {
350 if( pFirst->pData[j] >= 'A' && pFirst->pData[j] <= 'Z' )
351 pFirst->pData[j] -= 'A'-'a';
352 }
353 }
354
355 void toUpper()
356 {
357 flatten();
358 unShare();
359
360 for( long j = 0; j < nLength; j++ )
361 {
362 if( pFirst->pData[j] >= 'a' && pFirst->pData[j] <= 'z' )
363 pFirst->pData[j] += 'A'-'a';
364 }
365 }
366
367 void archive( class Archive &ar )
368 {
369 if( ar.isLoading() )
370 {
371 clear();
372 long nLen;
373 ar >> nLen;
374
375 Chunk *pNew = newChunk( nLen );
376 ar.read( pNew->pData, nLen*sizeof(chr) );
377 appendChunk( pNew );
378 }
379 else
380 {
381 flatten();
382
383 ar << nLength;
384 ar.write( pFirst->pData, nLength*sizeof(chr) );
385 }
386 }
387
388 private:
389 void flatten() const
390 {
391 if( isFlat() )
392 return;
393
394 if( pFirst == NULL )
395 return;
396
397 unShare();
398
399 Chunk *pNew = newChunk( nLength );
400 chr *pos = pNew->pData;
401 Chunk *i = pFirst;
402 for(;;)
403 {
404 cpy( pos, i->pData, i->nLength );
405 pos += i->nLength;
406 i = i->pNext;
407 if( i == NULL )
408 break;
409 }
410 realClear();
411
412 pLast = pFirst = pNew;
413 nLength = pNew->nLength;
414 }
415
416 void realClear() const
417 {
418 if( pFirst == NULL )
419 return;
420
421 if( isShared() )
422 {
423 decRefs();
424 }
425 else
426 {
427 Chunk *i = pFirst;
428 for(;;)
429 {
430 Chunk *n = i->pNext;
431 aChr.deallocate( i->pData, i->nLength+1 );
432 aChunk.deallocate( i, 1 );
433 if( n == NULL )
434 break;
435 i = n;
436 }
437 pFirst = pLast = NULL;
438 nLength = 0;
439 }
440 }
441
442 void copyFrom( const FBasicString<chr, chralloc, chunkalloc> &rSrc )
443 {
444 if( rSrc.pFirst == NULL )
445 return;
446
447 decRefs();
448
449 Chunk *pNew = newChunk( rSrc.nLength );
450 chr *pos = pNew->pData;
451 Chunk *i = rSrc.pFirst;
452 for(;;)
453 {
454 cpy( pos, i->pData, i->nLength );
455 pos += i->nLength;
456 i = i->pNext;
457 if( i == NULL )
458 break;
459 }
460 clear();
461
462 appendChunk( pNew );
463 }
464
465 bool isFlat() const
466 {
467 return (pFirst == pLast);
468 }
469
470 bool isShared() const
471 {
472 return (pnRefs != NULL);
473 }
474
475 Chunk *newChunk() const
476 {
477 Chunk *pNew = aChunk.allocate( 1 );
478 pNew->pNext = NULL;
479 return pNew;
480 }
481
482 Chunk *newChunk( long nLen ) const
483 {
484 Chunk *pNew = aChunk.allocate( 1 );
485 pNew->pNext = NULL;
486 pNew->nLength = nLen;
487 pNew->pData = aChr.allocate( nLen+1 );
488 pNew->pData[nLen] = (chr)0;
489 return pNew;
490 }
491
492 void appendChunk( Chunk *pNewChunk )
493 {
494 unShare();
495
496 if( pFirst == NULL )
497 pLast = pFirst = pNewChunk;
498 else
499 {
500 pLast->pNext = pNewChunk;
501 pLast = pNewChunk;
502 }
503
504 nLength += pNewChunk->nLength;
505 }
506
507 void prependChunk( Chunk *pNewChunk )
508 {
509 unShare();
510
511 if( pFirst == NULL )
512 pLast = pFirst = pNewChunk;
513 else
514 {
515 pNewChunk->pNext = pFirst;
516 pFirst = pNewChunk;
517 }
518
519 nLength += pNewChunk->nLength;
520 }
521
522 void joinShare( MyType &rSrc )
523 {
524 clear();
525
526 if( !rSrc.isFlat() )
527 rSrc.flatten();
528
529 rSrc.initCount();
530 pnRefs = rSrc.pnRefs;
531 (*pnRefs)++;
532 nLength = rSrc.nLength;
533 pFirst = rSrc.pFirst;
534 pLast = rSrc.pLast;
535 }
536
537 void joinShare( const MyType &rSrc )
538 {
539 clear();
540
541 rSrc.flatten();
542
543 if( !rSrc.isShared() )
544 {
545 rSrc.pnRefs = new uint32_t;
546 (*rSrc.pnRefs) = 1;
547 }
548 pnRefs = rSrc.pnRefs;
549 (*pnRefs)++;
550 nLength = rSrc.nLength;
551 pFirst = rSrc.pFirst;
552 pLast = rSrc.pLast;
553 }
554
555 /**
556 * This takes an object that was shared and makes a copy of the base data
557 * that was being shared so that this copy can be changed. This should be
558 * added before any call that will change this object;
559 */
560 void unShare() const
561 {
562 if( isShared() == false )
563 return;
564
565 Chunk *pNew = newChunk( nLength );
566 chr *pos = pNew->pData;
567 Chunk *i = pFirst;
568 for(;;)
569 {
570 cpy( pos, i->pData, i->nLength );
571 pos += i->nLength;
572 i = i->pNext;
573 if( i == NULL )
574 break;
575 }
576 decRefs();
577 pLast = pFirst = pNew;
578 nLength = pNew->nLength;
579 }
580
581 /**
582 * This decrements our ref count and pulls us out of the share. If the ref
583 * count hits zero because of this, it destroys the share. This is not
584 * safe to call on it's own, it's much better to call unShare.
585 */
586 void decRefs() const
587 {
588 if( isShared() )
589 {
590 (*pnRefs)--;
591 if( (*pnRefs) == 0 )
592 destroyShare();
593 else
594 {
595 pnRefs = NULL;
596 pFirst = NULL;
597 pLast = NULL;
598 nLength = 0;
599 }
600 }
601 }
602
603 /**
604 * While the unShare function removes an instance from a share, this
605 * function destroys the data that was in the share, removing the share
606 * itself. This should only be called when the refcount for the share has
607 * or is about to reach zero.
608 */
609 void destroyShare() const
610 {
611 delete pnRefs;
612 pnRefs = NULL;
613 realClear();
614 }
615
616#ifdef VALTEST
617 void cpy( chr *dest, const chr *src, long count ) const
618 {
619 for( int j = 0; j < count; j++ )
620 {
621 *dest = *src;
622 dest++;
623 src++;
624 }
625 }
626#endif
627
628 void initCount() const
629 {
630 if( !isShared() )
631 {
632 pnRefs = new uint32_t;
633 (*pnRefs) = 1;
634 }
635 }
636
637 private:
638 mutable long nLength;
639 mutable uint32_t *pnRefs;
640 mutable Chunk *pFirst;
641 mutable Chunk *pLast;
642
643 mutable chralloc aChr;
644 mutable chunkalloc aChunk;
645 };
646
647 typedef FBasicString<char> FString;
648
649 template<> uint32_t __calcHashCode<FString>( const FString &k );
650 template<> bool __cmpHashKeys<FString>( const FString &a, const FString &b );
651}
652
653#endif