diff options
Diffstat (limited to 'common/str.cpp')
-rw-r--r-- | common/str.cpp | 210 |
1 files changed, 125 insertions, 85 deletions
diff --git a/common/str.cpp b/common/str.cpp index ad48ef6087..5f8d4ffb7e 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -43,32 +43,43 @@ static int computeCapacity(int len) { return ((len + 32 - 1) & ~0x1F) - 1; } -String::String(const char *str, uint32 len) -: _len(0), _str(_storage) { +String::String(const char *str) : _len(0), _str(_storage) { + if (str == 0) { + _storage[0] = 0; + _len = 0; + } else + initWithCStr(str, strlen(str)); +} + +String::String(const char *str, uint32 len) : _len(0), _str(_storage) { + initWithCStr(str, len); +} + +String::String(const char *beginP, const char *endP) : _len(0), _str(_storage) { + assert(endP >= beginP); + initWithCStr(beginP, endP - beginP); +} + +void String::initWithCStr(const char *str, uint32 len) { + assert(str); // Init _storage member explicitly (ie. without calling its constructor) // for GCC 2.95.x compatibility (see also tracker item #1602879). _storage[0] = 0; - if (str && *str) { - const uint32 tmp = strlen(str); - assert(len <= tmp); - if (len <= 0) - len = tmp; - _len = len; - - if (len >= _builtinCapacity) { - // Not enough internal storage, so allocate more - _extern._capacity = computeCapacity(len); - _extern._refCount = 0; - _str = (char *)malloc(_extern._capacity+1); - assert(_str != 0); - } - - // Copy the string into the storage area - memcpy(_str, str, len); - _str[len] = 0; + _len = len; + + if (len >= _builtinCapacity) { + // Not enough internal storage, so allocate more + _extern._capacity = computeCapacity(len); + _extern._refCount = 0; + _str = (char *)malloc(_extern._capacity+1); + assert(_str != 0); } + + // Copy the string into the storage area + memmove(_str, str, len); + _str[len] = 0; } String::String(const String &str) @@ -91,6 +102,8 @@ String::String(char c) _storage[0] = c; _storage[1] = 0; + // TODO/FIXME: There is no reason for the following check -- we *do* + // allow strings to contain 0 bytes! _len = (c == 0) ? 0 : 1; } @@ -98,6 +111,74 @@ String::~String() { decRefCount(_extern._refCount); } +void String::makeUnique() { + ensureCapacity(_len, true); +} + +/** + * Ensure that enough storage is available to store at least new_len + * characters plus a null byte. In addition, if we currently share + * the storage with another string, unshare it, so that we can safely + * write to the storage. + */ +void String::ensureCapacity(uint32 new_len, bool keep_old) { + bool isShared; + uint32 curCapacity, newCapacity; + char *newStorage; + int *oldRefCount = _extern._refCount; + + if (isStorageIntern()) { + isShared = false; + curCapacity = _builtinCapacity - 1; + } else { + isShared = (oldRefCount && *oldRefCount > 1); + curCapacity = _extern._capacity; + } + + // Special case: If there is enough space, and we do not share + // the storage, then there is nothing to do. + if (!isShared && new_len <= curCapacity) + return; + + if (isShared && new_len <= _builtinCapacity - 1) { + // We share the storage, but there is enough internal storage: Use that. + newStorage = _storage; + newCapacity = _builtinCapacity - 1; + } else { + // We need to allocate storage on the heap! + + // Compute a suitable new capacity limit + newCapacity = computeCapacity(new_len); + + // Allocate new storage + newStorage = (char *)malloc(newCapacity+1); + assert(newStorage); + } + + // Copy old data if needed, elsewise reset the new storage. + if (keep_old) { + assert(_len <= newCapacity); + memcpy(newStorage, _str, _len + 1); + } else { + _len = 0; + newStorage[0] = 0; + } + + // Release hold on the old storage ... + decRefCount(oldRefCount); + + // ... in favor of the new storage + _str = newStorage; + + if (!isStorageIntern()) { + // Set the ref count & capacity if we use an external storage. + // It is important to do this *after* copying any old content, + // else we would override data that has not yet been copied! + _extern._refCount = 0; + _extern._capacity = newCapacity; + } +} + void String::incRefCount() const { assert(!isStorageIntern()); if (_extern._refCount == 0) { @@ -130,11 +211,14 @@ String& String::operator =(const char *str) { uint32 len = strlen(str); ensureCapacity(len, false); _len = len; - memcpy(_str, str, len + 1); + memmove(_str, str, len + 1); return *this; } String &String::operator =(const String &str) { + if (&str == this) + return *this; + if (str.isStorageIntern()) { decRefCount(_extern._refCount); _len = str._len; @@ -154,7 +238,8 @@ String &String::operator =(const String &str) { } String& String::operator =(char c) { - ensureCapacity(1, false); + decRefCount(_extern._refCount); + _str = _storage; _len = 1; _str[0] = c; _str[1] = 0; @@ -237,10 +322,7 @@ void String::deleteLastChar() { void String::deleteChar(uint32 p) { assert(p < _len); - // Call ensureCapacity to make sure we actually *own* the storage - // to which _str points to -- we wouldn't want to modify a storage - // which other string objects are sharing, after all. - ensureCapacity(_len, true); + makeUnique(); while (p++ < _len) _str[p-1] = _str[p]; _len--; @@ -257,7 +339,7 @@ void String::clear() { void String::setChar(char c, uint32 p) { assert(p <= _len); - ensureCapacity(_len, true); + makeUnique(); _str[p] = c; } @@ -272,78 +354,36 @@ void String::insertChar(char c, uint32 p) { } void String::toLowercase() { - ensureCapacity(_len, true); + makeUnique(); for (uint32 i = 0; i < _len; ++i) _str[i] = tolower(_str[i]); } void String::toUppercase() { - ensureCapacity(_len, true); + makeUnique(); for (uint32 i = 0; i < _len; ++i) _str[i] = toupper(_str[i]); } -/** - * Ensure that enough storage is available to store at least new_len - * characters plus a null byte. In addition, if we currently share - * the storage with another string, unshare it, so that we can safely - * write to the storage. - */ -void String::ensureCapacity(uint32 new_len, bool keep_old) { - bool isShared; - uint32 curCapacity, newCapacity; - char *newStorage; - int *oldRefCount = _extern._refCount; - - if (isStorageIntern()) { - isShared = false; - curCapacity = _builtinCapacity - 1; - } else { - isShared = (oldRefCount && *oldRefCount > 1); - curCapacity = _extern._capacity; - } - - // Special case: If there is enough space, and we do not share - // the storage, then there is nothing to do. - if (!isShared && new_len <= curCapacity) +void String::trim() { + if (_len == 0) return; - if (isShared && new_len <= _builtinCapacity - 1) { - // We share the storage, but there is enough internal storage: Use that. - newStorage = _storage; - newCapacity = _builtinCapacity - 1; - } else { - // We need to allocate storage on the heap! - - // Compute a suitable new capacity limit - newCapacity = computeCapacity(new_len); - - // Allocate new storage - newStorage = (char *)malloc(newCapacity+1); - assert(newStorage); - } - - // Copy old data if needed, elsewise reset the new storage. - if (keep_old) { - assert(_len <= newCapacity); - memcpy(newStorage, _str, _len + 1); - } else { - _len = 0; - newStorage[0] = 0; - } + makeUnique(); - // Release hold on the old storage ... - decRefCount(oldRefCount); + // Trim trailing whitespace + while (_len >= 1 && isspace(_str[_len-1])) + _len--; + _str[_len] = 0; - // ... in favor of the new storage - _str = newStorage; + // Trim leading whitespace + char *t = _str; + while (isspace(*t)) + t++; - if (!isStorageIntern()) { - // Set the ref count & capacity if we use an external storage. - // It is important to do this *after* copying any old content, - // else we would override data that has not yet been copied! - _extern._refCount = 0; - _extern._capacity = newCapacity; + if (t != _str) { + _len -= t - _str; + memmove(_str, t, _len + 1); } } |