1 /*
2 ** zstring.cpp
3 ** A dynamically-allocated string class.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2005-2008 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include <string.h>
38 #include <new> // for bad_alloc
39
40 #include "zstring.h"
41
42 FNullStringData FString::NullString =
43 {
44 0, // Length of string
45 2, // Size of character buffer
46 2, // RefCount; it must never be modified, so keep it above 1 user at all times
47 "\0"
48 };
49
AttachToOther(const FString & other)50 void FString::AttachToOther (const FString &other)
51 {
52 assert (other.Chars != NULL);
53
54 if (other.Data()->RefCount < 0)
55 {
56 AllocBuffer (other.Data()->Len);
57 StrCopy (Chars, other.Chars, other.Data()->Len);
58 }
59 else
60 {
61 Chars = const_cast<FString &>(other).Data()->AddRef();
62 }
63 }
64
FString(const char * copyStr)65 FString::FString (const char *copyStr)
66 {
67 if (copyStr == NULL || *copyStr == '\0')
68 {
69 NullString.RefCount++;
70 Chars = &NullString.Nothing[0];
71 }
72 else
73 {
74 size_t len = strlen (copyStr);
75 AllocBuffer (len);
76 StrCopy (Chars, copyStr, len);
77 }
78 }
79
FString(const char * copyStr,size_t len)80 FString::FString (const char *copyStr, size_t len)
81 {
82 AllocBuffer (len);
83 StrCopy (Chars, copyStr, len);
84 }
85
FString(char oneChar)86 FString::FString (char oneChar)
87 {
88 if (oneChar == '\0')
89 {
90 NullString.RefCount++;
91 Chars = &NullString.Nothing[0];
92 }
93 else
94 {
95 AllocBuffer (1);
96 Chars[0] = oneChar;
97 Chars[1] = '\0';
98 }
99 }
100
FString(const FString & head,const FString & tail)101 FString::FString (const FString &head, const FString &tail)
102 {
103 size_t len1 = head.Len();
104 size_t len2 = tail.Len();
105 AllocBuffer (len1 + len2);
106 StrCopy (Chars, head);
107 StrCopy (Chars + len1, tail);
108 }
109
FString(const FString & head,const char * tail)110 FString::FString (const FString &head, const char *tail)
111 {
112 size_t len1 = head.Len();
113 size_t len2 = strlen (tail);
114 AllocBuffer (len1 + len2);
115 StrCopy (Chars, head);
116 StrCopy (Chars + len1, tail, len2);
117 }
118
FString(const FString & head,char tail)119 FString::FString (const FString &head, char tail)
120 {
121 size_t len1 = head.Len();
122 AllocBuffer (len1 + 1);
123 StrCopy (Chars, head);
124 Chars[len1] = tail;
125 Chars[len1+1] = '\0';
126 }
127
FString(const char * head,const FString & tail)128 FString::FString (const char *head, const FString &tail)
129 {
130 size_t len1 = strlen (head);
131 size_t len2 = tail.Len();
132 AllocBuffer (len1 + len2);
133 StrCopy (Chars, head, len1);
134 StrCopy (Chars + len1, tail);
135 }
136
FString(const char * head,const char * tail)137 FString::FString (const char *head, const char *tail)
138 {
139 size_t len1 = strlen (head);
140 size_t len2 = strlen (tail);
141 AllocBuffer (len1 + len2);
142 StrCopy (Chars, head, len1);
143 StrCopy (Chars + len1, tail, len2);
144 }
145
FString(char head,const FString & tail)146 FString::FString (char head, const FString &tail)
147 {
148 size_t len2 = tail.Len();
149 AllocBuffer (1 + len2);
150 Chars[0] = head;
151 StrCopy (Chars + 1, tail);
152 }
153
~FString()154 FString::~FString ()
155 {
156 Data()->Release();
157 }
158
LockNewBuffer(size_t len)159 char *FString::LockNewBuffer(size_t len)
160 {
161 Data()->Release();
162 AllocBuffer(len);
163 assert(Data()->RefCount == 1);
164 Data()->RefCount = -1;
165 return Chars;
166 }
167
LockBuffer()168 char *FString::LockBuffer()
169 {
170 if (Data()->RefCount == 1)
171 { // We're the only user, so we can lock it straight away
172 Data()->RefCount = -1;
173 }
174 else if (Data()->RefCount < -1)
175 { // Already locked; just add to the lock count
176 Data()->RefCount--;
177 }
178 else
179 { // Somebody else is also using this character buffer, so create a copy
180 FStringData *old = Data();
181 AllocBuffer (old->Len);
182 StrCopy (Chars, old->Chars(), old->Len);
183 old->Release();
184 Data()->RefCount = -1;
185 }
186 return Chars;
187 }
188
UnlockBuffer()189 void FString::UnlockBuffer()
190 {
191 assert (Data()->RefCount < 0);
192
193 if (++Data()->RefCount == 0)
194 {
195 Data()->RefCount = 1;
196 }
197 }
198
operator =(const FString & other)199 FString &FString::operator = (const FString &other)
200 {
201 assert (Chars != NULL);
202
203 if (&other != this)
204 {
205 int oldrefcount = Data()->RefCount < 0;
206 Data()->Release();
207 AttachToOther(other);
208 if (oldrefcount < 0)
209 {
210 LockBuffer();
211 Data()->RefCount = oldrefcount;
212 }
213 }
214 return *this;
215 }
operator =(const char * copyStr)216 FString &FString::operator = (const char *copyStr)
217 {
218 if (copyStr != Chars)
219 {
220 if (copyStr == NULL || *copyStr == '\0')
221 {
222 Data()->Release();
223 NullString.RefCount++;
224 Chars = &NullString.Nothing[0];
225 }
226 else
227 {
228 // In case copyStr is inside us, we can't release it until
229 // we've finished the copy.
230 FStringData *old = Data();
231
232 if (copyStr < Chars || copyStr >= Chars + old->Len)
233 {
234 // We know the string isn't in our buffer, so release it now
235 // to reduce the potential for needless memory fragmentation.
236 old->Release();
237 old = NULL;
238 }
239 size_t len = strlen (copyStr);
240 AllocBuffer (len);
241 StrCopy (Chars, copyStr, len);
242 if (old != NULL)
243 {
244 old->Release();
245 }
246 }
247 }
248 return *this;
249 }
250
Format(const char * fmt,...)251 void FString::Format (const char *fmt, ...)
252 {
253 va_list arglist;
254 va_start (arglist, fmt);
255 VFormat (fmt, arglist);
256 va_end (arglist);
257 }
258
AppendFormat(const char * fmt,...)259 void FString::AppendFormat (const char *fmt, ...)
260 {
261 va_list arglist;
262 va_start (arglist, fmt);
263 StringFormat::VWorker (FormatHelper, this, fmt, arglist);
264 va_end (arglist);
265 }
266
VFormat(const char * fmt,va_list arglist)267 void FString::VFormat (const char *fmt, va_list arglist)
268 {
269 Data()->Release();
270 Chars = (char *)(FStringData::Alloc(128) + 1);
271 StringFormat::VWorker (FormatHelper, this, fmt, arglist);
272 }
273
VAppendFormat(const char * fmt,va_list arglist)274 void FString::VAppendFormat (const char *fmt, va_list arglist)
275 {
276 StringFormat::VWorker (FormatHelper, this, fmt, arglist);
277 }
278
FormatHelper(void * data,const char * cstr,int len)279 int FString::FormatHelper (void *data, const char *cstr, int len)
280 {
281 FString *str = (FString *)data;
282 size_t len1 = str->Len();
283 if (len1 + len > str->Data()->AllocLen || str->Chars == &NullString.Nothing[0])
284 {
285 str->ReallocBuffer((len1 + len + 127) & ~127);
286 }
287 StrCopy (str->Chars + len1, cstr, len);
288 str->Data()->Len = (unsigned int)(len1 + len);
289 return len;
290 }
291
operator +(const FString & tail) const292 FString FString::operator + (const FString &tail) const
293 {
294 return FString (*this, tail);
295 }
296
operator +(const char * tail) const297 FString FString::operator + (const char *tail) const
298 {
299 return FString (*this, tail);
300 }
301
operator +(const char * head,const FString & tail)302 FString operator + (const char *head, const FString &tail)
303 {
304 return FString (head, tail);
305 }
306
operator +(char tail) const307 FString FString::operator + (char tail) const
308 {
309 return FString (*this, tail);
310 }
311
operator +(char head,const FString & tail)312 FString operator + (char head, const FString &tail)
313 {
314 return FString (head, tail);
315 }
316
operator +=(const FString & tail)317 FString &FString::operator += (const FString &tail)
318 {
319 size_t len1 = Len();
320 size_t len2 = tail.Len();
321 ReallocBuffer (len1 + len2);
322 StrCopy (Chars + len1, tail);
323 return *this;
324 }
325
operator +=(const char * tail)326 FString &FString::operator += (const char *tail)
327 {
328 size_t len1 = Len();
329 size_t len2 = strlen(tail);
330 ReallocBuffer (len1 + len2);
331 StrCopy (Chars + len1, tail, len2);
332 return *this;
333 }
334
operator +=(char tail)335 FString &FString::operator += (char tail)
336 {
337 size_t len1 = Len();
338 ReallocBuffer (len1 + 1);
339 Chars[len1] = tail;
340 Chars[len1+1] = '\0';
341 return *this;
342 }
343
AppendCStrPart(const char * tail,size_t tailLen)344 FString &FString::AppendCStrPart (const char *tail, size_t tailLen)
345 {
346 size_t len1 = Len();
347 ReallocBuffer (len1 + tailLen);
348 StrCopy (Chars + len1, tail, tailLen);
349 return *this;
350 }
351
CopyCStrPart(const char * tail,size_t tailLen)352 FString &FString::CopyCStrPart(const char *tail, size_t tailLen)
353 {
354 ReallocBuffer(tailLen);
355 StrCopy(Chars, tail, tailLen);
356 return *this;
357 }
358
Truncate(long newlen)359 void FString::Truncate(long newlen)
360 {
361 if (newlen >= 0 && newlen < (long)Len())
362 {
363 ReallocBuffer (newlen);
364 Chars[newlen] = '\0';
365 }
366 }
367
Left(size_t numChars) const368 FString FString::Left (size_t numChars) const
369 {
370 size_t len = Len();
371 if (len < numChars)
372 {
373 numChars = len;
374 }
375 return FString (Chars, numChars);
376 }
377
Right(size_t numChars) const378 FString FString::Right (size_t numChars) const
379 {
380 size_t len = Len();
381 if (len < numChars)
382 {
383 numChars = len;
384 }
385 return FString (Chars + len - numChars, numChars);
386 }
387
Mid(size_t pos,size_t numChars) const388 FString FString::Mid (size_t pos, size_t numChars) const
389 {
390 size_t len = Len();
391 if (pos >= len)
392 {
393 return FString();
394 }
395 if (pos + numChars > len || pos + numChars < pos)
396 {
397 numChars = len - pos;
398 }
399 return FString (Chars + pos, numChars);
400 }
401
IndexOf(const FString & substr,long startIndex) const402 long FString::IndexOf (const FString &substr, long startIndex) const
403 {
404 return IndexOf (substr.Chars, startIndex);
405 }
406
IndexOf(const char * substr,long startIndex) const407 long FString::IndexOf (const char *substr, long startIndex) const
408 {
409 if (startIndex > 0 && Len() <= (size_t)startIndex)
410 {
411 return -1;
412 }
413 char *str = strstr (Chars + startIndex, substr);
414 if (str == NULL)
415 {
416 return -1;
417 }
418 return long(str - Chars);
419 }
420
IndexOf(char subchar,long startIndex) const421 long FString::IndexOf (char subchar, long startIndex) const
422 {
423 if (startIndex > 0 && Len() <= (size_t)startIndex)
424 {
425 return -1;
426 }
427 char *str = strchr (Chars + startIndex, subchar);
428 if (str == NULL)
429 {
430 return -1;
431 }
432 return long(str - Chars);
433 }
434
IndexOfAny(const FString & charset,long startIndex) const435 long FString::IndexOfAny (const FString &charset, long startIndex) const
436 {
437 return IndexOfAny (charset.Chars, startIndex);
438 }
439
IndexOfAny(const char * charset,long startIndex) const440 long FString::IndexOfAny (const char *charset, long startIndex) const
441 {
442 if (startIndex > 0 && Len() <= (size_t)startIndex)
443 {
444 return -1;
445 }
446 char *brk = strpbrk (Chars + startIndex, charset);
447 if (brk == NULL)
448 {
449 return -1;
450 }
451 return long(brk - Chars);
452 }
453
LastIndexOf(const FString & substr) const454 long FString::LastIndexOf (const FString &substr) const
455 {
456 return LastIndexOf (substr.Chars, long(Len()), substr.Len());
457 }
458
LastIndexOf(const char * substr) const459 long FString::LastIndexOf (const char *substr) const
460 {
461 return LastIndexOf (substr, long(Len()), strlen(substr));
462 }
463
LastIndexOf(char subchar) const464 long FString::LastIndexOf (char subchar) const
465 {
466 return LastIndexOf (subchar, long(Len()));
467 }
468
LastIndexOf(const FString & substr,long endIndex) const469 long FString::LastIndexOf (const FString &substr, long endIndex) const
470 {
471 return LastIndexOf (substr.Chars, endIndex, substr.Len());
472 }
473
LastIndexOf(const char * substr,long endIndex) const474 long FString::LastIndexOf (const char *substr, long endIndex) const
475 {
476 return LastIndexOf (substr, endIndex, strlen(substr));
477 }
478
LastIndexOf(char subchar,long endIndex) const479 long FString::LastIndexOf (char subchar, long endIndex) const
480 {
481 if ((size_t)endIndex > Len())
482 {
483 endIndex = long(Len());
484 }
485 while (--endIndex >= 0)
486 {
487 if (Chars[endIndex] == subchar)
488 {
489 return endIndex;
490 }
491 }
492 return -1;
493 }
494
LastIndexOf(const char * substr,long endIndex,size_t substrlen) const495 long FString::LastIndexOf (const char *substr, long endIndex, size_t substrlen) const
496 {
497 if ((size_t)endIndex > Len())
498 {
499 endIndex = long(Len());
500 }
501 substrlen--;
502 while (--endIndex >= long(substrlen))
503 {
504 if (strncmp (substr, Chars + endIndex - substrlen, substrlen + 1) == 0)
505 {
506 return endIndex;
507 }
508 }
509 return -1;
510 }
511
LastIndexOfAny(const FString & charset) const512 long FString::LastIndexOfAny (const FString &charset) const
513 {
514 return LastIndexOfAny (charset.Chars, long(Len()));
515 }
516
LastIndexOfAny(const char * charset) const517 long FString::LastIndexOfAny (const char *charset) const
518 {
519 return LastIndexOfAny (charset, long(Len()));
520 }
521
LastIndexOfAny(const FString & charset,long endIndex) const522 long FString::LastIndexOfAny (const FString &charset, long endIndex) const
523 {
524 return LastIndexOfAny (charset.Chars, endIndex);
525 }
526
LastIndexOfAny(const char * charset,long endIndex) const527 long FString::LastIndexOfAny (const char *charset, long endIndex) const
528 {
529 if ((size_t)endIndex > Len())
530 {
531 endIndex = long(Len());
532 }
533 while (--endIndex >= 0)
534 {
535 if (strchr (charset, Chars[endIndex]) != NULL)
536 {
537 return endIndex;
538 }
539 }
540 return -1;
541 }
542
ToUpper()543 void FString::ToUpper ()
544 {
545 LockBuffer();
546 size_t max = Len();
547 for (size_t i = 0; i < max; ++i)
548 {
549 Chars[i] = (char)toupper(Chars[i]);
550 }
551 UnlockBuffer();
552 }
553
ToLower()554 void FString::ToLower ()
555 {
556 LockBuffer();
557 size_t max = Len();
558 for (size_t i = 0; i < max; ++i)
559 {
560 Chars[i] = (char)tolower(Chars[i]);
561 }
562 UnlockBuffer();
563 }
564
SwapCase()565 void FString::SwapCase ()
566 {
567 LockBuffer();
568 size_t max = Len();
569 for (size_t i = 0; i < max; ++i)
570 {
571 if (isupper(Chars[i]))
572 {
573 Chars[i] = (char)tolower(Chars[i]);
574 }
575 else
576 {
577 Chars[i] = (char)toupper(Chars[i]);
578 }
579 }
580 UnlockBuffer();
581 }
582
StripLeft()583 void FString::StripLeft ()
584 {
585 size_t max = Len(), i, j;
586 if (max == 0) return;
587 for (i = 0; i < max; ++i)
588 {
589 if (!isspace(Chars[i]))
590 break;
591 }
592 if (Data()->RefCount <= 1)
593 {
594 for (j = 0; i <= max; ++j, ++i)
595 {
596 Chars[j] = Chars[i];
597 }
598 ReallocBuffer (j-1);
599 }
600 else
601 {
602 FStringData *old = Data();
603 AllocBuffer (max - i);
604 StrCopy (Chars, old->Chars() + i, max - i);
605 old->Release();
606 }
607 }
608
StripLeft(const FString & charset)609 void FString::StripLeft (const FString &charset)
610 {
611 return StripLeft (charset.Chars);
612 }
613
StripLeft(const char * charset)614 void FString::StripLeft (const char *charset)
615 {
616 size_t max = Len(), i, j;
617 if (max == 0) return;
618 for (i = 0; i < max; ++i)
619 {
620 if (!strchr (charset, Chars[i]))
621 break;
622 }
623 if (Data()->RefCount <= 1)
624 {
625 for (j = 0; i <= max; ++j, ++i)
626 {
627 Chars[j] = Chars[i];
628 }
629 ReallocBuffer (j-1);
630 }
631 else
632 {
633 FStringData *old = Data();
634 AllocBuffer (max - i);
635 StrCopy (Chars, old->Chars() + i, max - i);
636 old->Release();
637 }
638 }
639
StripRight()640 void FString::StripRight ()
641 {
642 size_t max = Len(), i;
643 for (i = max; i-- > 0; )
644 {
645 if (!isspace(Chars[i]))
646 break;
647 }
648 if (Data()->RefCount <= 1)
649 {
650 Chars[i+1] = '\0';
651 ReallocBuffer (i+1);
652 }
653 else
654 {
655 FStringData *old = Data();
656 AllocBuffer (i+1);
657 StrCopy (Chars, old->Chars(), i+1);
658 old->Release();
659 }
660 }
661
StripRight(const FString & charset)662 void FString::StripRight (const FString &charset)
663 {
664 return StripRight (charset.Chars);
665 }
666
StripRight(const char * charset)667 void FString::StripRight (const char *charset)
668 {
669 size_t max = Len(), i;
670 if (max == 0) return;
671 for (i = max; i-- > 0; )
672 {
673 if (!strchr (charset, Chars[i]))
674 break;
675 }
676 if (Data()->RefCount <= 1)
677 {
678 Chars[i+1] = '\0';
679 ReallocBuffer (i+1);
680 }
681 else
682 {
683 FStringData *old = Data();
684 AllocBuffer (i+1);
685 StrCopy (Chars, old->Chars(), i+1);
686 old->Release();
687 }
688 }
689
StripLeftRight()690 void FString::StripLeftRight ()
691 {
692 size_t max = Len(), i, j, k;
693 if (max == 0) return;
694 for (i = 0; i < max; ++i)
695 {
696 if (!isspace(Chars[i]))
697 break;
698 }
699 for (j = max - 1; j >= i; --j)
700 {
701 if (!isspace(Chars[j]))
702 break;
703 }
704 if (Data()->RefCount <= 1)
705 {
706 for (k = 0; i <= j; ++i, ++k)
707 {
708 Chars[k] = Chars[i];
709 }
710 Chars[k] = '\0';
711 ReallocBuffer (k);
712 }
713 else
714 {
715 FStringData *old = Data();
716 AllocBuffer (j - i);
717 StrCopy (Chars, old->Chars(), j - i);
718 old->Release();
719 }
720 }
721
StripLeftRight(const FString & charset)722 void FString::StripLeftRight (const FString &charset)
723 {
724 return StripLeftRight (charset.Chars);
725 }
726
StripLeftRight(const char * charset)727 void FString::StripLeftRight (const char *charset)
728 {
729 size_t max = Len(), i, j, k;
730 if (max == 0) return;
731 for (i = 0; i < max; ++i)
732 {
733 if (!strchr (charset, Chars[i]))
734 break;
735 }
736 for (j = max - 1; j >= i; --j)
737 {
738 if (!strchr (charset, Chars[j]))
739 break;
740 }
741 if (Data()->RefCount <= 1)
742 {
743 for (k = 0; i <= j; ++i, ++k)
744 {
745 Chars[k] = Chars[i];
746 }
747 Chars[k] = '\0';
748 ReallocBuffer (k);
749 }
750 else
751 {
752 FStringData *old = Data();
753 AllocBuffer (j - i);
754 StrCopy (Chars, old->Chars(), j - i);
755 old->Release();
756 }
757 }
758
Insert(size_t index,const FString & instr)759 void FString::Insert (size_t index, const FString &instr)
760 {
761 Insert (index, instr.Chars, instr.Len());
762 }
763
Insert(size_t index,const char * instr)764 void FString::Insert (size_t index, const char *instr)
765 {
766 Insert (index, instr, strlen(instr));
767 }
768
Insert(size_t index,const char * instr,size_t instrlen)769 void FString::Insert (size_t index, const char *instr, size_t instrlen)
770 {
771 size_t mylen = Len();
772 if (index > mylen)
773 {
774 index = mylen;
775 }
776 if (Data()->RefCount <= 1)
777 {
778 ReallocBuffer (mylen + instrlen);
779 memmove (Chars + index + instrlen, Chars + index, (mylen - index + 1)*sizeof(char));
780 memcpy (Chars + index, instr, instrlen*sizeof(char));
781 }
782 else
783 {
784 FStringData *old = Data();
785 AllocBuffer (mylen + instrlen);
786 StrCopy (Chars, old->Chars(), index);
787 StrCopy (Chars + index, instr, instrlen);
788 StrCopy (Chars + index + instrlen, old->Chars() + index, mylen - index);
789 old->Release();
790 }
791 }
792
ReplaceChars(char oldchar,char newchar)793 void FString::ReplaceChars (char oldchar, char newchar)
794 {
795 size_t i, j;
796
797 LockBuffer();
798 for (i = 0, j = Len(); i < j; ++i)
799 {
800 if (Chars[i] == oldchar)
801 {
802 Chars[i] = newchar;
803 }
804 }
805 UnlockBuffer();
806 }
807
ReplaceChars(const char * oldcharset,char newchar)808 void FString::ReplaceChars (const char *oldcharset, char newchar)
809 {
810 size_t i, j;
811
812 LockBuffer();
813 for (i = 0, j = Len(); i < j; ++i)
814 {
815 if (strchr (oldcharset, Chars[i]) != NULL)
816 {
817 Chars[i] = newchar;
818 }
819 }
820 UnlockBuffer();
821 }
822
StripChars(char killchar)823 void FString::StripChars (char killchar)
824 {
825 size_t read, write, mylen;
826
827 LockBuffer();
828 for (read = write = 0, mylen = Len(); read < mylen; ++read)
829 {
830 if (Chars[read] != killchar)
831 {
832 Chars[write++] = Chars[read];
833 }
834 }
835 Chars[write] = '\0';
836 ReallocBuffer (write);
837 UnlockBuffer();
838 }
839
StripChars(const char * killchars)840 void FString::StripChars (const char *killchars)
841 {
842 size_t read, write, mylen;
843
844 LockBuffer();
845 for (read = write = 0, mylen = Len(); read < mylen; ++read)
846 {
847 if (strchr (killchars, Chars[read]) == NULL)
848 {
849 Chars[write++] = Chars[read];
850 }
851 }
852 Chars[write] = '\0';
853 ReallocBuffer (write);
854 UnlockBuffer();
855 }
856
MergeChars(char merger)857 void FString::MergeChars (char merger)
858 {
859 MergeChars (merger, merger);
860 }
861
MergeChars(char merger,char newchar)862 void FString::MergeChars (char merger, char newchar)
863 {
864 size_t read, write, mylen;
865
866 LockBuffer();
867 for (read = write = 0, mylen = Len(); read < mylen; )
868 {
869 if (Chars[read] == merger)
870 {
871 while (Chars[++read] == merger)
872 {
873 }
874 Chars[write++] = newchar;
875 }
876 else
877 {
878 Chars[write++] = Chars[read++];
879 }
880 }
881 Chars[write] = '\0';
882 ReallocBuffer (write);
883 UnlockBuffer();
884 }
885
MergeChars(const char * charset,char newchar)886 void FString::MergeChars (const char *charset, char newchar)
887 {
888 size_t read, write, mylen;
889
890 LockBuffer();
891 for (read = write = 0, mylen = Len(); read < mylen; )
892 {
893 if (strchr (charset, Chars[read]) != NULL)
894 {
895 while (strchr (charset, Chars[++read]) != NULL)
896 {
897 }
898 Chars[write++] = newchar;
899 }
900 else
901 {
902 Chars[write++] = Chars[read++];
903 }
904 }
905 Chars[write] = '\0';
906 ReallocBuffer (write);
907 UnlockBuffer();
908 }
909
Substitute(const FString & oldstr,const FString & newstr)910 void FString::Substitute (const FString &oldstr, const FString &newstr)
911 {
912 return Substitute (oldstr.Chars, newstr.Chars, oldstr.Len(), newstr.Len());
913 }
914
Substitute(const char * oldstr,const FString & newstr)915 void FString::Substitute (const char *oldstr, const FString &newstr)
916 {
917 return Substitute (oldstr, newstr.Chars, strlen(oldstr), newstr.Len());
918 }
919
Substitute(const FString & oldstr,const char * newstr)920 void FString::Substitute (const FString &oldstr, const char *newstr)
921 {
922 return Substitute (oldstr.Chars, newstr, oldstr.Len(), strlen(newstr));
923 }
924
Substitute(const char * oldstr,const char * newstr)925 void FString::Substitute (const char *oldstr, const char *newstr)
926 {
927 return Substitute (oldstr, newstr, strlen(oldstr), strlen(newstr));
928 }
929
Substitute(const char * oldstr,const char * newstr,size_t oldstrlen,size_t newstrlen)930 void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrlen, size_t newstrlen)
931 {
932 LockBuffer();
933 for (size_t checkpt = 0; checkpt < Len(); )
934 {
935 char *match = strstr (Chars + checkpt, oldstr);
936 size_t len = Len();
937 if (match != NULL)
938 {
939 size_t matchpt = match - Chars;
940 if (oldstrlen != newstrlen)
941 {
942 ReallocBuffer (len + newstrlen - oldstrlen);
943 memmove (Chars + matchpt + newstrlen, Chars + matchpt + oldstrlen, (len + 1 - matchpt - oldstrlen)*sizeof(char));
944 }
945 memcpy (Chars + matchpt, newstr, newstrlen);
946 checkpt = matchpt + newstrlen;
947 }
948 else
949 {
950 break;
951 }
952 }
953 UnlockBuffer();
954 }
955
IsInt() const956 bool FString::IsInt () const
957 {
958 // String must match: [whitespace] [{+ | �}] [0 [{ x | X }]] [digits] [whitespace]
959
960 /* This state machine is based on a simplification of re2c's output for this input:
961 digits = [0-9];
962 hexdigits = [0-9a-fA-F];
963 octdigits = [0-7];
964
965 ("0" octdigits+ | "0" [xX] hexdigits+ | (digits \ '0') digits*) { return true; }
966 [\000-\377] { return false; }*/
967 const char *YYCURSOR = Chars;
968 char yych;
969
970 yych = *YYCURSOR;
971
972 // Skip preceding whitespace
973 while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; }
974
975 // Check for sign
976 if (yych == '+' || yych == '-') { yych = *++YYCURSOR; }
977
978 if (yych == '0')
979 {
980 yych = *++YYCURSOR;
981 if (yych >= '0' && yych <= '7')
982 {
983 do { yych = *++YYCURSOR; } while (yych >= '0' && yych <= '7');
984 }
985 else if (yych == 'X' || yych == 'x')
986 {
987 bool gothex = false;
988 yych = *++YYCURSOR;
989 while ((yych >= '0' && yych <= '9') || (yych >= 'A' && yych <= 'F') || (yych >= 'a' && yych <= 'f'))
990 {
991 gothex = true;
992 yych = *++YYCURSOR;
993 }
994 if (!gothex) return false;
995 }
996 else
997 {
998 return false;
999 }
1000 }
1001 else if (yych >= '1' && yych <= '9')
1002 {
1003 do { yych = *++YYCURSOR; } while (yych >= '0' && yych <= '9');
1004 }
1005 else
1006 {
1007 return false;
1008 }
1009
1010 // The rest should all be whitespace
1011 while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; }
1012 return yych == '\0';
1013 }
1014
IsFloat() const1015 bool FString::IsFloat () const
1016 {
1017 // String must match: [whitespace] [sign] [digits] [.digits] [ {d | D | e | E}[sign]digits] [whitespace]
1018 /* This state machine is based on a simplification of re2c's output for this input:
1019 digits = [0-9];
1020
1021 (digits+ | digits* "." digits+) ([dDeE] [+-]? digits+)? { return true; }
1022 [\000-\377] { return false; }
1023 */
1024 const char *YYCURSOR = Chars;
1025 char yych;
1026 bool gotdig = false;
1027
1028 yych = *YYCURSOR;
1029
1030 // Skip preceding whitespace
1031 while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; }
1032
1033 // Check for sign
1034 if (yych == '+' || yych == '-') { yych = *++YYCURSOR; }
1035
1036 while (yych >= '0' && yych <= '9')
1037 {
1038 gotdig = true;
1039 yych = *++YYCURSOR;
1040 }
1041 if (yych == '.')
1042 {
1043 yych = *++YYCURSOR;
1044 if (yych >= '0' && yych <= '9')
1045 {
1046 gotdig = true;
1047 do { yych = *++YYCURSOR; } while (yych >= '0' && yych <= '9');
1048 }
1049 else return false;
1050 }
1051 if (gotdig)
1052 {
1053 if (yych == 'D' || yych == 'd' || yych == 'E' || yych == 'e')
1054 {
1055 yych = *++YYCURSOR;
1056 if (yych == '+' || yych == '-') yych = *++YYCURSOR;
1057 while (yych >= '0' && yych <= '9') { yych = *++YYCURSOR; }
1058 }
1059 }
1060
1061 // The rest should all be whitespace
1062 while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; }
1063 return yych == '\0';
1064 }
1065
ToLong(int base) const1066 long FString::ToLong (int base) const
1067 {
1068 return strtol (Chars, NULL, base);
1069 }
1070
ToULong(int base) const1071 unsigned long FString::ToULong (int base) const
1072 {
1073 return strtoul (Chars, NULL, base);
1074 }
1075
ToDouble() const1076 double FString::ToDouble () const
1077 {
1078 return strtod (Chars, NULL);
1079 }
1080
StrCopy(char * to,const char * from,size_t len)1081 void FString::StrCopy (char *to, const char *from, size_t len)
1082 {
1083 memcpy (to, from, len*sizeof(char));
1084 to[len] = 0;
1085 }
1086
StrCopy(char * to,const FString & from)1087 void FString::StrCopy (char *to, const FString &from)
1088 {
1089 StrCopy (to, from.Chars, from.Len());
1090 }
1091
AllocBuffer(size_t len)1092 void FString::AllocBuffer (size_t len)
1093 {
1094 Chars = (char *)(FStringData::Alloc(len) + 1);
1095 Data()->Len = (unsigned int)len;
1096 }
1097
ReallocBuffer(size_t newlen)1098 void FString::ReallocBuffer (size_t newlen)
1099 {
1100 if (Data()->RefCount > 1)
1101 { // If more than one reference, we must use a new copy
1102 FStringData *old = Data();
1103 AllocBuffer (newlen);
1104 StrCopy (Chars, old->Chars(), newlen < old->Len ? newlen : old->Len);
1105 old->Release();
1106 }
1107 else
1108 {
1109 if (newlen > Data()->AllocLen)
1110 {
1111 Chars = (char *)(Data()->Realloc(newlen) + 1);
1112 }
1113 Data()->Len = (unsigned int)newlen;
1114 }
1115 }
1116
1117 // Under Windows, use the system heap functions for managing string memory.
1118 // Under other OSs, use ordinary memory management instead.
1119
1120 #ifdef _WIN32
1121 #define WIN32_LEAN_AND_MEAN
1122 #include <windows.h>
1123
1124 static HANDLE StringHeap;
1125 const SIZE_T STRING_HEAP_SIZE = 64*1024;
1126 #endif
1127
Alloc(size_t strlen)1128 FStringData *FStringData::Alloc (size_t strlen)
1129 {
1130 strlen += 1 + sizeof(FStringData); // Add space for header and terminating null
1131 strlen = (strlen + 7) & ~7; // Pad length up
1132
1133 #ifdef _WIN32
1134 if (StringHeap == NULL)
1135 {
1136 StringHeap = HeapCreate (0, STRING_HEAP_SIZE, 0);
1137 if (StringHeap == NULL)
1138 {
1139 throw std::bad_alloc();
1140 }
1141 }
1142
1143 FStringData *block = (FStringData *)HeapAlloc (StringHeap, 0, strlen);
1144 #else
1145 FStringData *block = (FStringData *)malloc (strlen);
1146 #endif
1147 if (block == NULL)
1148 {
1149 throw std::bad_alloc();
1150 }
1151 block->Len = 0;
1152 block->AllocLen = (unsigned int)strlen - sizeof(FStringData) - 1;
1153 block->RefCount = 1;
1154 return block;
1155 }
1156
Realloc(size_t newstrlen)1157 FStringData *FStringData::Realloc (size_t newstrlen)
1158 {
1159 assert (RefCount <= 1);
1160
1161 newstrlen += 1 + sizeof(FStringData); // Add space for header and terminating null
1162 newstrlen = (newstrlen + 7) & ~7; // Pad length up
1163
1164 #ifdef _WIN32
1165 FStringData *block = (FStringData *)HeapReAlloc (StringHeap, 0, this, newstrlen);
1166 #else
1167 FStringData *block = (FStringData *)realloc (this, newstrlen);
1168 #endif
1169 if (block == NULL)
1170 {
1171 throw std::bad_alloc();
1172 }
1173 block->AllocLen = (unsigned int)newstrlen - sizeof(FStringData) - 1;
1174 return block;
1175 }
1176
Dealloc()1177 void FStringData::Dealloc ()
1178 {
1179 assert (RefCount <= 0);
1180
1181 #ifdef _WIN32
1182 HeapFree (StringHeap, 0, this);
1183 #else
1184 free (this);
1185 #endif
1186 }
1187
MakeCopy()1188 FStringData *FStringData::MakeCopy ()
1189 {
1190 FStringData *copy = Alloc (Len);
1191 copy->Len = Len;
1192 FString::StrCopy (copy->Chars(), Chars(), Len);
1193 return copy;
1194 }
1195