1 /*------------------------------------------------------------------------------
2 * Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team
3 *
4 * Distributable under the terms of either the Apache License (Version 2.0) or
5 * the GNU Lesser General Public License, as specified in the COPYING file.
6 ------------------------------------------------------------------------------*/
7 #include "CLucene/_ApiHeader.h"
8 #include "StringBuffer.h"
9 #include "Misc.h"
10 #include <assert.h>
11
CL_NS_DEF(util)12 CL_NS_DEF(util)
13
14 StringBuffer::StringBuffer(TCHAR* buf,size_t maxlen, const bool consumeBuffer){
15 buffer = buf;
16 bufferLength = maxlen;
17 bufferOwner = !consumeBuffer;
18 len = 0;
19 }
StringBuffer()20 StringBuffer::StringBuffer(){
21 //Func - Constructor. Allocates a buffer with the default length.
22 //Pre - true
23 //Post - buffer of length bufferLength has been allocated
24
25 //Initialize
26 bufferLength = LUCENE_DEFAULT_TOKEN_BUFFER_SIZE;
27 len = 0;
28 //Allocate a buffer of length bufferLength
29 buffer = _CL_NEWARRAY(TCHAR,bufferLength);
30 bufferOwner = true;
31 }
32
StringBuffer(const size_t initSize)33 StringBuffer::StringBuffer(const size_t initSize){
34 //Func - Constructor. Allocates a buffer of length initSize + 1
35 //Pre - initSize > 0
36 //Post - A buffer has been allocated of length initSize + 1
37
38 //Initialize the bufferLength to initSize + 1 The +1 is for the terminator '\0'
39 bufferLength = initSize + 1;
40 len = 0;
41 //Allocate a buffer of length bufferLength
42 buffer = _CL_NEWARRAY(TCHAR,bufferLength);
43 bufferOwner = true;
44 }
45
StringBuffer(const TCHAR * value)46 StringBuffer::StringBuffer(const TCHAR* value){
47 //Func - Constructor.
48 // Creates an instance of Stringbuffer containing a copy of the string value
49 //Pre - value != NULL
50 //Post - An instance of StringBuffer has been created containing the copy of the string value
51
52 //Initialize the length of the string to be stored in buffer
53 len = (size_t) _tcslen(value);
54
55 //Calculate the space occupied in buffer by a copy of value
56 const size_t occupiedLength = len + 1;
57
58 // Minimum allocated buffer length is LUCENE_DEFAULT_TOKEN_BUFFER_SIZE.
59 bufferLength = (occupiedLength >= LUCENE_DEFAULT_TOKEN_BUFFER_SIZE
60 ? occupiedLength : LUCENE_DEFAULT_TOKEN_BUFFER_SIZE);
61
62 //Allocate a buffer of length bufferLength
63 buffer = _CL_NEWARRAY(TCHAR,bufferLength);
64 bufferOwner = true;
65 //Copy the string value into buffer
66 _tcsncpy(buffer, value, occupiedLength);
67 //Assert that the buffer has been terminated at the end of the string
68 CND_PRECONDITION (buffer[len] == '\0', "Buffer was not correctly terminated");
69 }
70
~StringBuffer()71 StringBuffer::~StringBuffer() {
72 // Func - Destructor
73 // Pre - true
74 // Post - Instanc has been destroyed
75
76 if( bufferOwner ){
77 _CLDELETE_CARRAY(buffer);
78 }else
79 buffer = NULL;
80 }
81
clear()82 void StringBuffer::clear(){
83 //Func - Clears the Stringbuffer and resets it to it default empty state
84 //Pre - true
85 //Post - pre(buffer) has been destroyed and a new one has been allocated
86
87 // TODO: Should we really delete and recreate the buffer - perhaps just reseting len should suffice?
88 // We should really look into at least providing both options
89
90 //Destroy the current buffer if present
91 _CLDELETE_LCARRAY(buffer);
92
93 //Initialize
94 len = 0;
95 bufferLength = LUCENE_DEFAULT_TOKEN_BUFFER_SIZE;
96 //Allocate a buffer of length bufferLength
97 buffer = _CL_NEWARRAY(TCHAR,bufferLength);
98 }
99
appendChar(const TCHAR character)100 void StringBuffer::appendChar(const TCHAR character) {
101 //Func - Appends a single character
102 //Pre - true
103 //Post - The character has been appended to the string in the buffer
104
105 //Check if the current buffer length is sufficient to have the string value appended
106 if (len + 1 > bufferLength){
107 //Have the size of the current string buffer increased because it is too small
108 growBuffer(len + 1);
109 }
110 //Put character at position len which is the end of the string in the buffer
111 //Note that this action might overwrite the terminator of the string '\0', which
112 //is kind of tricky
113 buffer[len] = character;
114 //Increase the len by to represent the correct length of the string in the buffer
115 len++;
116 }
117
append(const TCHAR * value)118 void StringBuffer::append(const TCHAR* value) {
119 //Func - Appends a copy of the string value
120 //Pre - value != NULL
121 //Post - value has been copied and appended to the string in buffer
122
123 append(value, _tcslen(value));
124 }
append(const TCHAR * value,size_t appendedLength)125 void StringBuffer::append(const TCHAR* value, size_t appendedLength) {
126 //Func - Appends a copy of the string value
127 //Pre - value != NULL
128 // appendedLength contains the length of the string value which is to be appended
129 //Post - value has been copied and appended to the string in buffer
130
131 //Check if the current buffer length is sufficient to have the string value appended
132 if (len + appendedLength + 1 > bufferLength){
133 //Have the size of the current string buffer increased because it is too small
134 growBuffer(len + appendedLength + 1);
135 }
136
137 //Copy the string value into the buffer at postion len
138 _tcsncpy(buffer + len, value, appendedLength);
139
140 //Add the length of the copied string to len to reflect the new length of the string in
141 //the buffer (Note: len is not the bufferlength!)
142 len += appendedLength;
143 }
144
appendInt(const int64_t value,const int32_t _Radix)145 void StringBuffer::appendInt(const int64_t value, const int32_t _Radix) {
146 //Func - Appends an integer (after conversion to a character string)
147 //Pre - true
148 //Post - The converted integer value has been appended to the string in buffer
149
150 //instantiate a buffer of 30 charactes for the conversion of the integer
151 TCHAR buf[30];
152 //Convert the integer value to a string buf using _Radix
153 _i64tot(value, buf, _Radix);
154 //Have the converted integer now stored in buf appended to the string in buffer
155 append(buf);
156 }
157
appendFloat(const float_t value,const size_t digits)158 void StringBuffer::appendFloat(const float_t value, const size_t digits){
159 //Func - Appends a float_t (after conversion to a character string)
160 //Pre - digits > 0. Indicates the minimum number of characters printed
161 //Post - The converted float_t value has been appended to the string in buffer
162
163 //using sprintf("%f" was not reliable on other plaforms... we use a custom float convertor
164 //bvk: also, using sprintf and %f seems excessivelly slow
165 assert(digits <= 8);
166
167 //the maximum number of characters that int64 will hold is 23. so we need 23*2+2
168 TCHAR buf[48]; //the buffer to hold
169 int64_t v = (int64_t)value; //the integer value of the float
170 _i64tot(v,buf,10); //add the whole number
171
172 size_t l = 99-_tcslen(buf); //how many digits we have to work with?
173 size_t dig = l< (size_t)digits ? l : digits;
174 if ( dig > 0 ){
175 _tcscat(buf,_T(".")); //add a decimal point
176
177 int64_t remi=(int64_t)((value-v)*pow((float_t)10,(float_t)(dig+1))); //take the remainder and make a whole number
178 if ( remi<0 ) remi*=-1;
179 int64_t remadj=remi/10;
180 if ( remi-(remadj*10) >=5 )
181 remadj++; //adjust remainder
182
183 // add as many zeros as necessary between the decimal point and the
184 // significant part of the number. Fixes a bug when trying to print
185 // numbers that have zeros right after the decimal point
186 if (remadj) {
187 size_t numZeros = dig - (size_t)log10((float_t)remadj) - 1;
188 while(numZeros-- > 0 && numZeros < 10)
189 _tcscat(buf,_T("0")); //add a zero before the decimal point
190 }
191
192 _i64tot(remadj,buf+_tcslen(buf),10); //add the remainder
193 }
194
195 append(buf);
196 }
197
appendBoost(const float_t boost)198 void StringBuffer::appendBoost(const float_t boost){
199 if (boost != 1.0f) {
200 appendChar(_T('^')); appendFloat(boost,1);
201 }
202 }
203
appendBool(const bool value)204 void StringBuffer::appendBool(const bool value){
205 append( value ? _T( "true" ) : _T( "false" ));
206 }
207
prepend(const TCHAR * value)208 void StringBuffer::prepend(const TCHAR* value){
209 //Func - Puts a copy of the string value infront of the current string in the StringBuffer
210 //Pre - value != NULL
211 //Post - The string in pre(buffer) has been shifted n positions where n equals the length of value.
212 // The string value was then copied to the beginning of stringbuffer
213
214 prepend(value, _tcslen(value));
215 }
216
prepend(const TCHAR * value,const size_t prependedLength)217 void StringBuffer::prepend(const TCHAR* value, const size_t prependedLength) {
218 //Func - Puts a copy of the string value in front of the string in the StringBuffer
219 //Pre - value != NULL
220 // prependedLength contains the length of the string value which is to be prepended
221 //Post - A copy of the string value is has been in front of the string in buffer
222 //todo: something is wrong with this code, i'm sure... it only grows (and therefore moves if the buffer is to small)
223 //Check if the current buffer length is sufficient to have the string value prepended
224 if (prependedLength + len + 1 > bufferLength){
225 //Have the size of the current string buffer increased because it is too small
226 //Because prependedLength is passed as the second argument to growBuffer,
227 //growBuffer will have left the first prependedLength characters empty
228 //when it recopied buffer during reallocation.
229 growBuffer(prependedLength + len + 1, prependedLength);
230 }
231
232 //Copy the string value into the buffer at postion 0
233 _tcsncpy(buffer, value, prependedLength);
234 //Add the length of the copied string to len to reflect the new length of the string in
235 //the buffer (Note: len is not the bufferlength!)
236 len += prependedLength;
237 }
238
length() const239 size_t StringBuffer::length() const{
240 //Func - Returns the length of the string in the StringBuffer
241 //Pre - true
242 //Post - The length len of the string in the buffer has been returned
243
244 return len;
245 }
toString()246 TCHAR* StringBuffer::toString(){
247 //Func - Returns a copy of the current string in the StringBuffer sized equal to the length of the string
248 // in the StringBuffer.
249 //Pre - true
250 //Post - The copied string has been returned
251
252 //Instantiate a buffer equal to the length len + 1
253 TCHAR* ret = _CL_NEWARRAY(TCHAR,len + 1);
254 if (ret){
255 //Copy the string in buffer
256 _tcsncpy(ret, buffer, len);
257 //terminate the string
258 ret[len] = '\0';
259 }
260 //return the the copy
261 return ret;
262 }
getBuffer()263 TCHAR* StringBuffer::getBuffer() {
264 //Func - '\0' terminates the buffer and returns its pointer
265 //Pre - true
266 //Post - buffer has been '\0' terminated and returned
267
268 // Check if the current buffer is '\0' terminated
269 if (len == bufferLength){
270 //Make space for terminator, if necessary.
271 growBuffer(len + 1);
272 }
273 //'\0' buffer so it can be returned properly
274 buffer[len] = '\0';
275
276 return buffer;
277 }
278
giveBuffer()279 TCHAR* StringBuffer::giveBuffer() {
280 TCHAR* ret = getBuffer();
281 buffer = NULL;
282 len = 0;
283 bufferLength = 0;
284 bufferOwner = false;
285 return ret;
286 }
287
reserve(const size_t size)288 void StringBuffer::reserve(const size_t size){
289 if ( bufferLength >= size )
290 return;
291 bufferLength = size;
292
293 //Allocate a new buffer of length bufferLength
294 TCHAR* tmp = _CL_NEWARRAY(TCHAR,bufferLength);
295 _tcsncpy(tmp, buffer, len);
296 tmp[len] = '\0';
297
298 //destroy the old buffer
299 if (buffer){
300 _CLDELETE_CARRAY(buffer);
301 }
302 //Assign the new buffer tmp to buffer
303 buffer = tmp;
304 }
305
growBuffer(const size_t minLength,const size_t skippingNInitialChars)306 void StringBuffer::growBuffer(const size_t minLength, const size_t skippingNInitialChars) {
307 //Func - Has the buffer grown to a minimum length of minLength or bigger and shifts the
308 // current string in buffer by skippingNInitialChars forward
309 //Pre - After growth, must have at least enough room for contents + terminator so
310 // minLength >= skippingNInitialChars + len + 1
311 // skippingNInitialChars >= 0
312 //Post - The buffer has been grown to a minimum length of minLength or bigger and
313 // if skippingNInitialChars > 0, the contents of the buffer has beeen shifted
314 // forward by skippingNInitialChars positions as the buffer is reallocated,
315 // leaving the first skippingNInitialChars uninitialized (presumably to be
316 // filled immediately thereafter by the caller).
317
318 CND_PRECONDITION (minLength >= skippingNInitialChars + len + 1,"skippingNInitialChars is not large enough");
319
320 //More aggressive growth strategy to offset smaller default buffer size:
321 if ( !bufferOwner ){
322 assert(bufferLength>=minLength);
323 return;
324 }
325
326 bufferLength *= 2;
327 //Check that bufferLength is bigger than minLength
328 if (bufferLength < minLength){
329 //Have bufferLength become minLength because it still was too small
330 bufferLength = minLength;
331 }
332
333 //Allocate a new buffer of length bufferLength
334 TCHAR* tmp = _CL_NEWARRAY(TCHAR,bufferLength);
335 memset(tmp, 0, sizeof(TCHAR) * skippingNInitialChars);
336
337 //The old buffer might not have been null-terminated, so we _tcsncpy
338 //only len bytes, not len+1 bytes (the latter might read one char off the
339 //end of the old buffer), then apply the terminator to the new buffer.
340 _tcsncpy(tmp + skippingNInitialChars, buffer, len);
341 tmp[skippingNInitialChars + len] = '\0';
342
343 //destroy the old buffer
344 _CLDELETE_LCARRAY(buffer);
345
346 //Assign the new buffer tmp to buffer
347 buffer = tmp;
348 }
349
setCharAt(size_t pos,const TCHAR chr)350 void StringBuffer::setCharAt(size_t pos, const TCHAR chr) {
351 CND_PRECONDITION (pos < len, "pos is not in string");
352 buffer[pos] = chr;
353 }
354
charAt(size_t pos)355 TCHAR StringBuffer::charAt(size_t pos) {
356 CND_PRECONDITION (pos < len, "pos is not in string");
357 return buffer[pos];
358 }
359
insert(const size_t pos,TCHAR chr)360 void StringBuffer::insert(const size_t pos, TCHAR chr) {
361 CND_PRECONDITION (pos <= len, "pos is larger than string len");
362 growBuffer(len + 1, 0);
363 memmove(&buffer[pos + 1], &buffer[pos], sizeof(TCHAR) * (len - pos));
364 buffer[pos] = chr;
365 len++;
366 }
367
insert(const size_t pos,const TCHAR * chrs,size_t length)368 void StringBuffer::insert(const size_t pos, const TCHAR* chrs, size_t length) {
369 CND_PRECONDITION (pos <= len, "pos is larger than string len");
370
371 if (length == -1) {
372 length = _tcslen(chrs);
373 }
374
375 if (length > 0) {
376 growBuffer(len + length, 0);
377 memmove(&buffer[pos + length], &buffer[pos], sizeof(TCHAR) * (len - pos));
378 memcpy(&buffer[pos], chrs, sizeof(TCHAR) * (length));
379 len += length;
380 }
381 }
382
deleteCharAt(size_t pos)383 void StringBuffer::deleteCharAt(size_t pos) {
384 CND_PRECONDITION (pos < len, "pos is larger than string len");
385
386 memmove(&buffer[pos], &buffer[pos + 1], sizeof(TCHAR) * (len - pos));
387 len--;
388 buffer[len] = _T('\0');
389 }
390
deleteChars(size_t start,size_t end)391 void StringBuffer::deleteChars(size_t start, size_t end) {
392 CND_PRECONDITION (start <= end && end <= len, "start/end is not in string");
393
394 if (start < end) {
395 memmove(&buffer[start], &buffer[end], sizeof(TCHAR) * (len - end));
396 buffer[len - (end - start)] = _T('\0');
397 len -= end - start;
398 }
399 }
400
toLower()401 void StringBuffer::toLower() {
402 _tcslwr(buffer);
403 }
404
substringEquals(size_t start,size_t end,const TCHAR * str,size_t length) const405 bool StringBuffer::substringEquals(size_t start, size_t end, const TCHAR* str, size_t length) const {
406 if (length == -1) {
407 length = _tcslen(str);
408 }
409
410 if (end - start != length) {
411 return false;
412 }
413
414 for (size_t c = start; c < end; c++) {
415 if (buffer[c] != str[c - start]) {
416 return false;
417 }
418 }
419
420 return true;
421 }
422
423 CL_NS_END
424