diff options
author | Mike Buland <eichlan@xagasoft.com> | 2007-03-15 05:24:58 +0000 |
---|---|---|
committer | Mike Buland <eichlan@xagasoft.com> | 2007-03-15 05:24:58 +0000 |
commit | cf847cc534a2a7ad06750c446028b7c6d126fe21 (patch) | |
tree | f4de1b9bcc261fe5dc758f13ee0b6d19b7ba5e27 | |
parent | 46725741fc82866e41652dee7adeddf376fde618 (diff) | |
download | libbu++-cf847cc534a2a7ad06750c446028b7c6d126fe21.tar.gz libbu++-cf847cc534a2a7ad06750c446028b7c6d126fe21.tar.bz2 libbu++-cf847cc534a2a7ad06750c446028b7c6d126fe21.tar.xz libbu++-cf847cc534a2a7ad06750c446028b7c6d126fe21.zip |
Looks like FString is ready for general consumption, not too shabby.
-rw-r--r-- | src/fstring.h | 241 | ||||
-rw-r--r-- | src/tests/fstring.cpp | 43 |
2 files changed, 247 insertions, 37 deletions
diff --git a/src/fstring.h b/src/fstring.h index 287641f..db54cdd 100644 --- a/src/fstring.h +++ b/src/fstring.h | |||
@@ -20,6 +20,7 @@ class FBasicString | |||
20 | { | 20 | { |
21 | private: | 21 | private: |
22 | typedef struct FStringChunk<chr> Chunk; | 22 | typedef struct FStringChunk<chr> Chunk; |
23 | typedef struct FBasicString<chr, chralloc, chunkalloc> MyType; | ||
23 | 24 | ||
24 | public: | 25 | public: |
25 | FBasicString() : | 26 | FBasicString() : |
@@ -48,8 +49,31 @@ public: | |||
48 | append( pData, nLength ); | 49 | append( pData, nLength ); |
49 | } | 50 | } |
50 | 51 | ||
52 | FBasicString( MyType &rSrc ) : | ||
53 | nLength( 0 ), | ||
54 | pnRefs( NULL ), | ||
55 | pFirst( NULL ), | ||
56 | pLast( NULL ) | ||
57 | { | ||
58 | joinShare( rSrc ); | ||
59 | } | ||
60 | |||
61 | FBasicString( const FBasicString<chr, chralloc, chunkalloc> &rSrc ) : | ||
62 | nLength( 0 ), | ||
63 | pnRefs( NULL ), | ||
64 | pFirst( NULL ), | ||
65 | pLast( NULL ) | ||
66 | { | ||
67 | // Here we have no choice but to copy, since the other guy is a const. | ||
68 | // In the case that the source were flat, we could get a reference, it | ||
69 | // would make some things faster, but not matter in many other cases. | ||
70 | |||
71 | copyFrom( rSrc ); | ||
72 | } | ||
73 | |||
51 | virtual ~FBasicString() | 74 | virtual ~FBasicString() |
52 | { | 75 | { |
76 | clear(); | ||
53 | } | 77 | } |
54 | 78 | ||
55 | void append( const chr *pData ) | 79 | void append( const chr *pData ) |
@@ -58,7 +82,7 @@ public: | |||
58 | for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); | 82 | for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); |
59 | 83 | ||
60 | Chunk *pNew = newChunk( nLen ); | 84 | Chunk *pNew = newChunk( nLen ); |
61 | memcpy( pNew->pData, pData, nLen * sizeof(chr) ); | 85 | cpy( pNew->pData, pData, nLen ); |
62 | 86 | ||
63 | appendChunk( pNew ); | 87 | appendChunk( pNew ); |
64 | } | 88 | } |
@@ -67,7 +91,7 @@ public: | |||
67 | { | 91 | { |
68 | Chunk *pNew = newChunk( nLen ); | 92 | Chunk *pNew = newChunk( nLen ); |
69 | 93 | ||
70 | memcpy( pNew->pData, pData, nLen * sizeof(chr) ); | 94 | cpy( pNew->pData, pData, nLen ); |
71 | 95 | ||
72 | appendChunk( pNew ); | 96 | appendChunk( pNew ); |
73 | } | 97 | } |
@@ -78,7 +102,7 @@ public: | |||
78 | for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); | 102 | for( nLen = 0; pData[nLen] != (chr)0; nLen++ ); |
79 | 103 | ||
80 | Chunk *pNew = newChunk( nLen ); | 104 | Chunk *pNew = newChunk( nLen ); |
81 | memcpy( pNew->pData, pData, nLen * sizeof(chr) ); | 105 | cpy( pNew->pData, pData, nLen ); |
82 | 106 | ||
83 | prependChunk( pNew ); | 107 | prependChunk( pNew ); |
84 | } | 108 | } |
@@ -87,7 +111,7 @@ public: | |||
87 | { | 111 | { |
88 | Chunk *pNew = newChunk( nLen ); | 112 | Chunk *pNew = newChunk( nLen ); |
89 | 113 | ||
90 | memcpy( pNew->pData, pData, nLen * sizeof(chr) ); | 114 | cpy( pNew->pData, pData, nLen ); |
91 | 115 | ||
92 | prependChunk( pNew ); | 116 | prependChunk( pNew ); |
93 | } | 117 | } |
@@ -97,18 +121,25 @@ public: | |||
97 | if( pFirst == NULL ) | 121 | if( pFirst == NULL ) |
98 | return; | 122 | return; |
99 | 123 | ||
100 | Chunk *i = pFirst; | 124 | if( isShared() ) |
101 | for(;;) | ||
102 | { | 125 | { |
103 | Chunk *n = i->pNext; | 126 | decRefs(); |
104 | aChr.deallocate( i->pData, i->nLength+1 ); | 127 | } |
105 | aChunk.deallocate( i, 1 ); | 128 | else |
106 | if( n == NULL ) | 129 | { |
107 | break; | 130 | Chunk *i = pFirst; |
108 | i = n; | 131 | for(;;) |
132 | { | ||
133 | Chunk *n = i->pNext; | ||
134 | aChr.deallocate( i->pData, i->nLength+1 ); | ||
135 | aChunk.deallocate( i, 1 ); | ||
136 | if( n == NULL ) | ||
137 | break; | ||
138 | i = n; | ||
139 | } | ||
140 | pFirst = pLast = NULL; | ||
141 | nLength = 0; | ||
109 | } | 142 | } |
110 | pFirst = pLast = NULL; | ||
111 | nLength = 0; | ||
112 | } | 143 | } |
113 | 144 | ||
114 | chr *c_str() | 145 | chr *c_str() |
@@ -120,14 +151,14 @@ public: | |||
120 | return pFirst->pData; | 151 | return pFirst->pData; |
121 | } | 152 | } |
122 | 153 | ||
123 | FBasicString<chr, chralloc, chunkalloc> &operator +=( const chr *pData ) | 154 | MyType &operator +=( const chr *pData ) |
124 | { | 155 | { |
125 | append( pData ); | 156 | append( pData ); |
126 | 157 | ||
127 | return (*this); | 158 | return (*this); |
128 | } | 159 | } |
129 | 160 | ||
130 | FBasicString<chr, chralloc, chunkalloc> &operator =( const chr *pData ) | 161 | MyType &operator =( const chr *pData ) |
131 | { | 162 | { |
132 | clear(); | 163 | clear(); |
133 | append( pData ); | 164 | append( pData ); |
@@ -135,6 +166,27 @@ public: | |||
135 | return (*this); | 166 | return (*this); |
136 | } | 167 | } |
137 | 168 | ||
169 | MyType &operator =( const MyType &rSrc ) | ||
170 | { | ||
171 | if( rSrc.isFlat() ) | ||
172 | { | ||
173 | joinShare( rSrc ); | ||
174 | } | ||
175 | else | ||
176 | { | ||
177 | copyFrom( rSrc ); | ||
178 | } | ||
179 | |||
180 | return (*this); | ||
181 | } | ||
182 | |||
183 | MyType &operator =( MyType &rSrc ) | ||
184 | { | ||
185 | joinShare( rSrc ); | ||
186 | |||
187 | return (*this); | ||
188 | } | ||
189 | |||
138 | bool operator ==( const chr *pData ) | 190 | bool operator ==( const chr *pData ) |
139 | { | 191 | { |
140 | if( pFirst == NULL ) { | 192 | if( pFirst == NULL ) { |
@@ -160,7 +212,12 @@ public: | |||
160 | return !(*this == pData); | 212 | return !(*this == pData); |
161 | } | 213 | } |
162 | 214 | ||
215 | chr &operator[]( long nIndex ) | ||
216 | { | ||
217 | flatten(); | ||
163 | 218 | ||
219 | return pFirst->pData[nIndex]; | ||
220 | } | ||
164 | 221 | ||
165 | private: | 222 | private: |
166 | void flatten() | 223 | void flatten() |
@@ -171,12 +228,37 @@ private: | |||
171 | if( pFirst == NULL ) | 228 | if( pFirst == NULL ) |
172 | return; | 229 | return; |
173 | 230 | ||
231 | unShare(); | ||
232 | |||
174 | Chunk *pNew = newChunk( nLength ); | 233 | Chunk *pNew = newChunk( nLength ); |
175 | chr *pos = pNew->pData; | 234 | chr *pos = pNew->pData; |
176 | Chunk *i = pFirst; | 235 | Chunk *i = pFirst; |
177 | for(;;) | 236 | for(;;) |
178 | { | 237 | { |
179 | memcpy( pos, i->pData, i->nLength*sizeof(chr) ); | 238 | cpy( pos, i->pData, i->nLength ); |
239 | pos += i->nLength; | ||
240 | i = i->pNext; | ||
241 | if( i == NULL ) | ||
242 | break; | ||
243 | } | ||
244 | clear(); | ||
245 | |||
246 | appendChunk( pNew ); | ||
247 | } | ||
248 | |||
249 | void copyFrom( const FBasicString<chr, chralloc, chunkalloc> &rSrc ) | ||
250 | { | ||
251 | if( rSrc.pFirst == NULL ) | ||
252 | return; | ||
253 | |||
254 | decRefs(); | ||
255 | |||
256 | Chunk *pNew = newChunk( rSrc.nLength ); | ||
257 | chr *pos = pNew->pData; | ||
258 | Chunk *i = rSrc.pFirst; | ||
259 | for(;;) | ||
260 | { | ||
261 | cpy( pos, i->pData, i->nLength ); | ||
180 | pos += i->nLength; | 262 | pos += i->nLength; |
181 | i = i->pNext; | 263 | i = i->pNext; |
182 | if( i == NULL ) | 264 | if( i == NULL ) |
@@ -187,12 +269,12 @@ private: | |||
187 | appendChunk( pNew ); | 269 | appendChunk( pNew ); |
188 | } | 270 | } |
189 | 271 | ||
190 | bool isFlat() | 272 | bool isFlat() const |
191 | { | 273 | { |
192 | return (pFirst == pLast); | 274 | return (pFirst == pLast); |
193 | } | 275 | } |
194 | 276 | ||
195 | bool isShared() | 277 | bool isShared() const |
196 | { | 278 | { |
197 | return (pnRefs != NULL); | 279 | return (pnRefs != NULL); |
198 | } | 280 | } |
@@ -210,11 +292,14 @@ private: | |||
210 | pNew->pNext = NULL; | 292 | pNew->pNext = NULL; |
211 | pNew->nLength = nLen; | 293 | pNew->nLength = nLen; |
212 | pNew->pData = aChr.allocate( nLen+1 ); | 294 | pNew->pData = aChr.allocate( nLen+1 ); |
295 | pNew->pData[nLen] = (chr)0; | ||
213 | return pNew; | 296 | return pNew; |
214 | } | 297 | } |
215 | 298 | ||
216 | void appendChunk( Chunk *pNewChunk ) | 299 | void appendChunk( Chunk *pNewChunk ) |
217 | { | 300 | { |
301 | unShare(); | ||
302 | |||
218 | if( pFirst == NULL ) | 303 | if( pFirst == NULL ) |
219 | pLast = pFirst = pNewChunk; | 304 | pLast = pFirst = pNewChunk; |
220 | else | 305 | else |
@@ -228,6 +313,8 @@ private: | |||
228 | 313 | ||
229 | void prependChunk( Chunk *pNewChunk ) | 314 | void prependChunk( Chunk *pNewChunk ) |
230 | { | 315 | { |
316 | unShare(); | ||
317 | |||
231 | if( pFirst == NULL ) | 318 | if( pFirst == NULL ) |
232 | pLast = pFirst = pNewChunk; | 319 | pLast = pFirst = pNewChunk; |
233 | else | 320 | else |
@@ -239,9 +326,123 @@ private: | |||
239 | nLength += pNewChunk->nLength; | 326 | nLength += pNewChunk->nLength; |
240 | } | 327 | } |
241 | 328 | ||
329 | void joinShare( MyType &rSrc ) | ||
330 | { | ||
331 | clear(); | ||
332 | |||
333 | if( !rSrc.isFlat() ) | ||
334 | rSrc.flatten(); | ||
335 | |||
336 | rSrc.initCount(); | ||
337 | pnRefs = rSrc.pnRefs; | ||
338 | (*pnRefs)++; | ||
339 | nLength = rSrc.nLength; | ||
340 | pFirst = rSrc.pFirst; | ||
341 | pLast = rSrc.pLast; | ||
342 | } | ||
343 | |||
344 | void joinShare( const MyType &rSrc ) | ||
345 | { | ||
346 | clear(); | ||
347 | |||
348 | if( !rSrc.isFlat() ) | ||
349 | return; | ||
350 | |||
351 | if( !rSrc.isShared() ) | ||
352 | { | ||
353 | rSrc.pnRefs = new uint32_t; | ||
354 | (*rSrc.pnRefs) = 1; | ||
355 | } | ||
356 | pnRefs = rSrc.pnRefs; | ||
357 | (*pnRefs)++; | ||
358 | nLength = rSrc.nLength; | ||
359 | pFirst = rSrc.pFirst; | ||
360 | pLast = rSrc.pLast; | ||
361 | } | ||
362 | |||
363 | /** | ||
364 | * This takes an object that was shared and makes a copy of the base data | ||
365 | * that was being shared so that this copy can be changed. This should be | ||
366 | * added before any call that will change this object; | ||
367 | */ | ||
368 | void unShare() | ||
369 | { | ||
370 | if( isShared() == false ) | ||
371 | return; | ||
372 | |||
373 | Chunk *pNew = newChunk( nLength ); | ||
374 | chr *pos = pNew->pData; | ||
375 | Chunk *i = pFirst; | ||
376 | for(;;) | ||
377 | { | ||
378 | cpy( pos, i->pData, i->nLength ); | ||
379 | pos += i->nLength; | ||
380 | i = i->pNext; | ||
381 | if( i == NULL ) | ||
382 | break; | ||
383 | } | ||
384 | decRefs(); | ||
385 | appendChunk( pNew ); | ||
386 | decRefs(); | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * This decrements our ref count and pulls us out of the share. If the ref | ||
391 | * count hits zero because of this, it destroys the share. This is not | ||
392 | * safe to call on it's own, it's much better to call unShare. | ||
393 | */ | ||
394 | void decRefs() | ||
395 | { | ||
396 | if( isShared() ) | ||
397 | { | ||
398 | (*pnRefs)--; | ||
399 | if( (*pnRefs) == 0 ) | ||
400 | destroyShare(); | ||
401 | else | ||
402 | { | ||
403 | pnRefs = NULL; | ||
404 | pFirst = NULL; | ||
405 | pLast = NULL; | ||
406 | nLength = 0; | ||
407 | } | ||
408 | } | ||
409 | } | ||
410 | |||
411 | /** | ||
412 | * While the unShare function removes an instance from a share, this | ||
413 | * function destroys the data that was in the share, removing the share | ||
414 | * itself. This should only be called when the refcount for the share has | ||
415 | * or is about to reach zero. | ||
416 | */ | ||
417 | void destroyShare() | ||
418 | { | ||
419 | delete pnRefs; | ||
420 | pnRefs = NULL; | ||
421 | clear(); | ||
422 | } | ||
423 | |||
424 | void cpy( chr *dest, const chr *src, long count ) | ||
425 | { | ||
426 | for( int j = 0; j < count; j++ ) | ||
427 | { | ||
428 | *dest = *src; | ||
429 | dest++; | ||
430 | src++; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | void initCount() const | ||
435 | { | ||
436 | if( !isShared() ) | ||
437 | { | ||
438 | pnRefs = new uint32_t; | ||
439 | (*pnRefs) = 1; | ||
440 | } | ||
441 | } | ||
442 | |||
242 | private: | 443 | private: |
243 | long nLength; | 444 | long nLength; |
244 | uint32_t *pnRefs; | 445 | mutable uint32_t *pnRefs; |
245 | Chunk *pFirst; | 446 | Chunk *pFirst; |
246 | Chunk *pLast; | 447 | Chunk *pLast; |
247 | 448 | ||
diff --git a/src/tests/fstring.cpp b/src/tests/fstring.cpp index 26905ac..cb85282 100644 --- a/src/tests/fstring.cpp +++ b/src/tests/fstring.cpp | |||
@@ -1,28 +1,37 @@ | |||
1 | #include "fstring.h" | 1 | #include "fstring.h" |
2 | 2 | ||
3 | int main( int argc, char *argv ) | 3 | FString genThing() |
4 | { | 4 | { |
5 | FString str("[] this won't be in there", 3); | 5 | FString bob; |
6 | 6 | bob.append("ab "); | |
7 | str.append("Hello"); | 7 | bob += "cd "; |
8 | str.append(" th"); | 8 | bob += "efg"; |
9 | str.append("ere."); | ||
10 | 9 | ||
11 | if( str == "[] Hello there." ) | 10 | printf("---bob------\n%08X: %s\n", (unsigned int)bob.c_str(), bob.c_str() ); |
12 | printf("1) check\n"); | 11 | return bob; |
12 | } | ||
13 | 13 | ||
14 | if( str != "[] Hello there. " ) | 14 | #define pem printf("---------\n%08X: %s\n%08X: %s\n", (unsigned int)str.c_str(), str.c_str(), (unsigned int)str2.c_str(), str2.c_str() ); |
15 | printf("2) check\n"); | 15 | int main( int argc, char *argv ) |
16 | { | ||
17 | FString str("th"); | ||
16 | 18 | ||
17 | if( str != "[] Hello there." ) | 19 | str.prepend("Hello "); |
18 | printf("3) failed\n"); | 20 | str.append("ere."); |
19 | else | ||
20 | printf("3) check\n"); | ||
21 | 21 | ||
22 | str += " How are you?"; | 22 | FString str2( str ); |
23 | pem; | ||
24 | str += " What's up?"; | ||
25 | pem; | ||
26 | str2 += " How are you?"; | ||
27 | pem; | ||
28 | str = str2; | ||
29 | pem; | ||
23 | 30 | ||
24 | str.prepend("Bob says: "); | 31 | str2 = genThing(); |
32 | pem; | ||
25 | 33 | ||
26 | printf("%s\n", str.c_str() ); | 34 | str = str2; |
35 | pem; | ||
27 | } | 36 | } |
28 | 37 | ||