ovr_sdk

view LibOVR/Src/Kernel/OVR_String.cpp @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
line source
1 /************************************************************************************
3 Filename : OVR_String.cpp
4 Content : String UTF8 string implementation with copy-on-write semantics
5 (thread-safe for assignment but not modification).
6 Created : September 19, 2012
7 Notes :
9 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
11 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
12 you may not use the Oculus VR Rift SDK except in compliance with the License,
13 which is provided at the time of installation or download, or which
14 otherwise accompanies this software in either electronic or hard copy form.
16 You may obtain a copy of the License at
18 http://www.oculusvr.com/licenses/LICENSE-3.2
20 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
21 distributed under the License is distributed on an "AS IS" BASIS,
22 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 See the License for the specific language governing permissions and
24 limitations under the License.
26 ************************************************************************************/
28 #include "OVR_String.h"
30 #include <stdlib.h>
31 #include <ctype.h>
33 #ifdef OVR_OS_QNX
34 # include <strings.h>
35 #endif
37 namespace OVR {
39 #define String_LengthIsSize (size_t(1) << String::Flag_LengthIsSizeShift)
41 String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} };
44 String::String()
45 {
46 pData = &NullData;
47 pData->AddRef();
48 };
50 String::String(const char* pdata)
51 {
52 // Obtain length in bytes; it doesn't matter if _data is UTF8.
53 size_t size = pdata ? OVR_strlen(pdata) : 0;
54 pData = AllocDataCopy1(size, 0, pdata, size);
55 };
57 String::String(const char* pdata1, const char* pdata2, const char* pdata3)
58 {
59 // Obtain length in bytes; it doesn't matter if _data is UTF8.
60 size_t size1 = pdata1 ? OVR_strlen(pdata1) : 0;
61 size_t size2 = pdata2 ? OVR_strlen(pdata2) : 0;
62 size_t size3 = pdata3 ? OVR_strlen(pdata3) : 0;
64 DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0,
65 pdata1, size1, pdata2, size2);
66 memcpy(pdataDesc->Data + size1 + size2, pdata3, size3);
67 pData = pdataDesc;
68 }
70 String::String(const char* pdata, size_t size)
71 {
72 OVR_ASSERT((size == 0) || (pdata != 0));
73 pData = AllocDataCopy1(size, 0, pdata, size);
74 };
77 String::String(const InitStruct& src, size_t size)
78 {
79 pData = AllocData(size, 0);
80 src.InitString(GetData()->Data, size);
81 }
83 String::String(const String& src)
84 {
85 pData = src.GetData();
86 pData->AddRef();
87 }
89 String::String(const StringBuffer& src)
90 {
91 pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize());
92 }
94 String::String(const wchar_t* data)
95 {
96 pData = &NullData;
97 pData->AddRef();
98 // Simplified logic for wchar_t constructor.
99 if (data)
100 *this = data;
101 }
104 String::DataDesc* String::AllocData(size_t size, size_t lengthIsSize)
105 {
106 String::DataDesc* pdesc;
108 if (size == 0)
109 {
110 pdesc = &NullData;
111 pdesc->AddRef();
112 return pdesc;
113 }
115 pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size);
116 pdesc->Data[size] = 0;
117 pdesc->RefCount = 1;
118 pdesc->Size = size | lengthIsSize;
119 return pdesc;
120 }
123 String::DataDesc* String::AllocDataCopy1(size_t size, size_t lengthIsSize,
124 const char* pdata, size_t copySize)
125 {
126 String::DataDesc* pdesc = AllocData(size, lengthIsSize);
127 memcpy(pdesc->Data, pdata, copySize);
128 return pdesc;
129 }
131 String::DataDesc* String::AllocDataCopy2(size_t size, size_t lengthIsSize,
132 const char* pdata1, size_t copySize1,
133 const char* pdata2, size_t copySize2)
134 {
135 String::DataDesc* pdesc = AllocData(size, lengthIsSize);
136 memcpy(pdesc->Data, pdata1, copySize1);
137 memcpy(pdesc->Data + copySize1, pdata2, copySize2);
138 return pdesc;
139 }
142 size_t String::GetLength() const
143 {
144 // Optimize length accesses for non-UTF8 character strings.
145 DataDesc* pdata = GetData();
146 size_t length, size = pdata->GetSize();
148 if (pdata->LengthIsSize())
149 return size;
151 length = (size_t)UTF8Util::GetLength(pdata->Data, (size_t)size);
153 if (length == size)
154 pdata->Size |= String_LengthIsSize;
156 return length;
157 }
160 //static uint32_t String_CharSearch(const char* buf, )
163 uint32_t String::GetCharAt(size_t index) const
164 {
165 intptr_t i = (intptr_t) index;
166 DataDesc* pdata = GetData();
167 const char* buf = pdata->Data;
168 uint32_t c;
170 if (pdata->LengthIsSize())
171 {
172 OVR_ASSERT(index < pdata->GetSize());
173 buf += i;
174 return UTF8Util::DecodeNextChar_Advance0(&buf);
175 }
177 c = UTF8Util::GetCharAt(index, buf, pdata->GetSize());
178 return c;
179 }
181 uint32_t String::GetFirstCharAt(size_t index, const char** offset) const
182 {
183 DataDesc* pdata = GetData();
184 intptr_t i = (intptr_t) index;
185 const char* buf = pdata->Data;
186 const char* end = buf + pdata->GetSize();
187 uint32_t c;
189 do
190 {
191 c = UTF8Util::DecodeNextChar_Advance0(&buf);
192 i--;
194 if (buf >= end)
195 {
196 // We've hit the end of the string; don't go further.
197 OVR_ASSERT(i == 0);
198 return c;
199 }
200 } while (i >= 0);
202 *offset = buf;
204 return c;
205 }
207 uint32_t String::GetNextChar(const char** offset) const
208 {
209 return UTF8Util::DecodeNextChar(offset);
210 }
214 void String::AppendChar(uint32_t ch)
215 {
216 DataDesc* pdata = GetData();
217 size_t size = pdata->GetSize();
218 char buff[8];
219 intptr_t encodeSize = 0;
221 // Converts ch into UTF8 string and fills it into buff.
222 UTF8Util::EncodeChar(buff, &encodeSize, ch);
223 OVR_ASSERT(encodeSize >= 0);
225 SetData(AllocDataCopy2(size + (size_t)encodeSize, 0,
226 pdata->Data, size, buff, (size_t)encodeSize));
227 pdata->Release();
228 }
231 void String::AppendString(const wchar_t* pstr, intptr_t len)
232 {
233 if (!pstr)
234 return;
236 DataDesc* pdata = GetData();
237 size_t oldSize = pdata->GetSize();
238 size_t encodeSize = (size_t)UTF8Util::GetEncodeStringSize(pstr, len);
240 DataDesc* pnewData = AllocDataCopy1(oldSize + (size_t)encodeSize, 0,
241 pdata->Data, oldSize);
242 UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len);
244 SetData(pnewData);
245 pdata->Release();
246 }
249 void String::AppendString(const char* putf8str, intptr_t utf8StrSz)
250 {
251 if (!putf8str || !utf8StrSz)
252 return;
253 if (utf8StrSz == -1)
254 utf8StrSz = (intptr_t)OVR_strlen(putf8str);
256 DataDesc* pdata = GetData();
257 size_t oldSize = pdata->GetSize();
259 SetData(AllocDataCopy2(oldSize + (size_t)utf8StrSz, 0,
260 pdata->Data, oldSize, putf8str, (size_t)utf8StrSz));
261 pdata->Release();
262 }
264 void String::AssignString(const InitStruct& src, size_t size)
265 {
266 DataDesc* poldData = GetData();
267 DataDesc* pnewData = AllocData(size, 0);
268 src.InitString(pnewData->Data, size);
269 SetData(pnewData);
270 poldData->Release();
271 }
273 void String::AssignString(const char* putf8str, size_t size)
274 {
275 DataDesc* poldData = GetData();
276 SetData(AllocDataCopy1(size, 0, putf8str, size));
277 poldData->Release();
278 }
280 void String::operator = (const char* pstr)
281 {
282 AssignString(pstr, pstr ? OVR_strlen(pstr) : 0);
283 }
285 void String::operator = (const wchar_t* pwstr)
286 {
287 pwstr = pwstr ? pwstr : L"";
289 DataDesc* poldData = GetData();
290 size_t size = (size_t)UTF8Util::GetEncodeStringSize(pwstr);
292 DataDesc* pnewData = AllocData(size, 0);
293 UTF8Util::EncodeString(pnewData->Data, pwstr);
294 SetData(pnewData);
295 poldData->Release();
296 }
299 void String::operator = (const String& src)
300 {
301 DataDesc* psdata = src.GetData();
302 DataDesc* pdata = GetData();
304 SetData(psdata);
305 psdata->AddRef();
306 pdata->Release();
307 }
310 void String::operator = (const StringBuffer& src)
311 {
312 DataDesc* polddata = GetData();
313 SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()));
314 polddata->Release();
315 }
317 void String::operator += (const String& src)
318 {
319 DataDesc *pourData = GetData(),
320 *psrcData = src.GetData();
321 size_t ourSize = pourData->GetSize(),
322 srcSize = psrcData->GetSize();
323 size_t lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag();
325 SetData(AllocDataCopy2(ourSize + srcSize, lflag,
326 pourData->Data, ourSize, psrcData->Data, srcSize));
327 pourData->Release();
328 }
331 String String::operator + (const char* str) const
332 {
333 String tmp1(*this);
334 tmp1 += (str ? str : "");
335 return tmp1;
336 }
338 String String::operator + (const String& src) const
339 {
340 String tmp1(*this);
341 tmp1 += src;
342 return tmp1;
343 }
345 void String::Remove(size_t posAt, intptr_t removeLength)
346 {
347 DataDesc* pdata = GetData();
348 size_t oldSize = pdata->GetSize();
349 // Length indicates the number of characters to remove.
350 size_t length = GetLength();
352 // If index is past the string, nothing to remove.
353 if (posAt >= length)
354 return;
355 // Otherwise, cap removeLength to the length of the string.
356 if ((posAt + removeLength) > length)
357 removeLength = length - posAt;
359 // Get the byte position of the UTF8 char at position posAt.
360 intptr_t bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize);
361 intptr_t removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos);
363 SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(),
364 pdata->Data, bytePos,
365 pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize)));
366 pdata->Release();
367 }
370 String String::Substring(size_t start, size_t end) const
371 {
372 size_t length = GetLength();
373 if ((start >= length) || (start >= end))
374 return String();
376 DataDesc* pdata = GetData();
378 // If size matches, we know the exact index range.
379 if (pdata->LengthIsSize())
380 return String(pdata->Data + start, end - start);
382 // Get position of starting character.
383 intptr_t byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize());
384 intptr_t byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart);
385 return String(pdata->Data + byteStart, (size_t)byteSize);
386 }
388 void String::Clear()
389 {
390 NullData.AddRef();
391 GetData()->Release();
392 SetData(&NullData);
393 }
396 String String::ToUpper() const
397 {
398 uint32_t c;
399 const char* psource = GetData()->Data;
400 const char* pend = psource + GetData()->GetSize();
401 String str;
402 intptr_t bufferOffset = 0;
403 char buffer[512];
405 while(psource < pend)
406 {
407 do {
408 c = UTF8Util::DecodeNextChar_Advance0(&psource);
409 UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c)));
410 } while ((psource < pend) && (bufferOffset < intptr_t(sizeof(buffer)-8)));
412 // Append string a piece at a time.
413 str.AppendString(buffer, bufferOffset);
414 bufferOffset = 0;
415 }
417 return str;
418 }
420 String String::ToLower() const
421 {
422 uint32_t c;
423 const char* psource = GetData()->Data;
424 const char* pend = psource + GetData()->GetSize();
425 String str;
426 intptr_t bufferOffset = 0;
427 char buffer[512];
429 while(psource < pend)
430 {
431 do {
432 c = UTF8Util::DecodeNextChar_Advance0(&psource);
433 UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c)));
434 } while ((psource < pend) && (bufferOffset < intptr_t(sizeof(buffer)-8)));
436 // Append string a piece at a time.
437 str.AppendString(buffer, bufferOffset);
438 bufferOffset = 0;
439 }
441 return str;
442 }
446 String& String::Insert(const char* substr, size_t posAt, intptr_t strSize)
447 {
448 DataDesc* poldData = GetData();
449 size_t oldSize = poldData->GetSize();
450 size_t insertSize = (strSize < 0) ? OVR_strlen(substr) : (size_t)strSize;
451 size_t byteIndex = (poldData->LengthIsSize()) ?
452 posAt : (size_t)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize);
454 OVR_ASSERT(byteIndex <= oldSize);
456 DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0,
457 poldData->Data, byteIndex, substr, insertSize);
458 memcpy(pnewData->Data + byteIndex + insertSize,
459 poldData->Data + byteIndex, oldSize - byteIndex);
460 SetData(pnewData);
461 poldData->Release();
462 return *this;
463 }
465 /*
466 String& String::Insert(const uint32_t* substr, size_t posAt, intptr_t len)
467 {
468 for (intptr_t i = 0; i < len; ++i)
469 {
470 size_t charw = InsertCharAt(substr[i], posAt);
471 posAt += charw;
472 }
473 return *this;
474 }
475 */
477 size_t String::InsertCharAt(uint32_t c, size_t posAt)
478 {
479 char buf[8];
480 intptr_t index = 0;
481 UTF8Util::EncodeChar(buf, &index, c);
482 OVR_ASSERT(index >= 0);
483 buf[(size_t)index] = 0;
485 Insert(buf, posAt, index);
486 return (size_t)index;
487 }
490 int String::CompareNoCase(const char* a, const char* b)
491 {
492 return OVR_stricmp(a, b);
493 }
495 int String::CompareNoCase(const char* a, const char* b, intptr_t len)
496 {
497 if (len)
498 {
499 intptr_t f,l;
500 intptr_t slen = len;
501 const char *s = b;
502 do {
503 f = (intptr_t)OVR_tolower((int)(*(a++)));
504 l = (intptr_t)OVR_tolower((int)(*(b++)));
505 } while (--len && f && (f == l) && *b != 0);
507 if (f == l && (len != 0 || *b != 0))
508 {
509 f = (intptr_t)slen;
510 l = (intptr_t)OVR_strlen(s);
511 return int(f - l);
512 }
514 return int(f - l);
515 }
516 else
517 return (0-(int)OVR_strlen(b));
518 }
520 // ***** Implement hash static functions
522 // Hash function
523 size_t String::BernsteinHashFunction(const void* pdataIn, size_t size, size_t seed)
524 {
525 const uint8_t* pdata = (const uint8_t*) pdataIn;
526 size_t h = seed;
527 while (size > 0)
528 {
529 size--;
530 h = ((h << 5) + h) ^ (unsigned) pdata[size];
531 }
533 return h;
534 }
536 // Hash function, case-insensitive
537 size_t String::BernsteinHashFunctionCIS(const void* pdataIn, size_t size, size_t seed)
538 {
539 const uint8_t* pdata = (const uint8_t*) pdataIn;
540 size_t h = seed;
541 while (size > 0)
542 {
543 size--;
544 h = ((h << 5) + h) ^ OVR_tolower(pdata[size]);
545 }
547 // Alternative: "sdbm" hash function, suggested at same web page above.
548 // h = 0;
549 // for bytes { h = (h << 16) + (h << 6) - hash + *p; }
550 return h;
551 }
555 // ***** String Buffer used for Building Strings
558 #define OVR_SBUFF_DEFAULT_GROW_SIZE 512
559 // Constructors / Destructor.
560 StringBuffer::StringBuffer()
561 : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
562 {
563 }
565 StringBuffer::StringBuffer(size_t growSize)
566 : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
567 {
568 SetGrowSize(growSize);
569 }
571 StringBuffer::StringBuffer(const char* data)
572 : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
573 {
574 AppendString(data);
575 }
577 StringBuffer::StringBuffer(const char* data, size_t dataSize)
578 : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
579 {
580 AppendString(data, dataSize);
581 }
583 StringBuffer::StringBuffer(const String& src)
584 : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
585 {
586 AppendString(src.ToCStr(), src.GetSize());
587 }
589 StringBuffer::StringBuffer(const StringBuffer& src)
590 : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
591 {
592 AppendString(src.ToCStr(), src.GetSize());
593 }
595 StringBuffer::StringBuffer(const wchar_t* data)
596 : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false)
597 {
598 *this = data;
599 }
601 StringBuffer::~StringBuffer()
602 {
603 if (pData)
604 OVR_FREE(pData);
605 }
606 void StringBuffer::SetGrowSize(size_t growSize)
607 {
608 if (growSize <= 16)
609 GrowSize = 16;
610 else
611 {
612 uint8_t bits = Alg::UpperBit(uint32_t(growSize-1));
613 size_t size = (size_t)1 << bits;
614 GrowSize = size == growSize ? growSize : size;
615 }
616 }
618 size_t StringBuffer::GetLength() const
619 {
620 size_t length, size = GetSize();
621 if (LengthIsSize)
622 return size;
624 length = (size_t)UTF8Util::GetLength(pData, (size_t)GetSize());
626 if (length == GetSize())
627 LengthIsSize = true;
628 return length;
629 }
631 void StringBuffer::Reserve(size_t _size)
632 {
633 if (_size >= BufferSize) // >= because of trailing zero! (!AB)
634 {
635 BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1);
636 if (!pData)
637 pData = (char*)OVR_ALLOC(BufferSize);
638 else
639 pData = (char*)OVR_REALLOC(pData, BufferSize);
640 }
641 }
642 void StringBuffer::Resize(size_t _size)
643 {
644 Reserve(_size);
645 LengthIsSize = false;
646 Size = _size;
647 if (pData)
648 pData[Size] = 0;
649 }
651 void StringBuffer::Clear()
652 {
653 Resize(0);
654 /*
655 if (pData != pEmptyNullData)
656 {
657 OVR_FREE(pHeap, pData);
658 pData = pEmptyNullData;
659 Size = BufferSize = 0;
660 LengthIsSize = false;
661 }
662 */
663 }
664 // Appends a character
665 void StringBuffer::AppendChar(uint32_t ch)
666 {
667 char buff[8];
668 size_t origSize = GetSize();
670 // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes
671 // in the UTF8 string.
672 intptr_t srcSize = 0;
673 UTF8Util::EncodeChar(buff, &srcSize, ch);
674 OVR_ASSERT(srcSize >= 0);
676 size_t size = origSize + srcSize;
677 Resize(size);
678 OVR_ASSERT(pData != NULL);
679 memcpy(pData + origSize, buff, srcSize);
680 }
682 // Append a string
683 void StringBuffer::AppendString(const wchar_t* pstr, intptr_t len)
684 {
685 if (!pstr || !len)
686 return;
688 intptr_t srcSize = UTF8Util::GetEncodeStringSize(pstr, len);
689 size_t origSize = GetSize();
690 size_t size = srcSize + origSize;
692 Resize(size);
693 OVR_ASSERT(pData != NULL);
694 UTF8Util::EncodeString(pData + origSize, pstr, len);
695 }
697 void StringBuffer::AppendString(const char* putf8str, intptr_t utf8StrSz)
698 {
699 if (!putf8str || !utf8StrSz)
700 return;
701 if (utf8StrSz == -1)
702 utf8StrSz = (intptr_t)OVR_strlen(putf8str);
704 size_t origSize = GetSize();
705 size_t size = utf8StrSz + origSize;
707 Resize(size);
708 OVR_ASSERT(pData != NULL);
709 memcpy(pData + origSize, putf8str, utf8StrSz);
710 }
712 // If pstr is NULL then the StringBuffer is cleared.
713 void StringBuffer::operator = (const char* pstr)
714 {
715 pstr = pstr ? pstr : "";
716 size_t size = OVR_strlen(pstr);
717 Resize(size);
718 OVR_ASSERT((pData != NULL) || (size == 0));
719 memcpy(pData, pstr, size);
720 }
722 // If pstr is NULL then the StringBuffer is cleared.
723 void StringBuffer::operator = (const wchar_t* pstr)
724 {
725 pstr = pstr ? pstr : L"";
726 size_t size = (size_t)UTF8Util::GetEncodeStringSize(pstr);
727 Resize(size);
728 OVR_ASSERT((pData != NULL) || (size == 0));
729 UTF8Util::EncodeString(pData, pstr);
730 }
732 void StringBuffer::operator = (const String& src)
733 {
734 const size_t size = src.GetSize();
735 Resize(size);
736 OVR_ASSERT((pData != NULL) || (size == 0));
737 memcpy(pData, src.ToCStr(), size);
738 }
740 void StringBuffer::operator = (const StringBuffer& src)
741 {
742 Clear();
743 AppendString(src.ToCStr(), src.GetSize());
744 }
747 // Inserts substr at posAt
748 void StringBuffer::Insert(const char* substr, size_t posAt, intptr_t len)
749 {
750 size_t oldSize = Size;
751 size_t insertSize = (len < 0) ? OVR_strlen(substr) : (size_t)len;
752 size_t byteIndex = LengthIsSize ? posAt :
753 (size_t)UTF8Util::GetByteIndex(posAt, pData, (intptr_t)Size);
755 OVR_ASSERT(byteIndex <= oldSize);
756 Reserve(oldSize + insertSize);
758 OVR_ASSERT(pData != NULL); // pData is unilaterally written to below.
759 memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1);
760 memcpy (pData + byteIndex, substr, insertSize);
761 LengthIsSize = false;
762 Size = oldSize + insertSize;
763 pData[Size] = 0;
764 }
766 // Inserts character at posAt
767 size_t StringBuffer::InsertCharAt(uint32_t c, size_t posAt)
768 {
769 char buf[8];
770 intptr_t len = 0;
771 UTF8Util::EncodeChar(buf, &len, c);
772 OVR_ASSERT(len >= 0);
773 buf[(size_t)len] = 0;
775 Insert(buf, posAt, len);
776 return (size_t)len;
777 }
779 } // OVR