#include #include #include #include // for bad_alloc #include "zstring.h" FNullStringData FString::NullString = { 0, // Length of string 2, // Size of character buffer 2, // RefCount; it must never be modified, so keep it above 1 user at all times "\0" }; void FString::AttachToOther (const FString &other) { assert (other.Chars != NULL); if (other.Data()->RefCount < 0) { AllocBuffer (other.Data()->Len); StrCopy (Chars, other.Chars, other.Data()->Len); } else { Chars = const_cast(other).Data()->AddRef(); } } FString::FString (const char *copyStr) { if (copyStr == NULL || *copyStr == '\0') { NullString.RefCount++; Chars = &NullString.Nothing[0]; } else { size_t len = strlen (copyStr); AllocBuffer (len); StrCopy (Chars, copyStr, len); } } FString::FString (const char *copyStr, size_t len) { AllocBuffer (len); StrCopy (Chars, copyStr, len); } FString::FString (char oneChar) { if (oneChar == '\0') { NullString.RefCount++; Chars = &NullString.Nothing[0]; } else { AllocBuffer (1); Chars[0] = oneChar; Chars[1] = '\0'; } } FString::FString (const FString &head, const FString &tail) { size_t len1 = head.Len(); size_t len2 = tail.Len(); AllocBuffer (len1 + len2); StrCopy (Chars, head); StrCopy (Chars + len1, tail); } FString::FString (const FString &head, const char *tail) { size_t len1 = head.Len(); size_t len2 = strlen (tail); AllocBuffer (len1 + len2); StrCopy (Chars, head); StrCopy (Chars + len1, tail, len2); } FString::FString (const FString &head, char tail) { size_t len1 = head.Len(); AllocBuffer (len1 + 1); StrCopy (Chars, head); Chars[len1] = tail; Chars[len1+1] = '\0'; } FString::FString (const char *head, const FString &tail) { size_t len1 = strlen (head); size_t len2 = tail.Len(); AllocBuffer (len1 + len2); StrCopy (Chars, head, len1); StrCopy (Chars + len1, tail); } FString::FString (const char *head, const char *tail) { size_t len1 = strlen (head); size_t len2 = strlen (tail); AllocBuffer (len1 + len2); StrCopy (Chars, head, len1); StrCopy (Chars + len1, tail, len2); } FString::FString (char head, const FString &tail) { size_t len2 = tail.Len(); AllocBuffer (1 + len2); Chars[0] = head; StrCopy (Chars + 1, tail); } FString::~FString () { Data()->Release(); } char *FString::LockBuffer() { if (Data()->RefCount == 1) { // We're the only user, so we can lock it straight away Data()->RefCount = -1; } else if (Data()->RefCount < -1) { // Already locked; just add to the lock count Data()->RefCount--; } else { // Somebody else is also using this character buffer, so create a copy FStringData *old = Data(); AllocBuffer (old->Len); StrCopy (Chars, old->Chars(), old->Len); old->Release(); Data()->RefCount = -1; } return Chars; } void FString::UnlockBuffer() { assert (Data()->RefCount < 0); if (++Data()->RefCount == 0) { Data()->RefCount = 1; } } FString &FString::operator = (const FString &other) { assert (Chars != NULL); if (&other != this) { int oldrefcount = Data()->RefCount < 0; Data()->Release(); AttachToOther(other); if (oldrefcount < 0) { LockBuffer(); Data()->RefCount = oldrefcount; } } return *this; } FString &FString::operator = (const char *copyStr) { Data()->Release(); if (copyStr == NULL || *copyStr == '\0') { NullString.RefCount++; Chars = &NullString.Nothing[0]; } else { size_t len = strlen (copyStr); AllocBuffer (len); StrCopy (Chars, copyStr, len); } return *this; } void FString::Format (const char *fmt, ...) { va_list arglist; va_start (arglist, fmt); VFormat (fmt, arglist); va_end (arglist); } void FString::AppendFormat (const char *fmt, ...) { va_list arglist; va_start (arglist, fmt); StringFormat::VWorker (FormatHelper, this, fmt, arglist); va_end (arglist); } void FString::VFormat (const char *fmt, va_list arglist) { Data()->Release(); Chars = (char *)(FStringData::Alloc(128) + 1); StringFormat::VWorker (FormatHelper, this, fmt, arglist); } void FString::VAppendFormat (const char *fmt, va_list arglist) { StringFormat::VWorker (FormatHelper, this, fmt, arglist); } int FString::FormatHelper (void *data, const char *cstr, int len) { FString *str = (FString *)data; size_t len1 = str->Len(); if (len1 + len > str->Data()->AllocLen || str->Chars == &NullString.Nothing[0]) { str->ReallocBuffer((len1 + len + 127) & ~127); } StrCopy (str->Chars + len1, cstr, len); str->Data()->Len = (unsigned int)(len1 + len); return len; } FString FString::operator + (const FString &tail) const { return FString (*this, tail); } FString FString::operator + (const char *tail) const { return FString (*this, tail); } FString operator + (const char *head, const FString &tail) { return FString (head, tail); } FString FString::operator + (char tail) const { return FString (*this, tail); } FString operator + (char head, const FString &tail) { return FString (head, tail); } FString &FString::operator += (const FString &tail) { size_t len1 = Len(); size_t len2 = tail.Len(); ReallocBuffer (len1 + len2); StrCopy (Chars + len1, tail); return *this; } FString &FString::operator += (const char *tail) { size_t len1 = Len(); size_t len2 = strlen(tail); ReallocBuffer (len1 + len2); StrCopy (Chars + len1, tail, len2); return *this; } FString &FString::operator += (char tail) { size_t len1 = Len(); ReallocBuffer (len1 + 1); Chars[len1] = tail; Chars[len1+1] = '\0'; return *this; } FString &FString::AppendCStrPart (const char *tail, size_t tailLen) { size_t len1 = Len(); ReallocBuffer (len1 + tailLen); StrCopy (Chars + len1, tail, tailLen); return *this; } void FString::Truncate (long newlen) { if (newlen >= 0 && newlen < (long)Len()) { ReallocBuffer (newlen); Chars[newlen] = '\0'; } } FString FString::Left (size_t numChars) const { size_t len = Len(); if (len < numChars) { numChars = len; } return FString (Chars, numChars); } FString FString::Right (size_t numChars) const { size_t len = Len(); if (len < numChars) { numChars = len; } return FString (Chars + len - numChars, numChars); } FString FString::Mid (size_t pos, size_t numChars) const { size_t len = Len(); if (pos >= len) { return FString(); } if (pos + numChars > len) { numChars = len - pos; } return FString (Chars + pos, numChars); } long FString::IndexOf (const FString &substr, long startIndex) const { return IndexOf (substr.Chars, startIndex); } long FString::IndexOf (const char *substr, long startIndex) const { if (startIndex > 0 && Len() <= (size_t)startIndex) { return -1; } char *str = strstr (Chars + startIndex, substr); if (str == NULL) { return -1; } return long(str - Chars); } long FString::IndexOf (char subchar, long startIndex) const { if (startIndex > 0 && Len() <= (size_t)startIndex) { return -1; } char *str = strchr (Chars + startIndex, subchar); if (str == NULL) { return -1; } return long(str - Chars); } long FString::IndexOfAny (const FString &charset, long startIndex) const { return IndexOfAny (charset.Chars, startIndex); } long FString::IndexOfAny (const char *charset, long startIndex) const { if (startIndex > 0 && Len() <= (size_t)startIndex) { return -1; } char *brk = strpbrk (Chars + startIndex, charset); if (brk == NULL) { return -1; } return long(brk - Chars); } long FString::LastIndexOf (const FString &substr) const { return LastIndexOf (substr.Chars, long(Len()), substr.Len()); } long FString::LastIndexOf (const char *substr) const { return LastIndexOf (substr, long(Len()), strlen(substr)); } long FString::LastIndexOf (char subchar) const { return LastIndexOf (subchar, long(Len())); } long FString::LastIndexOf (const FString &substr, long endIndex) const { return LastIndexOf (substr.Chars, endIndex, substr.Len()); } long FString::LastIndexOf (const char *substr, long endIndex) const { return LastIndexOf (substr, endIndex, strlen(substr)); } long FString::LastIndexOf (char subchar, long endIndex) const { if ((size_t)endIndex > Len()) { endIndex = long(Len()); } while (--endIndex >= 0) { if (Chars[endIndex] == subchar) { return endIndex; } } return -1; } long FString::LastIndexOf (const char *substr, long endIndex, size_t substrlen) const { if ((size_t)endIndex > Len()) { endIndex = long(Len()); } substrlen--; while (--endIndex >= long(substrlen)) { if (strncmp (substr, Chars + endIndex - substrlen, substrlen + 1) == 0) { return endIndex; } } return -1; } long FString::LastIndexOfAny (const FString &charset) const { return LastIndexOfAny (charset.Chars, long(Len())); } long FString::LastIndexOfAny (const char *charset) const { return LastIndexOfAny (charset, long(Len())); } long FString::LastIndexOfAny (const FString &charset, long endIndex) const { return LastIndexOfAny (charset.Chars, endIndex); } long FString::LastIndexOfAny (const char *charset, long endIndex) const { if ((size_t)endIndex > Len()) { endIndex = long(Len()); } while (--endIndex >= 0) { if (strchr (charset, Chars[endIndex]) != NULL) { return endIndex; } } return -1; } void FString::ToUpper () { LockBuffer(); size_t max = Len(); for (size_t i = 0; i < max; ++i) { Chars[i] = toupper(Chars[i]); } UnlockBuffer(); } void FString::ToLower () { LockBuffer(); size_t max = Len(); for (size_t i = 0; i < max; ++i) { Chars[i] = tolower(Chars[i]); } UnlockBuffer(); } void FString::SwapCase () { LockBuffer(); size_t max = Len(); for (size_t i = 0; i < max; ++i) { if (isupper(Chars[i])) { Chars[i] = tolower(Chars[i]); } else { Chars[i] = toupper(Chars[i]); } } UnlockBuffer(); } void FString::StripLeft () { size_t max = Len(), i, j; for (i = 0; i < max; ++i) { if (!isspace(Chars[i])) break; } if (Data()->RefCount <= 1) { for (j = 0; i <= max; ++j, ++i) { Chars[j] = Chars[i]; } ReallocBuffer (j-1); } else { FStringData *old = Data(); AllocBuffer (max - i); StrCopy (Chars, old->Chars() + i, max - i); old->Release(); } } void FString::StripLeft (const FString &charset) { return StripLeft (charset.Chars); } void FString::StripLeft (const char *charset) { size_t max = Len(), i, j; for (i = 0; i < max; ++i) { if (!strchr (charset, Chars[i])) break; } if (Data()->RefCount <= 1) { for (j = 0; i <= max; ++j, ++i) { Chars[j] = Chars[i]; } ReallocBuffer (j-1); } else { FStringData *old = Data(); AllocBuffer (max - i); StrCopy (Chars, old->Chars() + i, max - i); old->Release(); } } void FString::StripRight () { size_t max = Len(), i; for (i = max - 1; i-- > 0; ) { if (!isspace(Chars[i])) break; } if (Data()->RefCount <= 1) { Chars[i+1] = '\0'; ReallocBuffer (i+1); } else { FStringData *old = Data(); AllocBuffer (i+1); StrCopy (Chars, old->Chars(), i+1); old->Release(); } } void FString::StripRight (const FString &charset) { return StripRight (charset.Chars); } void FString::StripRight (const char *charset) { size_t max = Len(), i; for (i = max - 1; i-- > 0; ) { if (!strchr (charset, Chars[i])) break; } if (Data()->RefCount <= 1) { Chars[i+1] = '\0'; ReallocBuffer (i+1); } else { FStringData *old = Data(); AllocBuffer (i+1); StrCopy (Chars, old->Chars(), i+1); old->Release(); } } void FString::StripLeftRight () { size_t max = Len(), i, j, k; for (i = 0; i < max; ++i) { if (!isspace(Chars[i])) break; } for (j = max - 1; j >= i; --j) { if (!isspace(Chars[j])) break; } if (Data()->RefCount <= 1) { for (k = 0; i <= j; ++i, ++k) { Chars[k] = Chars[i]; } Chars[k] = '\0'; ReallocBuffer (k); } else { FStringData *old = Data(); AllocBuffer (j - i); StrCopy (Chars, old->Chars(), j - i); old->Release(); } } void FString::StripLeftRight (const FString &charset) { return StripLeftRight (charset.Chars); } void FString::StripLeftRight (const char *charset) { size_t max = Len(), i, j, k; for (i = 0; i < max; ++i) { if (!strchr (charset, Chars[i])) break; } for (j = max - 1; j >= i; --j) { if (!strchr (charset, Chars[j])) break; } if (Data()->RefCount <= 1) { for (k = 0; i <= j; ++i, ++k) { Chars[k] = Chars[i]; } Chars[k] = '\0'; ReallocBuffer (k); } else { FStringData *old = Data(); AllocBuffer (j - i); StrCopy (Chars, old->Chars(), j - i); old->Release(); } } void FString::Insert (size_t index, const FString &instr) { Insert (index, instr.Chars, instr.Len()); } void FString::Insert (size_t index, const char *instr) { Insert (index, instr, strlen(instr)); } void FString::Insert (size_t index, const char *instr, size_t instrlen) { size_t mylen = Len(); if (index > mylen) { index = mylen; } if (Data()->RefCount <= 1) { ReallocBuffer (mylen + instrlen); memmove (Chars + index + instrlen, Chars + index, (mylen - index + 1)*sizeof(char)); memcpy (Chars + index, instr, instrlen*sizeof(char)); } else { FStringData *old = Data(); AllocBuffer (mylen + instrlen); StrCopy (Chars, old->Chars(), index); StrCopy (Chars + index, instr, instrlen); StrCopy (Chars + index + instrlen, Chars + index, mylen - index + 1); old->Release(); } } void FString::ReplaceChars (char oldchar, char newchar) { size_t i, j; LockBuffer(); for (i = 0, j = Len(); i < j; ++i) { if (Chars[i] == oldchar) { Chars[i] = newchar; } } UnlockBuffer(); } void FString::ReplaceChars (const char *oldcharset, char newchar) { size_t i, j; LockBuffer(); for (i = 0, j = Len(); i < j; ++i) { if (strchr (oldcharset, Chars[i]) != NULL) { Chars[i] = newchar; } } UnlockBuffer(); } void FString::StripChars (char killchar) { size_t read, write, mylen; LockBuffer(); for (read = write = 0, mylen = Len(); read < mylen; ++read) { if (Chars[read] != killchar) { Chars[write++] = Chars[read]; } } Chars[write] = '\0'; ReallocBuffer (write); UnlockBuffer(); } void FString::StripChars (const char *killchars) { size_t read, write, mylen; LockBuffer(); for (read = write = 0, mylen = Len(); read < mylen; ++read) { if (strchr (killchars, Chars[read]) == NULL) { Chars[write++] = Chars[read]; } } Chars[write] = '\0'; ReallocBuffer (write); UnlockBuffer(); } void FString::MergeChars (char merger) { MergeChars (merger, merger); } void FString::MergeChars (char merger, char newchar) { size_t read, write, mylen; LockBuffer(); for (read = write = 0, mylen = Len(); read < mylen; ) { if (Chars[read] == merger) { while (Chars[++read] == merger) { } Chars[write++] = newchar; } else { Chars[write++] = Chars[read++]; } } Chars[write] = '\0'; ReallocBuffer (write); UnlockBuffer(); } void FString::MergeChars (const char *charset, char newchar) { size_t read, write, mylen; LockBuffer(); for (read = write = 0, mylen = Len(); read < mylen; ) { if (strchr (charset, Chars[read]) != NULL) { while (strchr (charset, Chars[++read]) != NULL) { } Chars[write++] = newchar; } else { Chars[write++] = Chars[read++]; } } Chars[write] = '\0'; ReallocBuffer (write); UnlockBuffer(); } void FString::Substitute (const FString &oldstr, const FString &newstr) { return Substitute (oldstr.Chars, newstr.Chars, oldstr.Len(), newstr.Len()); } void FString::Substitute (const char *oldstr, const FString &newstr) { return Substitute (oldstr, newstr.Chars, strlen(oldstr), newstr.Len()); } void FString::Substitute (const FString &oldstr, const char *newstr) { return Substitute (oldstr.Chars, newstr, oldstr.Len(), strlen(newstr)); } void FString::Substitute (const char *oldstr, const char *newstr) { return Substitute (oldstr, newstr, strlen(oldstr), strlen(newstr)); } void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrlen, size_t newstrlen) { LockBuffer(); for (size_t checkpt = 0; checkpt < Len(); ) { char *match = strstr (Chars + checkpt, oldstr); size_t len = Len(); if (match != NULL) { size_t matchpt = match - Chars; if (oldstrlen != newstrlen) { ReallocBuffer (len + newstrlen - oldstrlen); memmove (Chars + matchpt + newstrlen, Chars + matchpt + oldstrlen, (len + 1 - matchpt - oldstrlen)*sizeof(char)); } memcpy (Chars + matchpt, newstr, newstrlen); checkpt = matchpt + newstrlen; } else { break; } } UnlockBuffer(); } bool FString::IsInt () const { // String must match: [whitespace] [{+ | –}] [0 [{ x | X }]] [digits] [whitespace] /* This state machine is based on a simplification of re2c's output for this input: digits = [0-9]; hexdigits = [0-9a-fA-F]; octdigits = [0-7]; ("0" octdigits+ | "0" [xX] hexdigits+ | (digits \ '0') digits*) { return true; } [\000-\377] { return false; }*/ const char *YYCURSOR = Chars; char yych; yych = *YYCURSOR; // Skip preceding whitespace while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; } // Check for sign if (yych == '+' || yych == '-') { yych = *++YYCURSOR; } if (yych == '0') { yych = *++YYCURSOR; if (yych >= '0' && yych <= '7') { do { yych = *++YYCURSOR; } while (yych >= '0' && yych <= '7'); } else if (yych == 'X' || yych == 'x') { bool gothex = false; yych = *++YYCURSOR; while ((yych >= '0' && yych <= '9') || (yych >= 'A' && yych <= 'F') || (yych >= 'a' && yych <= 'f')) { gothex = true; yych = *++YYCURSOR; } if (!gothex) return false; } else { return false; } } else if (yych >= '1' && yych <= '9') { do { yych = *++YYCURSOR; } while (yych >= '0' && yych <= '9'); } else { return false; } // The rest should all be whitespace while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; } return yych == '\0'; } bool FString::IsFloat () const { // String must match: [whitespace] [sign] [digits] [.digits] [ {d | D | e | E}[sign]digits] [whitespace] /* This state machine is based on a simplification of re2c's output for this input: digits = [0-9]; (digits+ | digits* "." digits+) ([dDeE] [+-]? digits+)? { return true; } [\000-\377] { return false; } */ const char *YYCURSOR = Chars; char yych; bool gotdig = false; yych = *YYCURSOR; // Skip preceding whitespace while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; } // Check for sign if (yych == '+' || yych == '-') { yych = *++YYCURSOR; } while (yych >= '0' && yych <= '9') { gotdig = true; yych = *++YYCURSOR; } if (yych == '.') { yych = *++YYCURSOR; if (yych >= '0' && yych <= '9') { gotdig = true; do { yych = *++YYCURSOR; } while (yych >= '0' && yych <= '9'); } else return false; } if (gotdig) { if (yych == 'D' || yych == 'd' || yych == 'E' || yych == 'e') { yych = *++YYCURSOR; if (yych == '+' || yych == '-') yych = *++YYCURSOR; while (yych >= '0' && yych <= '9') { yych = *++YYCURSOR; } } } // The rest should all be whitespace while (yych != '\0' && isspace(yych)) { yych = *++YYCURSOR; } return yych == '\0'; } long FString::ToLong (int base) const { return strtol (Chars, NULL, base); } unsigned long FString::ToULong (int base) const { return strtoul (Chars, NULL, base); } double FString::ToDouble () const { return strtod (Chars, NULL); } void FString::StrCopy (char *to, const char *from, size_t len) { memcpy (to, from, len*sizeof(char)); to[len] = 0; } void FString::StrCopy (char *to, const FString &from) { StrCopy (to, from.Chars, from.Len()); } void FString::AllocBuffer (size_t len) { Chars = (char *)(FStringData::Alloc(len) + 1); Data()->Len = (unsigned int)len; } void FString::ReallocBuffer (size_t newlen) { if (Data()->RefCount > 1) { // If more than one reference, we must use a new copy FStringData *old = Data(); AllocBuffer (newlen); StrCopy (Chars, old->Chars(), old->Len); old->Release(); } else { if (newlen > Data()->AllocLen) { Chars = (char *)(Data()->Realloc(newlen) + 1); } Data()->Len = (unsigned int)newlen; } } // Under Windows, use the system heap functions for managing string memory. // Under other OSs, use ordinary memory management instead. #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include static HANDLE StringHeap; #endif FStringData *FStringData::Alloc (size_t strlen) { strlen += 1 + sizeof(FStringData); // Add space for header and terminating null strlen = (strlen + 7) & ~7; // Pad length up #ifdef _WIN32 if (StringHeap == NULL) { StringHeap = HeapCreate (0, 64*1024, 0); if (StringHeap == NULL) { throw std::bad_alloc(); } } FStringData *block = (FStringData *)HeapAlloc (StringHeap, 0, strlen); #else FStringData *block = (FStringData *)malloc (strlen); #endif if (block == NULL) { throw std::bad_alloc(); } block->Len = 0; block->AllocLen = (unsigned int)strlen - sizeof(FStringData) - 1; block->RefCount = 1; return block; } FStringData *FStringData::Realloc (size_t newstrlen) { assert (RefCount <= 1); newstrlen += 1 + sizeof(FStringData); // Add space for header and terminating null newstrlen = (newstrlen + 7) & ~7; // Pad length up #ifdef _WIN32 FStringData *block = (FStringData *)HeapReAlloc (StringHeap, 0, this, newstrlen); #else FStringData *block = (FStringData *)realloc (this, newstrlen); #endif if (block == NULL) { throw std::bad_alloc(); } block->AllocLen = (unsigned int)newstrlen - sizeof(FStringData) - 1; return block; } void FStringData::Dealloc () { assert (RefCount <= 0); #ifdef _WIN32 HeapFree (StringHeap, 0, this); #else free (this); #endif } FStringData *FStringData::MakeCopy () { FStringData *copy = Alloc (Len); copy->Len = Len; FString::StrCopy (copy->Chars(), Chars(), Len); return copy; }