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