diff options
Diffstat (limited to '')
-rw-r--r-- | src/fstring.h | 1304 |
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 | ||
9 | template< typename chr > | 10 | #define min( a, b ) ((a<b)?(a):(b)) |
10 | struct FStringChunk | 11 | |
11 | { | 12 | namespace 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 | */ | ||
28 | template< typename chr, typename chralloc=std::allocator<chr>, typename chunkalloc=std::allocator<struct FStringChunk<chr> > > | ||
29 | class 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 |
34 | private: | 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 | ||
38 | public: | 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 | ||
385 | private: | 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 | ||
634 | private: | 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 | ||
644 | typedef FBasicString<char> FString; | 894 | typedef FBasicString<char> FString; |
645 | 895 | ||
646 | #include "hash.h" | 896 | template<> uint32_t __calcHashCode<FString>( const FString &k ); |
647 | template<> uint32_t __calcHashCode<FString>( const FString &k ); | 897 | template<> bool __cmpHashKeys<FString>( const FString &a, const FString &b ); |
648 | template<> bool __cmpHashKeys<FString>( const FString &a, const FString &b ); | 898 | } |
649 | 899 | ||
900 | #include <ostream> | ||
901 | std::basic_ostream<char>& operator<< (std::basic_ostream<char> &os, const Bu::FString &val ); | ||
650 | 902 | ||
651 | #endif | 903 | #endif |