aboutsummaryrefslogtreecommitdiff
path: root/Src/Wasabi/bfc/string/string.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Wasabi/bfc/string/string.cpp')
-rw-r--r--Src/Wasabi/bfc/string/string.cpp884
1 files changed, 884 insertions, 0 deletions
diff --git a/Src/Wasabi/bfc/string/string.cpp b/Src/Wasabi/bfc/string/string.cpp
new file mode 100644
index 00000000..49b7aade
--- /dev/null
+++ b/Src/Wasabi/bfc/string/string.cpp
@@ -0,0 +1,884 @@
+#include <bfc/wasabi_std.h>
+#include "string.h"
+#include <bfc/nsguid.h>
+
+String::String(const char *initial_val)
+ : val(NULL)
+{
+ setValue(initial_val);
+}
+
+String::String(const String &s)
+ : val(NULL)
+{
+ if (s == NULL) setValue(NULL);
+ else setValue(s.getValue());
+}
+
+String::String(const String *s)
+ : val(NULL)
+{
+ if (s == NULL) setValue(NULL);
+ else setValue(s->getValue());
+}
+
+String::~String()
+{
+ FREE(val);
+}
+
+const char *String::getValueSafe(const char *def_val) const
+{
+ if (val == NULL)
+ return def_val;
+ else
+ return val;
+}
+
+int String::getChar(int pos, int bounds_check)
+{
+ if (pos < 0 || val == NULL) return -1;
+ if (bounds_check && pos >= len()) return -1;
+ return val[pos];
+}
+
+int String::setChar(int pos, int value)
+{
+ if (pos < 0 || val == NULL) return -1;
+ return val[pos] = value;
+}
+
+const char *String::setValue(const char *newval)
+{
+ if (newval != val)
+ {
+ if (newval == NULL)
+ {
+ FREE(val);
+ val = NULL;
+ }
+ else
+ {
+ int len = STRLEN(newval);
+ if (val != NULL)
+#ifdef STRING_REALLOC_OPTIMS
+ {
+ int oldlen = STRLEN(val);
+ // if smaller but greater than half previous size, don't realloc
+ if (len > oldlen || len < oldlen / 2)
+ val = (char *)REALLOC(val, len + 1);
+ }
+#else
+ val = (char *)REALLOC(val, len + 1);
+#endif
+ else
+ val = (char *)MALLOC(len + 1);
+ ASSERT(newval != NULL);
+ MEMCPY_(val, newval, len + 1);
+ }
+ }
+ return getValue();
+}
+
+int String::len() const
+{
+ return (val == NULL) ? 0 : STRLEN(val);
+}
+
+int String::isempty() const
+{
+ return (!val || !*val);
+}
+
+void String::toupper()
+{
+ if (!isempty())
+ STRTOUPPER(val);
+}
+
+void String::tolower()
+{
+ if (!isempty())
+ STRTOLOWER(val);
+}
+
+int String::isequal(const char *otherval) const
+{
+ return !STRCMPSAFE(getValue(), otherval);
+}
+
+int String::iscaseequal(const char *otherval) const
+{
+ return !STRICMPSAFE(getValue(), otherval);
+}
+
+int String::islessthan(const char *otherval) const
+{
+ return STRCMPSAFE(getValue(), otherval) < 0;
+}
+
+void String::changeChar(int from, int to)
+{
+ if (val == NULL) return ;
+ int length = len();
+ for (int i = 0; i < length; i++)
+ if (val[i] == from) val[i] = to;
+}
+
+void String::truncateOnChar(int which, int fromright)
+{
+ if (fromright)
+ {
+ if (val == NULL) return ;
+ int length = len();
+ for (int i = length - 1; i >= 0; i--)
+ {
+ if (val[i] == which)
+ {
+ val[i] = '\0';
+ return ;
+ }
+ }
+ }
+ else
+ {
+ changeChar(which, '\0');
+ }
+}
+
+int String::lastChar()
+{
+ if (isempty()) return -1;
+ return val[len() - 1];
+}
+
+const char *String::printf(const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+ return getValue();
+}
+
+const char *String::cat(const char *value)
+{
+ if (value == NULL || *value == 0) return getValue();
+ if (val == NULL) return setValue(value);
+ return catn(value, STRLEN(value));
+}
+
+const char *String::catn(const char *value, int len)
+{
+ if (len == 0) return val;
+ if (value == NULL || *value == 0) return getValue();
+ if (val == NULL) return ncpy(value, len);
+ int ol = STRLEN(val);
+ val = (char *)REALLOC(val, ol + len + 1);
+ val[ol + len] = 0;
+ STRNCPY(val + ol, value, len);
+ return val;
+}
+
+const char *String::prepend(const char *value)
+{
+ if (value == NULL || *value == 0) return getValue();
+ if (val == NULL) return setValue(value);
+ StringPrintf temp("%s%s", value, getValue());
+ swap(&temp);
+ return getValue();
+}
+
+// replaces string with n chars of val or length of val, whichever is less.
+const char *String::ncpy(const char *newstr, int numchars)
+{
+ val = (char *)REALLOC(val, numchars + 1);
+ val[numchars] = 0;
+ STRNCPY(val, newstr, numchars);
+ return getValue();
+}
+
+void String::strncpyTo(char *dest, int maxlen)
+{
+ ASSERT(dest != NULL);
+ ASSERT(maxlen >= 0);
+ if (maxlen == 0) return ;
+ else if (maxlen == 1)
+ {
+ *dest = '\0';
+ return ;
+ }
+ const char *src = val;
+ if (src == NULL) src = "";
+ int copylen = MIN(STRLEN(src), maxlen);
+ if (maxlen == copylen) copylen--; // make room for 0-termination
+ MEMCPY(dest, src, copylen);
+ dest[copylen] = 0;
+}
+
+// -----------------------------------------
+// Character based find-n-splice methods --
+// "l" and "r" prefixes specify to begin at
+// front or back of string:
+
+#ifdef FAST_METHODS
+#undef FAST_METHODS
+#endif//FAST_METHODS
+#ifdef SAFE_METHODS
+#undef SAFE_METHODS
+#endif//SAFE_METHODS
+#ifdef USE
+#undef USE
+#endif//USE
+
+#define FAST_METHODS 1
+#define SAFE_METHODS 0
+
+#define USE FAST_METHODS
+
+// Returns index of first found, -1 if not found.
+int String::lFindChar(char findval)
+{
+ int length = len();
+ for (int i = 0; i < length; i++)
+ {
+ if (val[i] == findval)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Same as above, save the "findval" is a string where it searches
+// for any of the characters in the string.
+int String::lFindChar(const char *findval)
+{
+ int length = len();
+ int numchars = STRLEN(findval);
+ for (int i = 0; i < length; i++)
+ {
+ for (int j = 0; j < numchars; j++)
+ {
+ if (val[i] == findval[j])
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+int String::rFindChar(char findval)
+{
+ int length = len();
+ for (int i = length - 1; i > 0; i--)
+ {
+ if (val[i] == findval)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// Same as above, save the "findval" is a string where it searches
+// for any of the characters in the string.
+int String::rFindChar(const char *findval)
+{
+ int length = len();
+ int numchars = STRLEN(findval);
+ for (int i = length - 1; i > 0; i--)
+ {
+ for (int j = 0; j < numchars; j++)
+ {
+ if (val[i] == findval[j])
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+// Splits string at findval. Characters passed by search, including the
+// found character, are MOVED to the returned string. If there is no char
+// to be found, the entire string is returnef and the called instance is
+// left empty. (Makes looped splits very easy).
+String String::lSplit(int idxval)
+{
+ if (val == NULL) return String();
+ if (idxval == -1)
+ { // Not Found
+ // Copy our contents to return on the stack
+ String retval(val);
+ // And zero the string.
+ if (val)
+ {
+ val[0] = 0;
+ }
+ return retval;
+ }
+ else
+ {
+ String retval;
+ // Copy into retval the number of characters to the found char index.
+ retval.ncpy(val, idxval + 1);
+ {
+ String testscope;
+ // Copy into retval the number of characters to the found char index.
+ testscope.ncpy(val, idxval + 1);
+ }
+#if USE == FAST_METHODS
+ size_t len = strlen(val + idxval + 1);
+ MEMCPY(val, val + idxval + 1, len + 1);
+#elif USE == SAFE_METHODS
+ // Copy from the found index downwards to save for this object
+ String temp(val + idxval + 1);
+ // And then copy into ourselves the tempspace.
+ *this = temp;
+#endif
+ return retval;
+ }
+ // this will never be hit. many compilers are too stupid to realize this.
+ return String();
+}
+
+String String::lSplitChar(char findval)
+{
+ if (val == NULL) return String();
+ // The index of the found character
+ int idxval = lFindChar(findval);
+ return lSplit(idxval);
+}
+
+String String::lSplitChar(const char *findval)
+{
+ if (val == NULL) return String();
+ // The index of the found character
+ int idxval = lFindChar(findval);
+ return lSplit(idxval);
+}
+
+String String::rSplit(int idxval)
+{
+ if (val == NULL) return String();
+ if (idxval == -1)
+ { // Not Found
+ // Copy our contents to return on the stack
+ String retval(val);
+ // And zero the string.
+ val[0] = 0;
+ return retval;
+ }
+ else
+ {
+ // Copy from the found index downwards to the retval
+ String retval(val + idxval);
+ // Terminate the found char index
+ val[idxval] = 0;
+ // That was easier, wasn't it?
+ return retval;
+ }
+ // this will never be hit. many compilers are too stupid to realize this.
+ return String();
+}
+
+String String::rSplitChar(char findval)
+{
+ if (val == NULL) return String();
+ // The index of the found character
+ int idxval = rFindChar(findval);
+ return rSplit(idxval);
+}
+
+String String::rSplitChar(const char *findval)
+{
+ if (val == NULL) return String();
+ // The index of the found character
+ int idxval = rFindChar(findval);
+ return rSplit(idxval);
+}
+
+// Same as split, except the find char is cut completely.
+String String::lSpliceChar(char findval)
+{
+ if (val == NULL) return String();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = lSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ String retval = lSplitChar(findval);
+
+ // We need to strip the findval char, which is the end char.
+ int end = retval.len();
+ if (end)
+ {
+ if (retval.val[end - 1] == findval)
+ {
+ retval.val[end - 1] = 0;
+ }
+ }
+ return retval;
+}
+
+// Same as split, except the find char is cut completely.
+String String::lSpliceChar(const char *findval)
+{
+ if (val == NULL) return String();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = lSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ String retval = lSplitChar(findval);
+ // We need to strip the findval char, which is the end char.
+ int end = retval.len();
+ int num = STRLEN(findval);
+ if (end)
+ {
+ for (int i = 0; i < num; i++)
+ {
+ if (retval.val[end - 1] == findval[i])
+ {
+ retval.val[end - 1] = 0;
+ }
+ }
+ }
+ return retval;
+}
+
+String String::rSpliceChar(char findval)
+{
+ if (val == NULL) return String();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = rSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ String retval = rSplitChar(findval);
+ // We need to strip the findval char, which is the first char.
+ // (But we still check for empty string:)
+ size_t end = retval.len();
+ if (end)
+ {
+ if (retval.val[0] == findval)
+ {
+#if USE == FAST_METHODS
+ size_t len = strlen(retval.val + 1);
+ MEMCPY(retval.val, retval.val + 1, len + 1);
+#elif USE == SAFE_METHODS
+ String temp(retval.val + 1);
+ retval = temp;
+#endif
+ return retval;
+ }
+ }
+ return retval;
+}
+
+String String::rSpliceChar(const char *findval)
+{
+ if (val == NULL) return String();
+ //CUT // Auto-scope reference allows us to avoid a copy.
+ //CUT String & retval = rSplitChar(findval);
+ //BU gcc doesn't agree with you and neither do I :/
+ String retval = rSplitChar(findval);
+ // We need to strip the findval char, which is the first char.
+ // (But we still check for empty string:)
+ size_t end = retval.len();
+ size_t num = STRLEN(findval);
+ if (end)
+ {
+ for (size_t i = 0; i < num; i++)
+ {
+ if (retval.val[0] == findval[i])
+ {
+#if USE == FAST_METHODS
+ size_t len = strlen(retval.val + 1);
+ MEMCPY(retval.val, retval.val + 1, len + 1);
+#elif USE == SAFE_METHODS
+ String temp(retval.val + 1);
+ retval = temp;
+#endif
+ return retval;
+ }
+ }
+ }
+ return retval;
+}
+
+int String::replace(const char *find, const char *replace)
+{
+ if (len() == 0 || find == NULL || replace == NULL) return 0;
+
+ int find_count = 0;
+
+ char *p, *p2;
+ int rep_len = STRLEN( replace );
+ int find_len = STRLEN( find );
+ int size_diff = rep_len - find_len;
+
+ if ( size_diff == 0 )
+ {
+ p = val;
+ while ( p = STRSTR( p, find ) )
+ {
+ STRNCPY( p, replace, rep_len );
+ p += find_len;
+ find_count++;
+ }
+ }
+ else
+ {
+ char *new_buf, *in;
+
+ p = val;
+ while ( p = STRSTR( p, find ) )
+ {
+ find_count++;
+ p += find_len;
+ }
+
+ new_buf = (char *)MALLOC( len() + find_count * size_diff + 1 );
+
+ p = val;
+ in = new_buf;
+ while ( p2 = STRSTR( p, find ) )
+ {
+ STRNCPY( in, p, (int)(p2 - p) );
+ in += p2 - p;
+ STRNCPY( in, replace, rep_len );
+ in += rep_len;
+ p = p2 + find_len;
+ }
+ STRCPY( in, p );
+ new_buf[ len() + find_count * size_diff ] = 0;
+
+ // just swap buffers
+
+ FREE(val);
+ val = new_buf;
+ }
+ return find_count;
+}
+
+int String::replaceNumericField(int value, int fieldchar)
+{
+ if (val == NULL || *val == '\0') return 0;
+ int nrep = 0;
+ for (const char *p = val; *p; p++)
+ {
+ if (*p == fieldchar) nrep++;
+ else if (nrep) break;
+ }
+ if (nrep == 0) return 0; // no field found
+ String rc;
+ char fc[2] = { 0, 0 };
+ fc[0] = fieldchar;
+ for (int i = 0; i < nrep; i++) rc.cat(fc);
+ StringPrintf fmt("%%0%0dd", nrep);
+ StringPrintf replacement(fmt.getValue(), value);
+ return replace(rc, replacement);
+}
+
+#undef USE
+#undef SAFE_METHODS
+#undef FAST_METHODS
+
+int String::numCharacters()
+{
+ // count newsize characters over how many bytes?
+ int count, bytes;
+ for (bytes = 0, count = 0; val[bytes]; count++, bytes++)
+ {
+ // If we encounter a lead byte, skip over the trail bytes.
+ switch (val[bytes] & 0xC0)
+ {
+ case 0x80: // trail bytes
+ // THIS SHOULD NEVER EVER EVER EVER HAPPEN!
+ // but we'll fall through anyhow, just in case someone
+ // sends us non-UTF8.
+ case 0xC0: // lead bytes
+ do
+ {
+ bytes++;
+ if (val[bytes] == 0)
+ {
+ // if people are giving us lame encodings, break here.
+ break;
+ }
+ }
+ while ((val[bytes + 1] & 0xC0) == 0x80);
+ break;
+ }
+ }
+ return count;
+}
+
+void String::trunc(int newlen)
+{
+ if (val == NULL) return ;
+ int oldlen = numCharacters();
+ if (newlen < 0) newlen = MAX(oldlen + newlen, 0);
+ if (newlen >= oldlen) return ;
+
+ // count newsize characters over how many bytes?
+ int count, bytes;
+ for (bytes = 0, count = 0; count < newlen; count++, bytes++)
+ {
+ // If we encounter a lead byte, skip over the trail bytes.
+ switch (val[bytes] & 0xC0)
+ {
+ case 0x80: // trail bytes
+ // THIS SHOULD NEVER EVER EVER EVER HAPPEN!
+ // but we'll fall through anyhow, just in case someone
+ // sends us non-UTF8.
+ case 0xC0: // lead bytes
+ do
+ {
+ bytes++;
+ }
+ while ((val[bytes + 1] & 0xC0) == 0x80);
+ break;
+ }
+ }
+
+ val[bytes] = 0;
+}
+
+void String::trim(const char *whitespace, int left, int right)
+{
+ if (val == NULL) return ;
+ if (left)
+ { // trim left
+ for (;;)
+ {
+ int restart = 0;
+ const char *p;
+ for (p = whitespace; *p; p++)
+ {
+ if (*p == val[0])
+ {
+ STRCPY(val, val + 1);
+ restart = 1;
+ break;
+ }
+ }
+ if (!restart) break;
+ }
+ } //left
+ if (right)
+ {
+ char *ptr = val;
+ while (ptr && *ptr) ptr++;
+ ptr--;
+ if (ptr <= val) return ;
+ for (; ptr > val; ptr--)
+ {
+ const char *p;
+ for (p = whitespace; *p; p++)
+ {
+ if (*p == *ptr)
+ {
+ *ptr = '\0';
+ break;
+ }
+ }
+ if (!*p) break;
+ }
+ } //right
+}
+
+int String::va_sprintf(const char *format, va_list args)
+{
+ if (!format) return 0;
+
+ va_list saveargs = args;
+ // roughly evaluate size of dest string
+ const char *p = format;
+ int length = 0;
+ while (p && *p)
+ {
+ if (*(p++) != '%') length++;
+ else
+ {
+ void *arg = va_arg(args, void *);
+ for (;;)
+ {
+ const char f = *p++;
+ if (f == 'c') length++;
+ else if (f == 'i') length += 16;
+ else if (f == 'u') length += 16;
+ else if (f == 'f') length += 64;
+ else if (f == 'd' || f == 'f') length += 64;
+ else if (f == 'x') length += 32; // Hex with LC Alphas: 0x0009a64c
+ else if (f == 'X') length += 32; // Hex with UC Alphas: 0x0009A64C
+ else if (f == 's')
+ { // ::vsrintf can properly handle null.
+ if (arg == NULL)
+ {
+ length += STRLEN("(null)"); // Just to be explicit.
+ }
+ else
+ {
+ length += STRLEN((const char *)arg);
+ }
+ }
+ else if (f == 'S')
+ { // ::vsrintf can properly handle null.
+ if (arg == NULL)
+ {
+ length += (int)wcslen(L"(null)"); // Just to be explicit.
+ }
+ else
+ {
+ length += (int)wcslen((const wchar_t *)arg);
+ }
+ }
+ else if (ISDIGIT(f)) continue;
+ else if (f == '.') continue;
+ else if (f == '%') length++;
+ else ASSERTPR(0, "undefined format passed to stringprintf!");
+ break;
+ }
+ }
+ }
+ if (val)
+ {
+ if (len() < length)
+ val = (char *)REALLOC(val, length + 1);
+ }
+ else val = (char *)MALLOC(length + 1);
+
+ // now write the string in val
+ int real_len = ::vsprintf_s(val, length + 1, format, saveargs);
+ ASSERTPR(real_len <= length, "String.printf overflow");
+ return real_len;
+}
+
+void String::purge()
+{
+ FREE(val);
+ val = NULL;
+}
+
+// swaps buffers with another string
+void String::swap(String *swapper)
+{
+ char *tempChar = swapper->val;
+ swapper->val = val;
+ val = tempChar;
+}
+
+void String::swap(String &swapper) // swaps buffers with another string
+{
+ char *tempChar = swapper.val;
+ swapper.val = val;
+ val = tempChar;
+}
+
+// take ownership of a buffer
+void String::own(char *swapper)
+{
+ if (val)
+ FREE(val);
+ val = swapper;
+}
+
+const char *String::catPostSeparator(const char *value, const char separator)
+{
+ if (value == NULL || *value == 0 || separator == 0) return getValue();
+ int oldLen = val ? STRLEN(val) : 0;
+ int newLen = STRLEN(value);
+ val = (char *)REALLOC(val, oldLen + newLen + 1 + 1); // +1 for separator, +1 for null character
+ STRCPY(val + oldLen, value);
+ val[oldLen + newLen] = separator; // add the separator
+ val[oldLen + newLen + 1] = 0; // null terminate
+ return val;
+}
+
+const char *String::catPreSeparator(const char separator, const char *value)
+{
+ if (value == NULL || *value == 0 || separator == 0) return getValue();
+ int oldLen = val ? STRLEN(val) : 0;
+ int newLen = STRLEN(value);
+ val = (char *)REALLOC(val, oldLen + newLen + 1 + 1); // +1 for separator, +1 for null character
+ val[oldLen] = separator; // add the separator
+ STRCPY(val + oldLen + 1, value); // +1 for separator (goes first)
+ val[oldLen + newLen + 1] = 0; // null terminate
+ return val;
+}
+
+void String::AppendPath(const char *path)
+{
+ catPreSeparator('/', path);
+}
+
+StringPrintf::StringPrintf(const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+}
+
+StringPrintf::StringPrintf(int value)
+{
+ *this += value;
+}
+
+StringPrintf::StringPrintf(double value)
+{
+ // TODO: review to use locale variant...
+ char* locale = _strdup(setlocale(LC_NUMERIC, NULL));
+ setlocale(LC_NUMERIC, "C");
+ *this += StringPrintf("%f", value);
+ if (locale)
+ {
+ setlocale(LC_NUMERIC, locale);
+ free(locale);
+ }
+}
+
+StringPrintf::StringPrintf(GUID g)
+{
+ char splab[nsGUID::GUID_STRLEN + 1] = {0};
+ nsGUID::toChar(g, splab);
+ cat(splab);
+}
+
+_DebugString::_DebugString(const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_sprintf(format, args);
+ va_end(args);
+
+ debugPrint();
+}
+
+_DebugString::_DebugString(const String &s) : String(s)
+{
+ debugPrint();
+}
+
+_DebugString::_DebugString(const String *s) : String(s)
+{
+ debugPrint();
+}
+
+void _DebugString::debugPrint()
+{
+#ifdef _WIN32
+ OutputDebugStringA(getValue());
+ if (lastChar() != '\n') OutputDebugStringA("\n");
+#else
+#warning port me
+#endif
+}
+
+int StringComparator::compareItem(String *p1, String* p2)
+{
+ return STRCMP(p1->getValue(), p2->getValue());
+}
+
+int StringComparator::compareAttrib(const wchar_t *attrib, String *item)
+{
+ return STRCMP((const char *)attrib, item->getValue());
+} \ No newline at end of file