/* Starshatter OpenSource Distribution Copyright (c) 1997-2004, Destroyer Studios LLC. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name "Destroyer Studios" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SUBSYSTEM: FoundationEx FILE: text.cpp AUTHOR: John DiCamillo OVERVIEW ======== Implementation of the Text class */ #include "MemDebug.h" #include "Text.h" #include "stdio.h" #include // +-------------------------------------------------------------------+ // SPECIAL TEXT REP FOR NULL STRINGS // This is used only by the default constructor for the Text object, // to prevent extra rep allocation when constructing empty strings. TextRep TextRep::nullrep; TextRep::TextRep() : ref(1234567), data(0), length(0), hash(0), sensitive(true) { #ifdef MEM_DEBUG data = new(__FILE__,__LINE__) char[4]; #else data = new char[4]; #endif if (data) ZeroMemory(data, 4); } // +-------------------------------------------------------------------+ ThreadSync TextRep::sync; TextRep::TextRep(const char* s) : ref(1), length(0), sensitive(true) { if (s) length = ::strlen(s); #ifdef MEM_DEBUG data = new(__FILE__,__LINE__) char[length+1]; #else data = new char[length+1]; #endif if (data) { if (s) ::strcpy(data, s); else data[length] = '\0'; dohash(); } } TextRep::TextRep(const char* s, int len) : ref(1), length(len), sensitive(true) { if (length < 0) length = 0; #ifdef MEM_DEBUG data = new(__FILE__,__LINE__) char[length+1]; #else data = new char[length+1]; #endif if (data) { ::CopyMemory(data, s, length); data[length] = '\0'; dohash(); } } TextRep::TextRep(char c, int len) : ref(1), length(len), sensitive(true) { if (length < 0) length = 0; #ifdef MEM_DEBUG data = new(__FILE__,__LINE__) char[length+1]; #else data = new char[length+1]; #endif if (data) { ::FillMemory(data, length, c); data[length] = '\0'; dohash(); } } TextRep::TextRep(const TextRep* rep) : ref(1) { length = rep->length; #ifdef MEM_DEBUG data = new(__FILE__,__LINE__) char[length+1]; #else data = new char[length+1]; #endif hash = rep->hash; sensitive = rep->sensitive; if (data) ::strcpy(data, rep->data); } TextRep::~TextRep() { delete[] data; } void TextRep::addref() { sync.acquire(); ref++; sync.release(); } long TextRep::deref() { sync.acquire(); long r = --ref; sync.release(); return r; } inline static void mash(unsigned& hash, unsigned chars) { hash = (chars ^ ((hash << 5) | (hash >> (8*sizeof(unsigned) - 5)))); } void TextRep::dohash() { unsigned hv = (unsigned)length; // Mix in the string length. unsigned i = length*sizeof(char)/sizeof(unsigned); const unsigned* p = (const unsigned*)data; while (i--) mash(hv, *p++); // XOR in the characters. // XOR in any remaining characters: i = length*sizeof(char)%sizeof(unsigned); if (i) { unsigned h = 0; const char* c = (const char*)p; while (i--) h = ((h << 8*sizeof(char)) | *c++); mash(hv, h); } hash = hv; } // +-------------------------------------------------------------------+ Text::Text() { rep = &TextRep::nullrep; rep->addref(); sym = rep->data; } Text::Text(char c) { char buf[2]; buf[0] = c; buf[1] = '\0'; #ifdef MEM_DEBUG rep = new(__FILE__,__LINE__) TextRep(buf); #else rep = new TextRep(buf); #endif if (!rep) { rep = &TextRep::nullrep; rep->addref(); } sym = rep->data; } Text::Text(const char* s) { #ifdef MEM_DEBUG rep = new(__FILE__,__LINE__) TextRep(s); #else rep = new TextRep(s); #endif if (!rep) { rep = &TextRep::nullrep; rep->addref(); } sym = rep->data; } Text::Text(const char* s, int len) { #ifdef MEM_DEBUG rep = new(__FILE__,__LINE__) TextRep(s, len); #else rep = new TextRep(s, len); #endif if (!rep) { rep = &TextRep::nullrep; rep->addref(); } sym = rep->data; } Text::Text(char c, int len) { #ifdef MEM_DEBUG rep = new(__FILE__,__LINE__) TextRep(c, len); #else rep = new TextRep(c, len); #endif if (!rep) { rep = &TextRep::nullrep; rep->addref(); } sym = rep->data; } Text::Text(const Text& s) { rep = s.rep; rep->addref(); sym = rep->data; } Text::~Text() { if (rep->deref() == 0) delete rep; rep = &TextRep::nullrep; sym = rep->data; } Text& Text::operator=(const char* s) { if (rep->deref() == 0) delete rep; #ifdef MEM_DEBUG rep = new(__FILE__,__LINE__) TextRep(s); #else rep = new TextRep(s); #endif if (!rep) rep = &TextRep::nullrep; sym = rep->data; return *this; } Text& Text::operator=(const Text& s) { s.rep->addref(); if (rep->deref() == 0) delete rep; rep = s.rep; sym = rep->data; return *this; } Text Text::operator+(char c) { #ifdef MEM_DEBUG char* buf = new(__FILE__,__LINE__) char[rep->length + 2]; #else char* buf = new char[rep->length + 2]; #endif if (buf) { ::strcpy(buf, sym); buf[rep->length] = c; buf[rep->length+1] = '\0'; Text retval(buf); delete [] buf; return retval; } else { return *this; } } Text Text::operator+(const char* s) { #ifdef MEM_DEBUG char* buf = new(__FILE__,__LINE__) char[::strlen(s) + rep->length + 1]; #else char* buf = new char[::strlen(s) + rep->length + 1]; #endif if (buf) { ::strcpy(buf, sym); ::strcat(buf, s); Text retval(buf); delete [] buf; return retval; } else { return *this; } } Text Text::operator+(const Text& s) { #ifdef MEM_DEBUG char* buf = new(__FILE__,__LINE__) char[s.rep->length + rep->length + 1]; #else char* buf = new char[s.rep->length + rep->length + 1]; #endif if (buf) { ::strcpy(buf, sym); ::strcat(buf, s.sym); Text retval(buf); delete [] buf; return retval; } else { return *this; } } bool Text::isSensitive() const { return rep->sensitive; } void Text::setSensitive(bool s) { rep->sensitive = s; } Text& Text::append(char c) { #ifdef MEM_DEBUG char* buf = new(__FILE__,__LINE__) char[rep->length + 2]; #else char* buf = new char[rep->length + 2]; #endif if (buf) { ::strcpy(buf, sym); buf[rep->length] = c; buf[rep->length+1] = '\0'; if (rep->deref() == 0) delete rep; #ifdef MEM_DEBUG rep = new(__FILE__,__LINE__) TextRep(buf); #else rep = new TextRep(buf); #endif if (!rep) rep = &TextRep::nullrep; sym = rep->data; delete [] buf; } return *this; } Text& Text::append(const char* s) { #ifdef MEM_DEBUG char* buf = new(__FILE__,__LINE__) char[::strlen(s) + rep->length + 1]; #else char* buf = new char[::strlen(s) + rep->length + 1]; #endif if (buf) { ::strcpy(buf, sym); ::strcat(buf, s); if (rep->deref() == 0) delete rep; #ifdef MEM_DEBUG rep = new(__FILE__,__LINE__) TextRep(buf); #else rep = new TextRep(buf); #endif if (!rep) rep = &TextRep::nullrep; sym = rep->data; delete [] buf; } return *this; } Text& Text::append(const Text& s) { #ifdef MEM_DEBUG char* buf = new(__FILE__,__LINE__) char[s.rep->length + rep->length + 1]; #else char* buf = new char[s.rep->length + rep->length + 1]; #endif if (buf) { int lenA = rep->length; int lenB = s.rep->length; CopyMemory(buf, sym, lenA); CopyMemory(buf + lenA, s.sym, lenB); buf[lenA + lenB] = 0; if (rep->deref() == 0) delete rep; #ifdef MEM_DEBUG rep = new(__FILE__,__LINE__) TextRep(buf, lenA + lenB); #else rep = new TextRep(buf); #endif if (!rep) rep = &TextRep::nullrep; sym = rep->data; delete [] buf; } return *this; } void Text::clone() { if (rep->ref > 1) { rep->deref(); #ifdef MEM_DEBUG TextRep* t = new(__FILE__,__LINE__) TextRep(rep); #else TextRep* t = new TextRep(rep); #endif rep = t; if (!rep) rep = &TextRep::nullrep; sym = rep->data; } } char Text::operator[](int index) const { if (index < (int) rep->length) return sym[index]; else throw "BOUNDS ERROR"; return '\0'; } char Text::operator()(int index) const { return sym[index]; } char& Text::operator[](int index) { if (index < (int) rep->length) { clone(); return (char&) sym[index]; } else throw "BOUNDS ERROR"; return (char&) sym[0]; } char& Text::operator()(int index) { clone(); return (char&) sym[index]; } Text Text::operator()(int start, int len) const { if (start > rep->length || len <= 0) return Text(); if (start + len > rep->length) len = rep->length - start; #ifdef MEM_DEBUG char* buf = new(__FILE__,__LINE__) char[len+1]; #else char* buf = new char[len+1]; #endif if (buf) { ::strncpy(buf, sym+start, len); buf[len] = '\0'; Text retval(buf); delete [] buf; return retval; } return Text(); } bool Text::contains(char c) const { if (rep->length > 0) { if (!rep->sensitive) { char alt = c; if (islower(alt)) alt = toupper(alt); else if (isupper(alt)) alt = tolower(alt); if (strchr(rep->data, alt) != 0) return true; } if (strchr(rep->data, c) != 0) return true; } return false; } bool Text::contains(const char* pattern) const { if (rep->length > 0 && pattern && *pattern) { if (rep->sensitive) { if (strstr(rep->data, pattern) != 0) return true; } else { Text smash1(*this); smash1.toLower(); Text smash2(pattern); smash2.toLower(); if (strstr(smash1.data(), smash2.data()) != 0) return true; } } return false; } bool Text::containsAnyOf(const char* charSet) const { if (rep->length > 0 && charSet && *charSet) { if (rep->sensitive) { if (strpbrk(rep->data, charSet) != 0) return true; } else { Text smash1(*this); smash1.toLower(); Text smash2(charSet); smash2.toLower(); if (strpbrk(smash1.data(), smash2.data()) != 0) return true; } } return false; } int Text::indexOf(char c) const { if (rep->length > 0) { if (!rep->sensitive) { char alt = c; if (islower(alt)) alt = toupper(alt); else if (isupper(alt)) alt = tolower(alt); const char* p = strchr(rep->data, alt); if (p) return (p - rep->data); } const char* p = strchr(rep->data, c); if (p) return (p - rep->data); } return -1; } int Text::indexOf(const char* pattern) const { if (rep->length > 0 && pattern && *pattern) { if (rep->sensitive) { const char* p = strstr(rep->data, pattern); if (p) return (p - rep->data); } else { Text smash1(*this); smash1.toLower(); Text smash2(pattern); smash2.toLower(); const char* p = strstr(smash1.data(), smash2.data()); if (p) return (p - smash1.data()); } } return -1; } void Text::toLower() { clone(); size_t n = rep->length; char* p = (char*) sym; while (n--) { *p = tolower((unsigned char)*p); p++; } rep->dohash(); } void Text::toUpper() { clone(); size_t n = rep->length; char* p = (char*) sym; while ( n-- ) { *p = toupper((unsigned char)*p); p++; } rep->dohash(); } Text Text::substring(int start, int length) { Text result; if (start >= 0 && start < (int) rep->length && length > 0) { if (start + length > (int) rep->length) length = (int) rep->length - start; const char* s = sym + start; #ifdef MEM_DEBUG result.rep = new(__FILE__,__LINE__) TextRep(s, length); #else result.rep = new TextRep(s, length); #endif if (!result.rep) result.rep = &TextRep::nullrep; result.sym = result.rep->data; } return result; } Text Text::trim() { Text result; if (rep->length) { const char* p = sym; const char* q = sym + rep->length-1; while (p && *p && isspace(*p)) p++; while (q>p && *q && isspace(*q)) q--; result = substring(p-sym, q-p+1); } return result; } Text Text::replace(const char* pattern, const char* substitution) { Text result; if (rep->length && pattern && *pattern) { int index = 0; int skip = strlen(pattern); do { const char* p = strstr(rep->data + index, pattern); if (p) { int len = (p - rep->data + index); result.append(substring(index, len)); result.append(substitution); index += len + skip; } else if (index < rep->length) { result.append(substring(index, rep->length-index)); index = -1; } } while (index >= 0 && index < rep->length); } return result; }