diff options
author | Mike Buland <eichlan@xagasoft.com> | 2007-04-03 03:49:53 +0000 |
---|---|---|
committer | Mike Buland <eichlan@xagasoft.com> | 2007-04-03 03:49:53 +0000 |
commit | f4c20290509d7ed3a8fd5304577e7a4cc0b9d974 (patch) | |
tree | 13cdf64f7cf134f397a7165b7a3fe0807e37026b /src/old/fstring.h | |
parent | 74d4c8cd27334fc7204d5a8773deb3d424565778 (diff) | |
download | libbu++-f4c20290509d7ed3a8fd5304577e7a4cc0b9d974.tar.gz libbu++-f4c20290509d7ed3a8fd5304577e7a4cc0b9d974.tar.bz2 libbu++-f4c20290509d7ed3a8fd5304577e7a4cc0b9d974.tar.xz libbu++-f4c20290509d7ed3a8fd5304577e7a4cc0b9d974.zip |
Ok, no code is left in src, it's all in src/old. We'll gradually move code back
into src as it's fixed and re-org'd. This includes tests, which, I may write a
unit test system into libbu++ just to make my life easier.
Diffstat (limited to 'src/old/fstring.h')
-rw-r--r-- | src/old/fstring.h | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/src/old/fstring.h b/src/old/fstring.h new file mode 100644 index 0000000..c5397cc --- /dev/null +++ b/src/old/fstring.h | |||
@@ -0,0 +1,651 @@ | |||
1 | #ifndef F_STRING_H | ||
2 | #define F_STRING_H | ||
3 | |||
4 | #include <stdint.h> | ||
5 | #include <memory> | ||
6 | #include "serializable.h" | ||
7 | #include "serializer.h" | ||
8 | |||
9 | template< typename chr > | ||
10 | struct FStringChunk | ||
11 | { | ||
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 | */ | ||
28 | template< typename chr, typename chralloc=std::allocator<chr>, typename chunkalloc=std::allocator<struct FStringChunk<chr> > > | ||
29 | class FBasicString : public Serializable | ||
30 | { | ||
31 | #ifndef VALTEST | ||
32 | #define cpy( dest, src, size ) memcpy( dest, src, size*sizeof(chr) ) | ||
33 | #endif | ||
34 | private: | ||
35 | typedef struct FStringChunk<chr> Chunk; | ||
36 | typedef struct FBasicString<chr, chralloc, chunkalloc> MyType; | ||
37 | |||
38 | public: | ||
39 | FBasicString() : | ||
40 | nLength( 0 ), | ||
41 | pnRefs( NULL ), | ||
42 | pFirst( NULL ), | ||
43 | pLast( NULL ) | ||
44 | { | ||
45 | } | ||
46 | |||
47 | FBasicString( const chr *pData ) : | ||
48 | nLength( 0 ), | ||
49 | pnRefs( NULL ), | ||
50 | pFirst( NULL ), | ||
51 | pLast( NULL ) | ||
52 | { | ||
53 | append( pData ); | ||
54 | } | ||
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 | |||
106 | virtual ~FBasicString() | ||
107 | { | ||
108 | clear(); | ||
109 | } | ||
110 | |||
111 | void append( const chr *pData ) | ||
112 | { | ||
113 | long nLen; | ||
114 | for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); | ||
115 | |||
116 | Chunk *pNew = newChunk( nLen ); | ||
117 | cpy( pNew->pData, pData, nLen ); | ||
118 | |||
119 | appendChunk( pNew ); | ||
120 | } | ||
121 | |||
122 | void append( const chr *pData, long nLen ) | ||
123 | { | ||
124 | Chunk *pNew = newChunk( nLen ); | ||
125 | |||
126 | cpy( pNew->pData, pData, nLen ); | ||
127 | |||
128 | appendChunk( pNew ); | ||
129 | } | ||
130 | |||
131 | void prepend( const chr *pData ) | ||
132 | { | ||
133 | long nLen; | ||
134 | for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); | ||
135 | |||
136 | Chunk *pNew = newChunk( nLen ); | ||
137 | cpy( pNew->pData, pData, nLen ); | ||
138 | |||
139 | prependChunk( pNew ); | ||
140 | } | ||
141 | |||
142 | void prepend( const chr *pData, long nLen ) | ||
143 | { | ||
144 | Chunk *pNew = newChunk( nLen ); | ||
145 | |||
146 | cpy( pNew->pData, pData, nLen ); | ||
147 | |||
148 | prependChunk( pNew ); | ||
149 | } | ||
150 | |||
151 | void clear() | ||
152 | { | ||
153 | realClear(); | ||
154 | } | ||
155 | |||
156 | void resize( long nNewSize ) | ||
157 | { | ||
158 | if( nLength == nNewSize ) | ||
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 | |||
192 | flatten(); | ||
193 | return pFirst->pData; | ||
194 | } | ||
195 | |||
196 | chr *c_str() | ||
197 | { | ||
198 | if( pFirst == NULL ) | ||
199 | return NULL; | ||
200 | |||
201 | flatten(); | ||
202 | return pFirst->pData; | ||
203 | } | ||
204 | |||
205 | const chr *c_str() const | ||
206 | { | ||
207 | if( pFirst == NULL ) | ||
208 | return NULL; | ||
209 | |||
210 | flatten(); | ||
211 | return pFirst->pData; | ||
212 | } | ||
213 | |||
214 | MyType &operator +=( const chr *pData ) | ||
215 | { | ||
216 | append( pData ); | ||
217 | |||
218 | return (*this); | ||
219 | } | ||
220 | |||
221 | MyType &operator +=( const MyType &rSrc ) | ||
222 | { | ||
223 | rSrc.flatten(); | ||
224 | append( rSrc.pFirst->pData, rSrc.nLength ); | ||
225 | |||
226 | return (*this); | ||
227 | } | ||
228 | |||
229 | MyType &operator +=( const chr pData ) | ||
230 | { | ||
231 | chr tmp[2] = { pData, (chr)0 }; | ||
232 | append( tmp ); | ||
233 | |||
234 | return (*this); | ||
235 | } | ||
236 | |||
237 | MyType &operator =( const chr *pData ) | ||
238 | { | ||
239 | clear(); | ||
240 | append( pData ); | ||
241 | |||
242 | return (*this); | ||
243 | } | ||
244 | |||
245 | MyType &operator =( const MyType &rSrc ) | ||
246 | { | ||
247 | //if( rSrc.isFlat() ) | ||
248 | //{ | ||
249 | joinShare( rSrc ); | ||
250 | //} | ||
251 | //else | ||
252 | //{ | ||
253 | // copyFrom( rSrc ); | ||
254 | //} | ||
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 | } | ||
267 | |||
268 | flatten(); | ||
269 | const chr *a = pData; | ||
270 | chr *b = pFirst->pData; | ||
271 | for( ; *a!=(chr)0; a++, b++ ) | ||
272 | { | ||
273 | if( *a != *b ) | ||
274 | return false; | ||
275 | } | ||
276 | |||
277 | return true; | ||
278 | } | ||
279 | |||
280 | bool operator ==( const MyType &pData ) const | ||
281 | { | ||
282 | if( pFirst == pData.pFirst ) | ||
283 | return true; | ||
284 | if( pFirst == NULL ) | ||
285 | return false; | ||
286 | |||
287 | flatten(); | ||
288 | pData.flatten(); | ||
289 | const chr *a = pData.pFirst->pData; | ||
290 | chr *b = pFirst->pData; | ||
291 | for( ; *a!=(chr)0; a++, b++ ) | ||
292 | { | ||
293 | if( *a != *b ) | ||
294 | return false; | ||
295 | } | ||
296 | |||
297 | return true; | ||
298 | } | ||
299 | |||
300 | bool operator !=(const chr *pData ) const | ||
301 | { | ||
302 | return !(*this == pData); | ||
303 | } | ||
304 | |||
305 | bool operator !=(const MyType &pData ) const | ||
306 | { | ||
307 | return !(*this == pData); | ||
308 | } | ||
309 | |||
310 | chr &operator[]( long nIndex ) | ||
311 | { | ||
312 | flatten(); | ||
313 | |||
314 | return pFirst->pData[nIndex]; | ||
315 | } | ||
316 | |||
317 | const chr &operator[]( long nIndex ) const | ||
318 | { | ||
319 | flatten(); | ||
320 | |||
321 | return pFirst->pData[nIndex]; | ||
322 | } | ||
323 | |||
324 | bool isWS( long nIndex ) const | ||
325 | { | ||
326 | flatten(); | ||
327 | |||
328 | return pFirst->pData[nIndex]==' ' || pFirst->pData[nIndex]=='\t' | ||
329 | || pFirst->pData[nIndex]=='\r' || pFirst->pData[nIndex]=='\n'; | ||
330 | } | ||
331 | |||
332 | bool isAlpha( long nIndex ) const | ||
333 | { | ||
334 | flatten(); | ||
335 | |||
336 | return (pFirst->pData[nIndex] >= 'a' && pFirst->pData[nIndex] <= 'z') | ||
337 | || (pFirst->pData[nIndex] >= 'A' && pFirst->pData[nIndex] <= 'Z'); | ||
338 | } | ||
339 | |||
340 | void toLower() | ||
341 | { | ||
342 | flatten(); | ||
343 | unShare(); | ||
344 | |||
345 | for( long j = 0; j < nLength; j++ ) | ||
346 | { | ||
347 | if( pFirst->pData[j] >= 'A' && pFirst->pData[j] <= 'Z' ) | ||
348 | pFirst->pData[j] -= 'A'-'a'; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | void toUpper() | ||
353 | { | ||
354 | flatten(); | ||
355 | unShare(); | ||
356 | |||
357 | for( long j = 0; j < nLength; j++ ) | ||
358 | { | ||
359 | if( pFirst->pData[j] >= 'a' && pFirst->pData[j] <= 'z' ) | ||
360 | pFirst->pData[j] += 'A'-'a'; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | void serialize( class Serializer &ar ) | ||
365 | { | ||
366 | if( ar.isLoading() ) | ||
367 | { | ||
368 | clear(); | ||
369 | long nLen; | ||
370 | ar >> nLen; | ||
371 | |||
372 | Chunk *pNew = newChunk( nLen ); | ||
373 | ar.read( pNew->pData, nLen*sizeof(chr) ); | ||
374 | appendChunk( pNew ); | ||
375 | } | ||
376 | else | ||
377 | { | ||
378 | flatten(); | ||
379 | |||
380 | ar << nLength; | ||
381 | ar.write( pFirst->pData, nLength*sizeof(chr) ); | ||
382 | } | ||
383 | } | ||
384 | |||
385 | private: | ||
386 | void flatten() const | ||
387 | { | ||
388 | if( isFlat() ) | ||
389 | return; | ||
390 | |||
391 | if( pFirst == NULL ) | ||
392 | return; | ||
393 | |||
394 | unShare(); | ||
395 | |||
396 | Chunk *pNew = newChunk( nLength ); | ||
397 | chr *pos = pNew->pData; | ||
398 | Chunk *i = pFirst; | ||
399 | for(;;) | ||
400 | { | ||
401 | cpy( pos, i->pData, i->nLength ); | ||
402 | pos += i->nLength; | ||
403 | i = i->pNext; | ||
404 | if( i == NULL ) | ||
405 | break; | ||
406 | } | ||
407 | realClear(); | ||
408 | |||
409 | pLast = pFirst = pNew; | ||
410 | nLength = pNew->nLength; | ||
411 | } | ||
412 | |||
413 | void realClear() const | ||
414 | { | ||
415 | if( pFirst == NULL ) | ||
416 | return; | ||
417 | |||
418 | if( isShared() ) | ||
419 | { | ||
420 | decRefs(); | ||
421 | } | ||
422 | else | ||
423 | { | ||
424 | Chunk *i = pFirst; | ||
425 | for(;;) | ||
426 | { | ||
427 | Chunk *n = i->pNext; | ||
428 | aChr.deallocate( i->pData, i->nLength+1 ); | ||
429 | aChunk.deallocate( i, 1 ); | ||
430 | if( n == NULL ) | ||
431 | break; | ||
432 | i = n; | ||
433 | } | ||
434 | pFirst = pLast = NULL; | ||
435 | nLength = 0; | ||
436 | } | ||
437 | } | ||
438 | |||
439 | void copyFrom( const FBasicString<chr, chralloc, chunkalloc> &rSrc ) | ||
440 | { | ||
441 | if( rSrc.pFirst == NULL ) | ||
442 | return; | ||
443 | |||
444 | decRefs(); | ||
445 | |||
446 | Chunk *pNew = newChunk( rSrc.nLength ); | ||
447 | chr *pos = pNew->pData; | ||
448 | Chunk *i = rSrc.pFirst; | ||
449 | for(;;) | ||
450 | { | ||
451 | cpy( pos, i->pData, i->nLength ); | ||
452 | pos += i->nLength; | ||
453 | i = i->pNext; | ||
454 | if( i == NULL ) | ||
455 | break; | ||
456 | } | ||
457 | clear(); | ||
458 | |||
459 | appendChunk( pNew ); | ||
460 | } | ||
461 | |||
462 | bool isFlat() const | ||
463 | { | ||
464 | return (pFirst == pLast); | ||
465 | } | ||
466 | |||
467 | bool isShared() const | ||
468 | { | ||
469 | return (pnRefs != NULL); | ||
470 | } | ||
471 | |||
472 | Chunk *newChunk() const | ||
473 | { | ||
474 | Chunk *pNew = aChunk.allocate( 1 ); | ||
475 | pNew->pNext = NULL; | ||
476 | return pNew; | ||
477 | } | ||
478 | |||
479 | Chunk *newChunk( long nLen ) const | ||
480 | { | ||
481 | Chunk *pNew = aChunk.allocate( 1 ); | ||
482 | pNew->pNext = NULL; | ||
483 | pNew->nLength = nLen; | ||
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 | |||
493 | if( pFirst == NULL ) | ||
494 | pLast = pFirst = pNewChunk; | ||
495 | else | ||
496 | { | ||
497 | pLast->pNext = pNewChunk; | ||
498 | pLast = pNewChunk; | ||
499 | } | ||
500 | |||
501 | nLength += pNewChunk->nLength; | ||
502 | } | ||
503 | |||
504 | void prependChunk( Chunk *pNewChunk ) | ||
505 | { | ||
506 | unShare(); | ||
507 | |||
508 | if( pFirst == NULL ) | ||
509 | pLast = pFirst = pNewChunk; | ||
510 | else | ||
511 | { | ||
512 | pNewChunk->pNext = pFirst; | ||
513 | pFirst = pNewChunk; | ||
514 | } | ||
515 | |||
516 | nLength += pNewChunk->nLength; | ||
517 | } | ||
518 | |||
519 | void joinShare( MyType &rSrc ) | ||
520 | { | ||
521 | clear(); | ||
522 | |||
523 | if( !rSrc.isFlat() ) | ||
524 | rSrc.flatten(); | ||
525 | |||
526 | rSrc.initCount(); | ||
527 | pnRefs = rSrc.pnRefs; | ||
528 | (*pnRefs)++; | ||
529 | nLength = rSrc.nLength; | ||
530 | pFirst = rSrc.pFirst; | ||
531 | pLast = rSrc.pLast; | ||
532 | } | ||
533 | |||
534 | void joinShare( const MyType &rSrc ) | ||
535 | { | ||
536 | clear(); | ||
537 | |||
538 | rSrc.flatten(); | ||
539 | |||
540 | if( !rSrc.isShared() ) | ||
541 | { | ||
542 | rSrc.pnRefs = new uint32_t; | ||
543 | (*rSrc.pnRefs) = 1; | ||
544 | } | ||
545 | pnRefs = rSrc.pnRefs; | ||
546 | (*pnRefs)++; | ||
547 | nLength = rSrc.nLength; | ||
548 | pFirst = rSrc.pFirst; | ||
549 | pLast = rSrc.pLast; | ||
550 | } | ||
551 | |||
552 | /** | ||
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 | |||
562 | Chunk *pNew = newChunk( nLength ); | ||
563 | chr *pos = pNew->pData; | ||
564 | Chunk *i = pFirst; | ||
565 | for(;;) | ||
566 | { | ||
567 | cpy( pos, i->pData, i->nLength ); | ||
568 | pos += i->nLength; | ||
569 | i = i->pNext; | ||
570 | if( i == NULL ) | ||
571 | break; | ||
572 | } | ||
573 | decRefs(); | ||
574 | pLast = pFirst = pNew; | ||
575 | nLength = pNew->nLength; | ||
576 | } | ||
577 | |||
578 | /** | ||
579 | * 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 | ||
581 | * safe to call on it's own, it's much better to call unShare. | ||
582 | */ | ||
583 | void decRefs() const | ||
584 | { | ||
585 | if( isShared() ) | ||
586 | { | ||
587 | (*pnRefs)--; | ||
588 | if( (*pnRefs) == 0 ) | ||
589 | destroyShare(); | ||
590 | else | ||
591 | { | ||
592 | pnRefs = NULL; | ||
593 | pFirst = NULL; | ||
594 | pLast = NULL; | ||
595 | nLength = 0; | ||
596 | } | ||
597 | } | ||
598 | } | ||
599 | |||
600 | /** | ||
601 | * While the unShare function removes an instance from a share, this | ||
602 | * 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 | ||
604 | * or is about to reach zero. | ||
605 | */ | ||
606 | void destroyShare() const | ||
607 | { | ||
608 | delete pnRefs; | ||
609 | pnRefs = NULL; | ||
610 | realClear(); | ||
611 | } | ||
612 | |||
613 | #ifdef VALTEST | ||
614 | void cpy( chr *dest, const chr *src, long count ) const | ||
615 | { | ||
616 | for( int j = 0; j < count; j++ ) | ||
617 | { | ||
618 | *dest = *src; | ||
619 | dest++; | ||
620 | src++; | ||
621 | } | ||
622 | } | ||
623 | #endif | ||
624 | |||
625 | void initCount() const | ||
626 | { | ||
627 | if( !isShared() ) | ||
628 | { | ||
629 | pnRefs = new uint32_t; | ||
630 | (*pnRefs) = 1; | ||
631 | } | ||
632 | } | ||
633 | |||
634 | private: | ||
635 | mutable long nLength; | ||
636 | mutable uint32_t *pnRefs; | ||
637 | mutable Chunk *pFirst; | ||
638 | mutable Chunk *pLast; | ||
639 | |||
640 | mutable chralloc aChr; | ||
641 | mutable chunkalloc aChunk; | ||
642 | }; | ||
643 | |||
644 | typedef FBasicString<char> FString; | ||
645 | |||
646 | #include "hash.h" | ||
647 | template<> uint32_t __calcHashCode<FString>( const FString &k ); | ||
648 | template<> bool __cmpHashKeys<FString>( const FString &a, const FString &b ); | ||
649 | |||
650 | |||
651 | #endif | ||