aboutsummaryrefslogtreecommitdiff
path: root/src/fbasicstring.h
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2009-01-07 15:59:57 +0000
committerMike Buland <eichlan@xagasoft.com>2009-01-07 15:59:57 +0000
commit45e065bc4fc93731ea9a0543462bc7cf9e6084d7 (patch)
treea9e8279fe00b9b01dc2393f59dc7f41b5e416b2a /src/fbasicstring.h
parentd96fe229e79f9b1947cbd24ff52d6bf7bb9bf80d (diff)
downloadlibbu++-45e065bc4fc93731ea9a0543462bc7cf9e6084d7.tar.gz
libbu++-45e065bc4fc93731ea9a0543462bc7cf9e6084d7.tar.bz2
libbu++-45e065bc4fc93731ea9a0543462bc7cf9e6084d7.tar.xz
libbu++-45e065bc4fc93731ea9a0543462bc7cf9e6084d7.zip
Only two real changes. First, Bu::FString and Bu::FBasicString are in different
files. This won't affect any programs at all anywhere. This will just make it easier to maintain and extend later. You still want to include "bu/fstring.h" and use Bu::FString in code. The other is kinda fun. I created a special format for unit tests, they use the extension .unit now and use the mkunit.sh script to convert them to c++ code. There are some nice features here too, maintaining unit tests is much, much easier, and we can have more features without making the code any harder to use. Also, it will be easier to have the unit tests generate reports and be run from a master program and the like.
Diffstat (limited to '')
-rw-r--r--src/fbasicstring.h1132
1 files changed, 1132 insertions, 0 deletions
diff --git a/src/fbasicstring.h b/src/fbasicstring.h
new file mode 100644
index 0000000..669784b
--- /dev/null
+++ b/src/fbasicstring.h
@@ -0,0 +1,1132 @@
1/*
2 * Copyright (C) 2007-2008 Xagasoft, All rights reserved.
3 *
4 * This file is part of the libbu++ library and is released under the
5 * terms of the license contained in the file LICENSE.
6 */
7
8#ifndef BU_F_BASIC_STRING_H
9#define BU_F_BASIC_STRING_H
10
11#include <stdint.h>
12#include <memory>
13
14#ifndef WIN32
15#include <wordexp.h>
16#endif
17
18#include "bu/archival.h"
19#include "bu/archive.h"
20#include "bu/util.h"
21
22namespace Bu
23{
24 template< typename chr >
25 struct FStringChunk
26 {
27 long nLength;
28 chr *pData;
29 FStringChunk *pNext;
30 };
31 /**
32 * Flexible String class. This class was designed with string passing and
33 * generation in mind. Like the standard string class you can specify what
34 * datatype to use for each character. Unlike the standard string class,
35 * collection of appended and prepended terms is done lazily, making long
36 * operations that involve many appends very inexpensive. In addition
37 * internal ref-counting means that if you pass strings around between
38 * functions there's almost no overhead in time or memory since a reference
39 * is created and no data is actually copied. This also means that you
40 * never need to put any FBasicString into a ref-counting container class.
41 *
42 *@param chr (typename) Type of character (i.e. char)
43 *@param nMinSize (int) Chunk size (default: 256)
44 *@param chralloc (typename) Memory Allocator for chr
45 *@param chunkalloc (typename) Memory Allocator for chr chunks
46 */
47 template< typename chr, int nMinSize=256, typename chralloc=std::allocator<chr>, typename chunkalloc=std::allocator<struct FStringChunk<chr> > >
48 class FBasicString : public Archival
49 {
50#ifndef VALTEST
51#define cpy( dest, src, size ) memcpy( dest, src, size*sizeof(chr) )
52#endif
53 private:
54 //template< typename chr >
55 /* struct Chunk
56 {
57 long nLength;
58 chr *pData;
59 FChunk *pNext;
60 }; */
61
62 typedef struct FStringChunk<chr> Chunk;
63 typedef struct FBasicString<chr, nMinSize, chralloc, chunkalloc> MyType;
64
65 public:
66 FBasicString() :
67 nLength( 0 ),
68 pFirst( NULL ),
69 pLast( NULL )
70 {
71 }
72
73 FBasicString( const chr *pData ) :
74 nLength( 0 ),
75 pFirst( NULL ),
76 pLast( NULL )
77 {
78 append( pData );
79 }
80
81 FBasicString( const chr *pData, long nLength ) :
82 nLength( 0 ),
83 pFirst( NULL ),
84 pLast( NULL )
85 {
86 append( pData, nLength );
87 }
88
89 FBasicString( const MyType &rSrc ) :
90 Archival(),
91 nLength( 0 ),
92 pFirst( NULL ),
93 pLast( NULL )
94 {
95 if( rSrc.nLength > 0 )
96 {
97 rSrc.flatten();
98 append( rSrc.pFirst->pData, rSrc.nLength );
99 }
100 }
101
102 FBasicString( const MyType &rSrc, long nLength ) :
103 nLength( 0 ),
104 pFirst( NULL ),
105 pLast( NULL )
106 {
107 append( rSrc.pFirst->pData, nLength );
108 }
109
110 FBasicString( const MyType &rSrc, long nStart, long nLength ) :
111 nLength( 0 ),
112 pFirst( NULL ),
113 pLast( NULL )
114 {
115 append( rSrc.pFirst->pData+nStart, nLength );
116 }
117
118 FBasicString( long nSize ) :
119 nLength( nSize ),
120 pFirst( NULL ),
121 pLast( NULL )
122 {
123 pFirst = pLast = newChunk( nSize );
124 }
125
126 virtual ~FBasicString()
127 {
128 clear();
129 }
130
131 /**
132 *@todo void append( const MyType & sData )
133 */
134
135 /**
136 * Append data to your string.
137 *@param pData (const chr *) The data to append.
138 */
139 void append( const chr *pData )
140 {
141 if( !pData ) return;
142 long nLen;
143 for( nLen = 0; pData[nLen] != (chr)0; nLen++ ) { }
144 if( nLen == 0 )
145 return;
146
147 Chunk *pNew = newChunk( nLen );
148 cpy( pNew->pData, pData, nLen );
149
150 appendChunk( pNew );
151 }
152
153 /**
154 * Append data to your string.
155 *@param pData (const chr *) The data to append.
156 *@param nLen (long) The length of the data to append.
157 */
158 void append( const chr *pData, long nLen )
159 {
160 if( nLen == 0 )
161 return;
162
163 Chunk *pNew = newChunk( nLen );
164
165 cpy( pNew->pData, pData, nLen );
166
167 appendChunk( pNew );
168 }
169
170 /**
171 * Append a single chr to your string.
172 *@param cData (const chr &) The character to append.
173 */
174 void append( const chr &cData )
175 {
176 if( pLast && pLast->nLength < nMinSize )
177 {
178 pLast->pData[pLast->nLength] = cData;
179 ++pLast->nLength; ++nLength;
180 // pLast->pData[pLast->nLength] = (chr)0;
181 }
182 else
183 {
184 append( &cData, 1 );
185 }
186 }
187
188 /**
189 * Append another FString to this one.
190 *@param sData (MyType &) The FString to append.
191 */
192 void append( const MyType & sData )
193 {
194 append( sData.getStr(), sData.getSize() );
195 }
196
197 /**
198 * Append another FString to this one.
199 *@param sData (MyType &) The FString to append.
200 *@param nLen How much data to append.
201 */
202 void append( const MyType & sData, long nLen )
203 {
204 append( sData.getStr(), nLen );
205 }
206
207 /**
208 * Prepend another FString to this one.
209 *@param sData (MyType &) The FString to prepend.
210 */
211 void prepend( const MyType & sData )
212 {
213 prepend( sData.getStr(), sData.getSize() );
214 }
215
216 /**
217 * Prepend data to your string.
218 *@param pData (const chr *) The data to prepend.
219 */
220 void prepend( const chr *pData )
221 {
222 if( pData == NULL )
223 return;
224 long nLen;
225 for( nLen = 0; pData[nLen] != (chr)0; nLen++ ) { }
226
227 Chunk *pNew = newChunk( nLen );
228 cpy( pNew->pData, pData, nLen );
229
230 prependChunk( pNew );
231 }
232
233 /**
234 * Prepend data to your string.
235 *@param pData (const chr *) The data to prepend.
236 *@param nLen (long) The length of the data to prepend.
237 */
238 void prepend( const chr *pData, long nLen )
239 {
240 Chunk *pNew = newChunk( nLen );
241
242 cpy( pNew->pData, pData, nLen );
243
244 prependChunk( pNew );
245 }
246
247 void insert( long nPos, const chr *pData, long nLen )
248 {
249 if( nLen <= 0 )
250 return;
251 if( nPos <= 0 )
252 {
253 prepend( pData, nLen );
254 }
255 else if( nPos >= nLength )
256 {
257 append( pData, nLen );
258 }
259 else
260 {
261 flatten();
262 Chunk *p1 = newChunk( nPos );
263 Chunk *p2 = newChunk( nLen );
264 Chunk *p3 = newChunk( nLength-nPos );
265 cpy( p1->pData, pFirst->pData, nPos );
266 cpy( p2->pData, pData, nLen );
267 cpy( p3->pData, pFirst->pData+nPos, nLength-nPos );
268 clear();
269 appendChunk( p1 );
270 appendChunk( p2 );
271 appendChunk( p3 );
272 }
273 }
274
275 void insert( long nPos, const MyType &str )
276 {
277 if( nPos <= 0 )
278 {
279 prepend( str );
280 }
281 else if( nPos >= nLength )
282 {
283 append( str );
284 }
285 else
286 {
287 flatten();
288 Chunk *p1 = newChunk( nPos );
289 Chunk *p3 = newChunk( nLength-nPos );
290 cpy( p1->pData, pFirst->pData, nPos );
291 cpy( p3->pData, pFirst->pData+nPos, nLength-nPos );
292 clear();
293 appendChunk( p1 );
294 for( Chunk *pChnk = str.pFirst; pChnk; pChnk = pChnk->pNext )
295 {
296 appendChunk( copyChunk( pChnk ) );
297 }
298
299 appendChunk( p3 );
300 }
301 }
302
303 void insert( long nPos, const chr *pData )
304 {
305 insert( nPos, pData, strlen( pData ) );
306 }
307
308 void remove( long nPos, long nLen )
309 {
310 if( nLen <= 0 || nPos < 0 || nPos >= nLength )
311 return;
312 if( nLen > nLength-nPos )
313 nLen = nLength-nPos;
314 flatten();
315 cpy( pFirst->pData+nPos, pFirst->pData+nPos+nLen, nLength-nPos-nLen+1 );
316 nLength -= nLen;
317 pFirst->nLength -= nLen;
318 }
319
320 /**
321 *@todo void prepend( const chr &cData )
322 */
323
324 /**
325 * Clear all data from the string.
326 */
327 void clear()
328 {
329 realClear();
330 }
331
332 /**
333 * Force the string to resize
334 *@param nNewSize (long) The new size of the string.
335 */
336 void resize( long nNewSize )
337 {
338 if( nLength == nNewSize )
339 return;
340 if( nNewSize < 0 )
341 nNewSize = 0;
342
343 flatten();
344
345 Chunk *pNew = newChunk( nNewSize );
346 long nNewLen = (nNewSize<nLength)?(nNewSize):(nLength);
347 if( nLength > 0 )
348 {
349 cpy( pNew->pData, pFirst->pData, nNewLen );
350 aChr.deallocate( pFirst->pData, pFirst->nLength+1 );
351 aChunk.deallocate( pFirst, 1 );
352 }
353 pNew->pData[nNewLen] = (chr)0;
354 pFirst = pLast = pNew;
355 nLength = nNewSize;
356 }
357
358 /**
359 * Get the current size of the string.
360 *@returns (long) The current size of the string.
361 */
362 long getSize() const
363 {
364 return nLength;
365 }
366
367 /**
368 * Get a pointer to the string array.
369 *@returns (chr *) The string data.
370 */
371 chr *getStr()
372 {
373 if( pFirst == NULL )
374 return (chr *)"";
375
376 flatten();
377 pFirst->pData[nLength] = (chr)0;
378 return pFirst->pData;
379 }
380
381 /**
382 * Get a const pointer to the string array.
383 *@returns (const chr *) The string data.
384 */
385 const chr *getStr() const
386 {
387 if( pFirst == NULL )
388 return (chr *)"";
389
390 flatten();
391 pFirst->pData[nLength] = (chr)0;
392 return pFirst->pData;
393 }
394
395 MyType getSubStr( long iStart, long iSize=-1 ) const
396 {
397 if( iStart < 0 )
398 iStart = 0;
399 if( iStart >= nLength )
400 return "";
401 if( iSize < 0 )
402 iSize = nLength;
403 if( iStart+iSize > nLength )
404 iSize = nLength-iStart;
405 if( iSize == 0 )
406 return "";
407
408 flatten();
409 MyType ret( pFirst->pData+iStart, iSize );
410 return ret;
411 }
412
413 /**
414 * (std::string compatability) Get a pointer to the string array.
415 *@returns (chr *) The string data.
416 */
417 DEPRECATED
418 chr *c_str()
419 {
420 if( pFirst == NULL )
421 return NULL;
422
423 flatten();
424 pFirst->pData[nLength] = (chr)0;
425 return pFirst->pData;
426 }
427
428 /**
429 * (std::string compatability) Get a const pointer to the string array.
430 *@returns (const chr *) The string data.
431 */
432 DEPRECATED
433 const chr *c_str() const
434 {
435 if( pFirst == NULL )
436 return NULL;
437
438 flatten();
439 pFirst->pData[nLength] = (chr)0;
440 return pFirst->pData;
441 }
442
443 /**
444 * Plus equals operator for FString.
445 *@param pData (const chr *) The data to append to your FString.
446 */
447 MyType &operator +=( const chr *pData )
448 {
449 append( pData );
450
451 return (*this);
452 }
453
454 /**
455 * Plus equals operator for FString.
456 *@param pData (const MyType &) The FString to append to your FString.
457 */
458 MyType &operator +=( const MyType &rSrc )
459 {
460 if( rSrc.nLength == 0 )
461 return (*this);
462 rSrc.flatten();
463 append( rSrc.pFirst->pData, rSrc.nLength );
464
465 return (*this);
466 }
467
468 /**
469 * Plus equals operator for FString.
470 *@param pData (const chr) The character to append to your FString.
471 */
472 MyType &operator +=( const chr cData )
473 {
474 if( pLast && pLast->nLength < nMinSize )
475 {
476 pLast->pData[pLast->nLength] = cData;
477 ++pLast->nLength; ++nLength;
478 // pLast->pData[pLast->nLength] = (chr)0;
479 }
480 else
481 {
482 append( &cData, 1 );
483 }
484 //append( pData );
485
486 return (*this);
487 }
488
489 /**
490 * Assignment operator.
491 *@param pData (const chr *) The character array to append to your
492 * FString.
493 */
494 MyType &operator =( const chr *pData )
495 {
496 clear();
497 append( pData );
498
499 return (*this);
500 }
501
502 MyType &operator =( const std::basic_string<chr> &rData )
503 {
504 clear();
505 append( rData.c_str(), rData.size() );
506
507 return (*this);
508 }
509
510 MyType operator +( const MyType &rRight )
511 {
512 MyType ret( *this );
513 ret.append( rRight );
514 return ret;
515 }
516
517 MyType operator +( const chr *pRight )
518 {
519 MyType ret( *this );
520 ret.append( pRight );
521 return ret;
522 }
523
524 MyType operator +( chr *pRight )
525 {
526 MyType ret( *this );
527 ret.append( pRight );
528 return ret;
529 }
530
531 /**
532 * Reset your FString to this character array.
533 *@param pData (const chr *) The character array to set your FString to.
534 */
535 void set( const chr *pData )
536 {
537 clear();
538 append( pData );
539 }
540
541 /**
542 * Reset your FString to this character array.
543 *@param pData (const chr *) The character array to set your FString to.
544 *@param nSize (long) The length of the inputted character array.
545 */
546 void set( const chr *pData, long nSize )
547 {
548 clear();
549 append( pData, nSize );
550 }
551
552 void expand()
553 {
554 flatten();
555
556#ifndef WIN32
557 wordexp_t result;
558
559 /* Expand the string for the program to run. */
560 switch (wordexp (pFirst->pData, &result, 0))
561 {
562 case 0: /* Successful. */
563 {
564 set( result.we_wordv[0] );
565 wordfree( &result );
566 return;
567 }
568 break;
569 case WRDE_NOSPACE:
570 /* If the error was `WRDE_NOSPACE',
571 then perhaps part of the result was allocated. */
572 wordfree (&result);
573 default: /* Some other error. */
574 return;
575 }
576#endif
577 }
578
579 /**
580 * Assignment operator.
581 *@param rSrc (const MyType &) The FString to set your FString to.
582 */
583 MyType &operator =( const MyType &rSrc )
584 {
585 copyFrom( rSrc );
586
587 return (*this);
588 }
589
590 /**
591 * Equals comparison operator.
592 *@param pData (const chr *) The character array to compare your FString
593 * to.
594 */
595 bool operator ==( const chr *pData ) const
596 {
597 if( pFirst == NULL ) {
598 if( pData == NULL )
599 return true;
600 if( pData[0] == (chr)0 )
601 return true;
602 return false;
603 }
604
605 flatten();
606 pFirst->pData[nLength] = (chr)0;
607 const chr *a = pData;
608 chr *b = pFirst->pData;
609 for( long j = 0; *a!=(chr)0 || *b!=(chr)0; j++, a++, b++ )
610 {
611 if( *a != *b )
612 return false;
613 if( *a == (chr)0 && j < nLength )
614 return false;
615 }
616
617 return true;
618 }
619
620 /**
621 * Equals comparison operator.
622 *@param pData (const MyType &) The FString to compare your FString to.
623 */
624 bool operator ==( const MyType &pData ) const
625 {
626 if( pFirst == pData.pFirst )
627 return true;
628 if( pFirst == NULL )
629 return false;
630 if( nLength != pData.nLength )
631 return false;
632
633 flatten();
634 pData.flatten();
635 const chr *a = pData.pFirst->pData;
636 chr *b = pFirst->pData;
637 for( long j = 0; j < nLength; j++, a++, b++ )
638 {
639 if( *a != *b )
640 return false;
641 }
642
643 return true;
644 }
645
646 /**
647 * Not equals comparison operator.
648 *@param pData (const chr *) The character array to compare your FString
649 * to.
650 */
651 bool operator !=(const chr *pData ) const
652 {
653 return !(*this == pData);
654 }
655
656 /**
657 * Not equals comparison operator.
658 *@param pData (const MyType &) The FString to compare your FString to.
659 */
660 bool operator !=(const MyType &pData ) const
661 {
662 return !(*this == pData);
663 }
664
665 /**
666 * Indexing operator
667 *@param nIndex (long) The index of the character you want.
668 *@returns (chr &) The character at position (nIndex).
669 */
670 chr &operator[]( long nIndex )
671 {
672 flatten();
673
674 return pFirst->pData[nIndex];
675 }
676
677 /**
678 * Const indexing operator
679 *@param nIndex (long) The index of the character you want.
680 *@returns (const chr &) The character at position (nIndex).
681 */
682 const chr &operator[]( long nIndex ) const
683 {
684 flatten();
685
686 return pFirst->pData[nIndex];
687 }
688/*
689 operator const chr *() const
690 {
691 if( !pFirst ) return NULL;
692 flatten();
693 return pFirst->pData;
694 }
695 */
696
697 operator bool() const
698 {
699 return (pFirst != NULL);
700 }
701
702 bool isSet() const
703 {
704 return (pFirst != NULL);
705 }
706
707 /**
708 * Is the character at index (nIndex) white space?
709 *@param nIndex (long) The index of the character you want to check.
710 *@returns (bool) Is it white space?
711 */
712 bool isWS( long nIndex ) const
713 {
714 flatten();
715
716 return pFirst->pData[nIndex]==' ' || pFirst->pData[nIndex]=='\t'
717 || pFirst->pData[nIndex]=='\r' || pFirst->pData[nIndex]=='\n';
718 }
719
720 /**
721 * Is the character at index (nIndex) a letter?
722 *@param nIndex (long) The index of the character you want to check.
723 *@returns (bool) Is it a letter?
724 */
725 bool isAlpha( long nIndex ) const
726 {
727 flatten();
728
729 return (pFirst->pData[nIndex] >= 'a' && pFirst->pData[nIndex] <= 'z')
730 || (pFirst->pData[nIndex] >= 'A' && pFirst->pData[nIndex] <= 'Z');
731 }
732
733 /**
734 * Convert your alpha characters to lower case.
735 */
736 void toLower()
737 {
738 flatten();
739
740 for( long j = 0; j < nLength; j++ )
741 {
742 if( pFirst->pData[j] >= 'A' && pFirst->pData[j] <= 'Z' )
743 pFirst->pData[j] -= 'A'-'a';
744 }
745 }
746
747 /**
748 * Convert your alpha characters to upper case.
749 */
750 void toUpper()
751 {
752 flatten();
753
754 for( long j = 0; j < nLength; j++ )
755 {
756 if( pFirst->pData[j] >= 'a' && pFirst->pData[j] <= 'z' )
757 pFirst->pData[j] += 'A'-'a';
758 }
759 }
760
761 /**
762 * Find the index of the first occurrance of (sText)
763 *@param sText (const chr *) The string to search for.
764 *@returns (long) The index of the first occurrance. -1 for not found.
765 */
766 long find( const chr cChar ) const
767 {
768 flatten();
769 for( long j = 0; j < pFirst->nLength; j++ )
770 {
771 if( pFirst->pData[j] == cChar )
772 return j;
773 }
774 return -1;
775 }
776
777 /**
778 * Find the index of the first occurrance of cChar
779 *@param cChar (const chr) The character to search for.
780 *@returns (long) The index of the first occurrance. -1 for not found.
781 */
782 long find( const chr *sText ) const
783 {
784 long nTLen = strlen( sText );
785 flatten();
786 for( long j = 0; j < pFirst->nLength-nTLen; j++ )
787 {
788 if( !strncmp( sText, pFirst->pData+j, nTLen ) )
789 return j;
790 }
791 return -1;
792 }
793
794 /**
795 * Find the index of the first occurrance of cChar
796 *@param sText (const chr *) The string to search for.
797 *@returns (long) The index of the first occurrance. -1 for not found.
798 */
799 long find( long iStart, const chr cChar ) const
800 {
801 flatten();
802 for( long j = iStart; j < pFirst->nLength; j++ )
803 {
804 if( pFirst->pData[j] == cChar )
805 return j;
806 }
807 return -1;
808 }
809
810 /**
811 * Find the index of the first occurrance of sText
812 *@param cChar (const chr) The character to search for.
813 *@returns (long) The index of the first occurrance. -1 for not found.
814 */
815 long find( long iStart, const chr *sText ) const
816 {
817 long nTLen = strlen( sText );
818 flatten();
819 for( long j = iStart; j < pFirst->nLength-nTLen; j++ )
820 {
821 if( !strncmp( sText, pFirst->pData+j, nTLen ) )
822 return j;
823 }
824 return -1;
825 }
826
827 /**
828 * Do a reverse search for (sText)
829 *@param sText (const chr *) The string to search for.
830 *@returns (long) The index of the last occurrance. -1 for not found.
831 */
832 long rfind( const chr *sText ) const
833 {
834 long nTLen = strlen( sText );
835 flatten();
836 for( long j = pFirst->nLength-nTLen-1; j >= 0; j-- )
837 {
838 if( !strncmp( sText, pFirst->pData+j, nTLen ) )
839 return j;
840 }
841 return -1;
842 }
843
844 /**
845 * Remove nAmnt bytes from the front of the string. This function
846 * operates in O(n) time and should be used sparingly.
847 */
848 void trimFront( long nAmnt )
849 {
850 long nNewLen = nLength - nAmnt;
851 flatten();
852 Chunk *pNew = newChunk( nNewLen );
853 cpy( pNew->pData, pFirst->pData+nAmnt, nNewLen );
854 clear();
855 appendChunk( pNew );
856 }
857
858 void format( const char *sFrmt, ...)
859 {
860 clear();
861
862 va_list ap;
863 va_start( ap, sFrmt );
864
865 long iLen = vsnprintf( NULL, 0, sFrmt, ap );
866
867 Chunk *pNew = newChunk( iLen );
868 vsnprintf( pNew->pData, iLen+1, sFrmt, ap );
869 appendChunk( pNew );
870
871 va_end( ap );
872 }
873
874 void formatAppend( const char *sFrmt, ...)
875 {
876 va_list ap;
877 va_start( ap, sFrmt );
878
879 long iLen = vsnprintf( NULL, 0, sFrmt, ap );
880
881 Chunk *pNew = newChunk( iLen );
882 vsnprintf( pNew->pData, iLen+1, sFrmt, ap );
883 appendChunk( pNew );
884
885 va_end( ap );
886 }
887
888 void formatPrepend( const char *sFrmt, ...)
889 {
890 va_list ap;
891 va_start( ap, sFrmt );
892
893 long iLen = vsnprintf( NULL, 0, sFrmt, ap );
894
895 Chunk *pNew = newChunk( iLen );
896 vsnprintf( pNew->pData, iLen+1, sFrmt, ap );
897 prependChunk( pNew );
898
899 va_end( ap );
900 }
901
902 /**
903 * Function the archiver calls to archive your FString.
904 *@param ar (Archive) The archive which is archiving your FString.
905 */
906 void archive( class Archive &ar )
907 {
908 if( ar.isLoading() )
909 {
910 clear();
911 long nLen;
912 ar >> nLen;
913
914 if( nLen > 0 )
915 {
916 Chunk *pNew = newChunk( nLen );
917 ar.read( pNew->pData, nLen*sizeof(chr) );
918 appendChunk( pNew );
919 }
920 }
921 else
922 {
923 flatten();
924
925 ar << nLength;
926 if( nLength )
927 ar.write( pFirst->pData, nLength*sizeof(chr) );
928 }
929 }
930
931 typedef chr *iterator;
932 typedef const chr *const_iterator;
933
934 iterator begin()
935 {
936 if( nLength == 0 )
937 return NULL;
938 flatten();
939 return pFirst->pData;
940 }
941
942 const_iterator begin() const
943 {
944 if( nLength == 0 )
945 return NULL;
946 flatten();
947 return pFirst->pData;
948 }
949
950 iterator end()
951 {
952 if( nLength == 0 )
953 return NULL;
954 return pFirst->pData+pFirst->nLength;
955 }
956
957 const_iterator end() const
958 {
959 if( nLength == 0 )
960 return NULL;
961 return pFirst->pData+pFirst->nLength;
962 }
963
964 bool isEmpty() const
965 {
966 if( nLength == 0 )
967 return true;
968 return false;
969 }
970
971 private:
972 void flatten() const
973 {
974 if( isFlat() )
975 return;
976
977 if( pFirst == NULL )
978 return;
979
980 Chunk *pNew = newChunk( nLength );
981 chr *pos = pNew->pData;
982 Chunk *i = pFirst;
983 for(;;)
984 {
985 cpy( pos, i->pData, i->nLength );
986 pos += i->nLength;
987 i = i->pNext;
988 if( i == NULL )
989 break;
990 }
991 realClear();
992
993 pLast = pFirst = pNew;
994 nLength = pNew->nLength;
995 }
996
997 void realClear() const
998 {
999 if( pFirst == NULL )
1000 return;
1001
1002 Chunk *i = pFirst;
1003 for(;;)
1004 {
1005 Chunk *n = i->pNext;
1006 aChr.deallocate( i->pData, i->nLength+1 );
1007 aChunk.deallocate( i, 1 );
1008 if( n == NULL )
1009 break;
1010 i = n;
1011 }
1012 pFirst = pLast = NULL;
1013 nLength = 0;
1014 }
1015
1016 void copyFrom( const FBasicString<chr, nMinSize, chralloc, chunkalloc> &rSrc )
1017 {
1018 if( rSrc.pFirst == NULL )
1019 {
1020 clear();
1021 return;
1022 }
1023
1024 Chunk *pNew = newChunk( rSrc.nLength );
1025 chr *pos = pNew->pData;
1026 Chunk *i = rSrc.pFirst;
1027 for(;;)
1028 {
1029 cpy( pos, i->pData, i->nLength );
1030 pos += i->nLength;
1031 i = i->pNext;
1032 if( i == NULL )
1033 break;
1034 }
1035 clear();
1036
1037 appendChunk( pNew );
1038 }
1039
1040 bool isFlat() const
1041 {
1042 return (pFirst == pLast);
1043 }
1044
1045 Chunk *newChunk() const
1046 {
1047 Chunk *pNew = aChunk.allocate( 1 );
1048 pNew->pNext = NULL;
1049 return pNew;
1050 }
1051
1052 Chunk *newChunk( long nLen ) const
1053 {
1054 Chunk *pNew = aChunk.allocate( 1 );
1055 pNew->pNext = NULL;
1056 pNew->nLength = nLen;
1057 pNew->pData = aChr.allocate( (nLen<nMinSize)?(nMinSize):(nLen)+1 );
1058 pNew->pData[nLen] = (chr)0;
1059 return pNew;
1060 }
1061
1062 Chunk *copyChunk( Chunk *pSrc ) const
1063 {
1064 Chunk *pNew = aChunk.allocate( 1 );
1065 pNew->pNext = pSrc->pNext;
1066 pNew->nLength = pSrc->nLength;
1067 pNew->pData = aChr.allocate( pSrc->nLength+1 );
1068 cpy( pNew->pData, pSrc->pData, pSrc->nLength );
1069 pNew->pData[pNew->nLength] = (chr)0;
1070 return pNew;
1071 }
1072
1073 void appendChunk( Chunk *pNewChunk )
1074 {
1075 if( pFirst == NULL )
1076 pLast = pFirst = pNewChunk;
1077 else
1078 {
1079 pLast->pNext = pNewChunk;
1080 pLast = pNewChunk;
1081 }
1082
1083 nLength += pNewChunk->nLength;
1084 }
1085
1086 void prependChunk( Chunk *pNewChunk )
1087 {
1088 if( pFirst == NULL )
1089 pLast = pFirst = pNewChunk;
1090 else
1091 {
1092 pNewChunk->pNext = pFirst;
1093 pFirst = pNewChunk;
1094 }
1095
1096 nLength += pNewChunk->nLength;
1097 }
1098
1099#ifdef VALTEST
1100 void cpy( chr *dest, const chr *src, long count ) const
1101 {
1102 for( int j = 0; j < count; j++ )
1103 {
1104 *dest = *src;
1105 dest++;
1106 src++;
1107 }
1108 }
1109#endif
1110
1111 private:
1112 mutable long nLength;
1113 mutable Chunk *pFirst;
1114 mutable Chunk *pLast;
1115
1116 mutable chralloc aChr;
1117 mutable chunkalloc aChunk;
1118 };
1119
1120 template<class T> FBasicString<T> operator +( const T *pLeft, const FBasicString<T> &rRight )
1121 {
1122 Bu::FBasicString<T> ret( pLeft );
1123 ret.append( rRight );
1124 return ret;
1125 }
1126}
1127
1128#ifndef VALTEST
1129#undef cpy
1130#endif
1131
1132#endif