Torque3D Documentation / _generateds / stringBuffer.cpp

stringBuffer.cpp

Engine/source/core/stringBuffer.cpp

More...

Public Defines

Detailed Description

Public Defines

decRequestCount16() 
incRequestCount16() 
incRequestCount8() 
SBMAddThisStringBuffer() 
  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "core/stringBuffer.h"
 25#include "core/frameAllocator.h"
 26#include "core/strings/unicode.h"
 27#include "core/strings/stringFunctions.h"
 28#include "console/engineAPI.h"
 29
 30
 31#if defined(TORQUE_DEBUG)
 32   class StringBufferManager
 33   {
 34      public:
 35         static StringBufferManager& getManager();
 36         Vector<StringBuffer*> strings;
 37         U64 request8;
 38         U64 request16;
 39
 40         StringBufferManager()
 41         {
 42            request8 = 0.0;
 43            request16 = 0.0;
 44            VECTOR_SET_ASSOCIATION( strings );
 45         }
 46         
 47         void add(StringBuffer* s);
 48         void remove(StringBuffer* s);
 49         void updateStats();
 50         void dumpStats();
 51         void dumpAllStrings();
 52   };
 53
 54   DefineEngineFunction( sbmDumpStats, void, (), , "()")
 55   {
 56      StringBufferManager::getManager().dumpStats();
 57   }
 58
 59   DefineEngineFunction( sbmDumpStrings, void, (), , "()")
 60   {
 61      StringBufferManager::getManager().dumpAllStrings();
 62   }
 63#endif // TORQUE_DEBUG
 64
 65
 66#if defined(TORQUE_DEBUG)
 67#define SBMAddThisStringBuffer() \
 68   StringBufferManager::getManager().add(this); \
 69   rc = new RequestCounts; \
 70   clearRequestCounts()
 71#define incRequestCount8() rc->requestCount8++
 72#define incRequestCount16() rc->requestCount16++
 73#define decRequestCount16() rc->requestCount16--
 74#else
 75#define SBMAddThisStringBuffer()
 76#define incRequestCount8()
 77#define incRequestCount16()
 78#define decRequestCount16()
 79#endif
 80
 81StringBuffer::StringBuffer() : mBuffer(), mBuffer8(), mDirty8(true)
 82{
 83   VECTOR_SET_ASSOCIATION( mBuffer );
 84   SBMAddThisStringBuffer();
 85   mBuffer.push_back(0);
 86};
 87
 88/// Copy constructor. Very important.
 89StringBuffer::StringBuffer(const StringBuffer &copy) : mBuffer(), mBuffer8()
 90{
 91   VECTOR_SET_ASSOCIATION( mBuffer );
 92   SBMAddThisStringBuffer();
 93   set(&copy);
 94};
 95
 96StringBuffer::StringBuffer(const StringBuffer *in)
 97: mBuffer(), mBuffer8()
 98{
 99   VECTOR_SET_ASSOCIATION( mBuffer );
100   SBMAddThisStringBuffer();
101   set(in);
102}
103
104StringBuffer::StringBuffer(const UTF8 *in)
105: mBuffer(), mBuffer8()
106{
107   VECTOR_SET_ASSOCIATION( mBuffer );
108   SBMAddThisStringBuffer();
109   set(in);
110}
111
112StringBuffer::StringBuffer(const UTF16 *in)
113: mBuffer(), mBuffer8()
114{
115   VECTOR_SET_ASSOCIATION( mBuffer );
116   SBMAddThisStringBuffer();
117   set(in);
118}
119
120//-------------------------------------------------------------------------
121
122StringBuffer::~StringBuffer()
123{
124   // Everything will get cleared up nicely cuz it's a vector. Sweet.
125   #if defined(TORQUE_DEBUG)
126   StringBufferManager::getManager().remove(this);
127   delete rc;
128   #endif
129
130}
131
132//-------------------------------------------------------------------------
133
134void StringBuffer::set(const StringBuffer *in)
135{
136   // Copy the vector.
137   mBuffer.setSize(in->mBuffer.size());
138   dMemcpy(mBuffer.address(), in->mBuffer.address(), sizeof(UTF16) * mBuffer.size());
139   mDirty8 = true;
140}
141
142void StringBuffer::set(const UTF8 *in)
143{
144   incRequestCount8();
145   // Convert and store. Note that a UTF16 version of the string cannot be longer.
146   FrameTemp<UTF16> tmpBuff(dStrlen(in)+1);
147   if(!in || in[0] == 0 || !convertUTF8toUTF16N(in, tmpBuff, dStrlen(in)+1))
148   {
149      // Easy out, it's a blank string, or a bad string.
150      mBuffer.clear();
151      mBuffer.push_back(0);
152      AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF8 - not a null terminated string!");
153      mDirty8 = true;
154      return;
155   }
156
157   // Otherwise, we've a copy to do. (This might not be strictly necessary.)
158   mBuffer.setSize(dStrlen(tmpBuff)+1);
159   dMemcpy(mBuffer.address(), tmpBuff, sizeof(UTF16) * mBuffer.size());
160   mBuffer.compact();
161   AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF8 - not a null terminated string!");
162   mDirty8 = true;
163}
164
165void StringBuffer::set(const UTF16 *in)
166{
167   incRequestCount16();
168   // Just copy, it's already UTF16.
169   U32 newsize = dStrlen(in);
170   mBuffer.setSize(newsize + 1);
171   dMemcpy(mBuffer.address(), in, sizeof(UTF16) * newsize);
172   mBuffer[newsize] = '\0';
173   mBuffer.compact();
174   AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF16 - not a null terminated string!");
175   mDirty8 = true;
176}
177
178//-------------------------------------------------------------------------
179
180void StringBuffer::append(const StringBuffer &in)
181{
182   append(in.mBuffer.address(), in.length());
183}
184
185void StringBuffer::append(const UTF8* in)
186{
187   incRequestCount8();
188   decRequestCount16(); // because we're about to inc it when we go through append(utf16)
189   
190   // convert to UTF16, because that's our internal format.
191   // if the conversion fails, exit.
192   UTF16* tmp = createUTF16string(in);
193   AssertFatal(tmp, "StringBuffer::append(UTF8) - could not convert UTF8 string!");
194   if(!tmp)
195      return;
196      
197   append(tmp);
198   delete[] tmp;
199}
200
201void StringBuffer::append(const UTF16* in)
202{
203   AssertFatal(in, "StringBuffer::append(UTF16) - null UTF16 string!");
204   append(in, dStrlen(in));
205}
206
207void StringBuffer::append(const UTF16* in, const U32 len)
208{
209   incRequestCount16();
210   
211   // Stick in onto the end of us - first make space.
212   U32 oldSize = length();
213   mBuffer.increment(len);
214 
215   // Copy it in, ignoring both our terminator and theirs.
216   dMemcpy(&mBuffer[oldSize], in, sizeof(UTF16) * len);
217
218   // Terminate the string.
219   mBuffer.last() = 0;
220   
221   // mark utf8 buffer dirty
222   mDirty8 = true;
223}
224
225void StringBuffer::insert(const U32 charOffset, const StringBuffer &in)
226{
227   insert(charOffset, in.mBuffer.address(), in.length());
228}
229
230void StringBuffer::insert(const U32 charOffset, const UTF8* in)
231{
232   incRequestCount8();
233   decRequestCount16();
234   
235   // convert to UTF16, because that's our internal format.
236   // if the conversion fails, exit.
237   UTF16* tmp = createUTF16string(in);
238   AssertFatal(tmp, "StringBuffer::insert(UTF8) - could not convert UTF8 string!");
239   if(!tmp)
240      return;
241      
242   insert(charOffset, tmp);
243   delete[] tmp;
244}
245
246void StringBuffer::insert(const U32 charOffset, const UTF16* in)
247{
248   AssertFatal(in, "StringBuffer::insert(UTF16) - null UTF16 string!");
249   insert(charOffset, in, dStrlen(in));
250}
251
252void StringBuffer::insert(const U32 charOffset, const UTF16* in, const U32 len)
253{
254   incRequestCount16();
255   
256   // Deal with append case.
257   if(charOffset >= length())
258   {
259      append(in, len);
260      return;
261   }
262
263   // Append was easy, now we have to do some work.
264   
265   // Copy everything we have that comes after charOffset past where the new
266   // string data will be.
267
268   // Figure the number of UTF16's to copy, taking into account the possibility
269   // that we may be inserting a long string into a short string.
270
271   // How many chars come after the insert point? Add 1 to deal with terminator.
272   const U32 copyCharLength = (S32)(length() - charOffset) + 1;
273
274   // Make some space in our buffer. We only need in.length() more as we
275   // will be dropping one of the two terminators present in this operation.
276   mBuffer.increment(len);
277
278   for(S32 i=copyCharLength-1; i>=0; i--)
279      mBuffer[charOffset+i+len] = mBuffer[charOffset+i];
280
281   //  Can't copy directly: memcpy behavior is undefined if src and dst overlap.
282   //dMemcpy(&mBuffer[charOffset+len], &mBuffer[charOffset], sizeof(UTF16) * copyCharLength);
283
284   // And finally copy the new data in, not including its terminator.
285   dMemcpy(&mBuffer[charOffset], in, sizeof(UTF16) * len);
286
287   // All done!
288   AssertFatal(mBuffer.last() == 0, "StringBuffer::insert - not a null terminated string!");
289   
290   // mark utf8 buffer dirty
291   mDirty8 = true;
292}
293
294StringBuffer StringBuffer::substring(const U32 start, const U32 len) const
295{
296   // Deal with bonehead user input.
297   if(start >= length() || len == 0)
298   {
299      // Either they asked beyond the end of the string or we're null. Either
300      // way, let's give them a null string.
301      return StringBuffer("");
302   }
303
304   AssertFatal(start < length(), "StringBuffer::substring - invalid start!");
305   AssertFatal(start+len <= length(), "StringBuffer::substring - invalid len!");
306   AssertFatal(len > 0, "StringBuffer::substring - len must be >= 1.");
307
308   StringBuffer tmp;
309   tmp.mBuffer.clear();
310   for(S32 i=0; i<len; i++)
311      tmp.mBuffer.push_back(mBuffer[start+i]);
312   if(tmp.mBuffer.last() != 0) tmp.mBuffer.push_back(0);
313   
314   // Make sure this shit is terminated; we might get a totally empty string.
315   if(!tmp.mBuffer.size()) 
316      tmp.mBuffer.push_back(0);
317
318   return tmp;
319}
320
321UTF8* StringBuffer::createSubstring8(const U32 start, const U32 len) const
322{
323   incRequestCount8();
324   
325   StringBuffer sub = this->substring(start, len);
326   return sub.createCopy8();
327}
328
329void StringBuffer::cut(const U32 start, const U32 len)
330{
331   AssertFatal(start < length(), "StringBuffer::cut - invalid start!");
332   AssertFatal(start+len <= length(), "StringBuffer::cut - invalid len!");
333   AssertFatal(len > 0, "StringBuffer::cut - len >= 1.");
334
335   AssertFatal(mBuffer.last() == 0, "StringBuffer::cut - not a null terminated string! (pre)");
336
337   // Now snip things.
338   for(S32 i=start; i<mBuffer.size()-len; i++)
339      mBuffer[i] = mBuffer[i+len];
340   mBuffer.decrement(len);
341   mBuffer.compact();
342
343   AssertFatal(mBuffer.last() == 0, "StringBuffer::cut - not a null terminated string! (post)");
344   
345   // mark utf8 buffer dirty
346   mDirty8 = true;
347}
348
349const UTF16 StringBuffer::getChar(const U32 offset) const
350{
351   incRequestCount16();
352   
353   // Allow them to grab the null terminator if they want.
354   AssertFatal(offset<mBuffer.size(), "StringBuffer::getChar - outside of range.");
355
356   return mBuffer[offset];
357}
358
359//-------------------------------------------------------------------------
360
361void StringBuffer::getCopy8(UTF8 *buff, const U32 buffSize) const
362{
363   incRequestCount8();
364   AssertFatal(mBuffer.last() == 0, "StringBuffer::get UTF8 - not a null terminated string!");
365   convertUTF16toUTF8N(mBuffer.address(), buff, buffSize);
366}
367
368void StringBuffer::getCopy(UTF16 *buff, const U32 buffSize) const
369{
370   incRequestCount16();
371   // Just copy it out.
372   AssertFatal(mBuffer.last() == 0, "StringBuffer::get UTF8 - not a null terminated string!");
373   dMemcpy(buff, mBuffer.address(), sizeof(UTF16) * getMin(buffSize, (U32)mBuffer.size()));
374   // ensure null termination.
375   buff[buffSize-1] = NULL;
376}
377
378UTF8* StringBuffer::createCopy8() const
379{
380   incRequestCount8();
381   // convert will create a buffer of the appropriate size for a null terminated
382   // input string.
383   UTF8* out = createUTF8string(mBuffer.address());
384   return out;
385}
386
387const UTF16* StringBuffer::getPtr() const
388{
389   incRequestCount16();
390   // get a pointer to the StringBuffer's data store.
391   // this pointer is volatile, and not thread safe.
392   // use this in situations where you can be sure that the StringBuffer will
393   // not be modified out from under you.
394   // the key here is, you avoid yet another data copy. data copy is slow on
395   // most modern hardware.
396   return mBuffer.address();
397}
398
399const UTF8* StringBuffer::getPtr8() const
400{
401   incRequestCount8();
402   // get a pointer to the utf8 version of the StringBuffer's data store.
403   // if the utf8 version is dirty, update it first.
404   if(mDirty8)
405      // force const cheating.
406      const_cast<StringBuffer*>(this)->updateBuffer8();
407   return mBuffer8.address();
408}
409
410void StringBuffer::updateBuffer8()
411{
412   U32 slackLen = getUTF8BufferSizeEstimate();
413   mBuffer8.setSize(slackLen);
414   U32 len = convertUTF16toUTF8N(mBuffer.address(), mBuffer8.address(), slackLen);
415   mBuffer8.setSize(len+1);
416   mBuffer8.compact();
417   mDirty8 = false;
418}
419
420
421#if defined(TORQUE_DEBUG)
422StringBufferManager& StringBufferManager::getManager()
423{ 
424   static StringBufferManager _sbm; return _sbm;
425}
426
427void StringBufferManager::add(StringBuffer* s)
428{
429   strings.push_back(s); 
430}
431
432void StringBufferManager::remove(StringBuffer* s)
433{
434   U32 nstrings = strings.size() - 1;
435   for(S32 i = nstrings; i >= 0; i--)
436      if(strings[i] == s)
437         strings.erase_fast(i);
438}
439
440void StringBufferManager::updateStats()
441{
442   request8 = 0;
443   request16 = 0;
444   U32 nstrings = strings.size();
445   for(S32 i=0; i < nstrings; i++)
446   {
447      request8 += strings[i]->rc->requestCount8;
448      request16 += strings[i]->rc->requestCount16;
449   }
450}
451
452void StringBufferManager::dumpStats()
453{
454   updateStats();
455   Con::printf("===== String Manager Stats =====");
456   Con::printf(" strings:        %i", strings.size());
457   Con::printf(" utf8 requests:  %Lu", request8);
458   Con::printf(" utf16 requests: %Lu", request16);
459}
460
461void StringBufferManager::dumpAllStrings()
462{
463   U32 nstrings = strings.size();
464   Con::printf("===== String Manager: All Strings =====");
465   Con::printf(" utf8 | utf16 | string");
466   for(S32 i=0; i < nstrings; i++)
467   {
468      UTF8* tmp = strings[i]->createCopy8();
469      strings[i]->rc->requestCount8--;
470      Con::printf("%5llu  %5llu    \"%s\"", strings[i]->rc->requestCount8, strings[i]->rc->requestCount16, tmp);
471      delete[] tmp;
472   }
473}
474
475#endif
476