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