2020-10-27 22:22:25 +00:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
2021-12-26 17:47:58 +00:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
2020-10-27 22:22:25 +00:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2021-12-26 17:47:58 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2020-10-27 22:22:25 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common/base-str.h"
|
|
|
|
#include "common/hash-str.h"
|
|
|
|
#include "common/list.h"
|
|
|
|
#include "common/memorypool.h"
|
2021-06-29 13:12:20 +00:00
|
|
|
#include "common/textconsole.h"
|
2020-10-27 22:22:25 +00:00
|
|
|
#include "common/util.h"
|
|
|
|
#include "common/mutex.h"
|
|
|
|
|
|
|
|
namespace Common {
|
|
|
|
|
|
|
|
#define TEMPLATE template<class T>
|
|
|
|
#define BASESTRING BaseString<T>
|
|
|
|
|
|
|
|
MemoryPool *g_refCountPool = nullptr; // FIXME: This is never freed right now
|
|
|
|
#ifndef SCUMMVM_UTIL
|
|
|
|
Mutex *g_refCountPoolMutex = nullptr;
|
|
|
|
|
|
|
|
void lockMemoryPoolMutex() {
|
|
|
|
// The Mutex class can only be used once g_system is set and initialized,
|
|
|
|
// but we may use the String class earlier than that (it is for example
|
|
|
|
// used in the OSystem_POSIX constructor). However in those early stages
|
|
|
|
// we can hope we don't have multiple threads either.
|
|
|
|
if (!g_system || !g_system->backendInitialized())
|
|
|
|
return;
|
|
|
|
if (!g_refCountPoolMutex)
|
|
|
|
g_refCountPoolMutex = new Mutex();
|
|
|
|
g_refCountPoolMutex->lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void unlockMemoryPoolMutex() {
|
|
|
|
if (g_refCountPoolMutex)
|
|
|
|
g_refCountPoolMutex->unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::releaseMemoryPoolMutex() {
|
|
|
|
if (g_refCountPoolMutex){
|
|
|
|
delete g_refCountPoolMutex;
|
|
|
|
g_refCountPoolMutex = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static uint32 computeCapacity(uint32 len) {
|
|
|
|
// By default, for the capacity we use the next multiple of 32
|
|
|
|
return ((len + 32 - 1) & ~0x1F);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE
|
|
|
|
BASESTRING::BaseString(const BASESTRING &str)
|
2021-04-15 19:20:04 +00:00
|
|
|
: _size(str._size) {
|
2020-10-27 22:22:25 +00:00
|
|
|
if (str.isStorageIntern()) {
|
|
|
|
// String in internal storage: just copy it
|
|
|
|
memcpy(_storage, str._storage, _builtinCapacity * sizeof(value_type));
|
|
|
|
_str = _storage;
|
|
|
|
} else {
|
|
|
|
// String in external storage: use refcount mechanism
|
|
|
|
str.incRefCount();
|
|
|
|
_extern._refCount = str._extern._refCount;
|
|
|
|
_extern._capacity = str._extern._capacity;
|
|
|
|
_str = str._str;
|
|
|
|
}
|
|
|
|
assert(_str != nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE BASESTRING::BaseString(const value_type *str) : _size(0), _str(_storage) {
|
|
|
|
if (str == nullptr) {
|
|
|
|
_storage[0] = 0;
|
|
|
|
_size = 0;
|
|
|
|
} else {
|
|
|
|
uint32 len = 0;
|
|
|
|
const value_type *s = str;
|
|
|
|
while (*s++) {
|
|
|
|
++len;
|
|
|
|
}
|
|
|
|
initWithValueTypeStr(str, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE BASESTRING::BaseString(const value_type *str, uint32 len) : _size(0), _str(_storage) {
|
|
|
|
initWithValueTypeStr(str, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE BASESTRING::BaseString(const value_type *beginP, const value_type *endP) : _size(0), _str(_storage) {
|
|
|
|
assert(endP >= beginP);
|
|
|
|
initWithValueTypeStr(beginP, endP - beginP);
|
|
|
|
}
|
|
|
|
|
2020-11-05 23:39:48 +00:00
|
|
|
TEMPLATE BASESTRING::~BaseString() {
|
|
|
|
decRefCount(_extern._refCount);
|
|
|
|
}
|
|
|
|
|
2020-10-27 22:22:25 +00:00
|
|
|
TEMPLATE void BASESTRING::makeUnique() {
|
|
|
|
ensureCapacity(_size, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::ensureCapacity(uint32 new_size, bool keep_old) {
|
|
|
|
bool isShared;
|
|
|
|
uint32 curCapacity, newCapacity;
|
|
|
|
value_type *newStorage;
|
|
|
|
int *oldRefCount = _extern._refCount;
|
|
|
|
|
|
|
|
if (isStorageIntern()) {
|
|
|
|
isShared = false;
|
|
|
|
curCapacity = _builtinCapacity;
|
|
|
|
} 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_size < curCapacity)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (isShared && new_size < _builtinCapacity) {
|
|
|
|
// We share the storage, but there is enough internal storage: Use that.
|
|
|
|
newStorage = _storage;
|
|
|
|
newCapacity = _builtinCapacity;
|
|
|
|
} else {
|
|
|
|
// We need to allocate storage on the heap!
|
|
|
|
|
|
|
|
// Compute a suitable new capacity limit
|
|
|
|
// If the current capacity is sufficient we use the same capacity
|
|
|
|
if (new_size < curCapacity)
|
|
|
|
newCapacity = curCapacity;
|
|
|
|
else
|
|
|
|
newCapacity = MAX(curCapacity * 2, computeCapacity(new_size + 1));
|
|
|
|
|
|
|
|
// Allocate new storage
|
|
|
|
newStorage = new value_type[newCapacity];
|
|
|
|
assert(newStorage);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy old data if needed, elsewise reset the new storage.
|
|
|
|
if (keep_old) {
|
|
|
|
assert(_size < newCapacity);
|
|
|
|
memcpy(newStorage, _str, (_size + 1) * sizeof(value_type));
|
|
|
|
} else {
|
|
|
|
_size = 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 = nullptr;
|
|
|
|
_extern._capacity = newCapacity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE
|
|
|
|
void BASESTRING::incRefCount() const {
|
|
|
|
assert(!isStorageIntern());
|
|
|
|
if (_extern._refCount == nullptr) {
|
2021-03-31 22:28:11 +00:00
|
|
|
#ifndef SCUMMVM_UTIL
|
2021-03-31 21:50:11 +00:00
|
|
|
lockMemoryPoolMutex();
|
2021-03-31 22:28:11 +00:00
|
|
|
#endif
|
2020-10-27 22:22:25 +00:00
|
|
|
if (g_refCountPool == nullptr) {
|
|
|
|
g_refCountPool = new MemoryPool(sizeof(int));
|
|
|
|
assert(g_refCountPool);
|
|
|
|
}
|
|
|
|
|
|
|
|
_extern._refCount = (int *)g_refCountPool->allocChunk();
|
2021-03-31 22:28:11 +00:00
|
|
|
#ifndef SCUMMVM_UTIL
|
2021-03-31 21:50:11 +00:00
|
|
|
unlockMemoryPoolMutex();
|
2021-03-31 22:28:11 +00:00
|
|
|
#endif
|
2020-10-27 22:22:25 +00:00
|
|
|
*_extern._refCount = 2;
|
|
|
|
} else {
|
|
|
|
++(*_extern._refCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE
|
|
|
|
void BASESTRING::decRefCount(int *oldRefCount) {
|
|
|
|
if (isStorageIntern())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (oldRefCount) {
|
|
|
|
--(*oldRefCount);
|
|
|
|
}
|
|
|
|
if (!oldRefCount || *oldRefCount <= 0) {
|
|
|
|
// The ref count reached zero, so we free the string storage
|
|
|
|
// and the ref count storage.
|
|
|
|
if (oldRefCount) {
|
2021-03-31 22:28:11 +00:00
|
|
|
#ifndef SCUMMVM_UTIL
|
2021-03-31 21:50:11 +00:00
|
|
|
lockMemoryPoolMutex();
|
2021-03-31 22:28:11 +00:00
|
|
|
#endif
|
2020-10-27 22:22:25 +00:00
|
|
|
assert(g_refCountPool);
|
|
|
|
g_refCountPool->freeChunk(oldRefCount);
|
2021-03-31 22:28:11 +00:00
|
|
|
#ifndef SCUMMVM_UTIL
|
2021-03-31 21:50:11 +00:00
|
|
|
unlockMemoryPoolMutex();
|
2021-03-31 22:28:11 +00:00
|
|
|
#endif
|
2020-10-27 22:22:25 +00:00
|
|
|
}
|
|
|
|
// Coverity thinks that we always free memory, as it assumes
|
|
|
|
// (correctly) that there are cases when oldRefCount == 0
|
|
|
|
// Thus, DO NOT COMPILE, trick it and shut tons of false positives
|
|
|
|
#ifndef __COVERITY__
|
|
|
|
delete[] _str;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Even though _str points to a freed memory block now,
|
|
|
|
// we do not change its value, because any code that calls
|
|
|
|
// decRefCount will have to do this afterwards anyway.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::initWithValueTypeStr(const value_type *str, uint32 len) {
|
|
|
|
assert(str);
|
|
|
|
|
|
|
|
_storage[0] = 0;
|
|
|
|
|
|
|
|
_size = len;
|
|
|
|
|
|
|
|
if (len >= _builtinCapacity) {
|
|
|
|
// Not enough internal storage, so allocate more
|
|
|
|
_extern._capacity = computeCapacity(len + 1);
|
|
|
|
_extern._refCount = nullptr;
|
|
|
|
_str = new value_type[_extern._capacity];
|
|
|
|
assert(_str != nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the string into the storage area
|
|
|
|
memmove(_str, str, len * sizeof(value_type));
|
|
|
|
_str[len] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::equals(const BaseString &x) const {
|
|
|
|
if (this == &x || _str == x._str) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x.size() != _size) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !memcmp(_str, x._str, _size * sizeof(value_type));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::equals(const value_type *ptr) const {
|
|
|
|
if (_str == ptr) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint i = 0;
|
|
|
|
|
|
|
|
for (; i < _size && *ptr; i++, ptr++) {
|
|
|
|
if (_str[i] != *ptr)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == _size && *ptr == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::equalsC(const char *ptr) const {
|
|
|
|
uint i = 0;
|
|
|
|
|
|
|
|
for (; i < _size && *ptr; i++, ptr++) {
|
2020-12-21 23:24:46 +00:00
|
|
|
if (_str[i] != (T)*ptr)
|
2020-10-27 22:22:25 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == _size && *ptr == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator==(const BaseString &x) const {
|
|
|
|
return equals(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator==(const value_type *x) const {
|
2020-11-06 09:44:25 +00:00
|
|
|
return equals(x);
|
2020-10-27 22:22:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator!=(const BaseString &x) const {
|
|
|
|
return !equals(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator!=(const value_type *x) const {
|
2020-11-06 09:44:25 +00:00
|
|
|
return !equals(x);
|
2020-10-27 22:22:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE int BASESTRING::compareTo(const BaseString &x) const {
|
|
|
|
for (uint32 i = 0, n = x.size(); i < _size && i < n; ++i) {
|
|
|
|
uint32 sc = _str[i];
|
|
|
|
uint32 xc = x[i];
|
|
|
|
if (sc < xc)
|
|
|
|
return -1;
|
|
|
|
else if (sc > xc)
|
|
|
|
return +1;
|
|
|
|
}
|
|
|
|
if (_size < x.size())
|
|
|
|
return -1;
|
|
|
|
if (_size == x.size())
|
|
|
|
return 0;
|
|
|
|
return +1;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE int BASESTRING::compareTo(const value_type *ptr) const {
|
|
|
|
uint32 i = 0;
|
|
|
|
for (; i < _size && *ptr; ++i, ptr++) {
|
|
|
|
uint32 sc = _str[i];
|
|
|
|
uint32 xc = *ptr;
|
|
|
|
if (sc < xc)
|
|
|
|
return -1;
|
|
|
|
else if (sc > xc)
|
|
|
|
return +1;
|
|
|
|
}
|
|
|
|
if (i == _size && *ptr == 0)
|
|
|
|
return 0;
|
|
|
|
if (*ptr == 0)
|
|
|
|
return +1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE int BASESTRING::compareToC(const char *ptr) const {
|
|
|
|
uint32 i = 0;
|
|
|
|
for (; i < _size && *ptr; ++i, ptr++) {
|
|
|
|
uint32 sc = _str[i];
|
|
|
|
uint32 xc = *ptr;
|
|
|
|
if (sc < xc)
|
|
|
|
return -1;
|
|
|
|
else if (sc > xc)
|
|
|
|
return +1;
|
|
|
|
}
|
|
|
|
if (i == _size && *ptr == 0)
|
|
|
|
return 0;
|
|
|
|
if (*ptr == 0)
|
|
|
|
return +1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator<(const BaseString &x) const {
|
|
|
|
return compareTo(x) < 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator<=(const BaseString &x) const {
|
|
|
|
return compareTo(x) <= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator>(const BaseString &x) const {
|
|
|
|
return compareTo(x) > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator>=(const BaseString &x) const {
|
|
|
|
return compareTo(x) >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator<(const value_type *x) const {
|
|
|
|
return compareTo(x) < 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator<=(const value_type *x) const {
|
|
|
|
return compareTo(x) <= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator>(const value_type *x) const {
|
|
|
|
return compareTo(x) > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::operator>=(const value_type *x) const {
|
|
|
|
return compareTo(x) >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::contains(value_type x) const {
|
|
|
|
for (uint32 i = 0; i < _size; ++i) {
|
|
|
|
if (_str[i] == x) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::contains(const BaseString &otherString) const {
|
|
|
|
if (empty() || otherString.empty() || _size < otherString.size()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-06-20 18:11:13 +00:00
|
|
|
for (const_iterator cur = begin(); cur != end(); ++cur) {
|
|
|
|
uint i = 0;
|
|
|
|
while (true) {
|
|
|
|
if (i == otherString.size()) {
|
2020-10-27 22:22:25 +00:00
|
|
|
return true;
|
2021-06-20 18:11:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cur[i] != otherString[i]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++i;
|
2020-10-27 22:22:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::insertChar(value_type c, uint32 p) {
|
|
|
|
assert(p <= _size);
|
|
|
|
|
|
|
|
ensureCapacity(_size + 1, true);
|
|
|
|
_size++;
|
|
|
|
for (uint32 i = _size; i > p; --i)
|
|
|
|
_str[i] = _str[i - 1];
|
|
|
|
_str[p] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::deleteChar(uint32 p) {
|
|
|
|
assert(p < _size);
|
|
|
|
|
|
|
|
makeUnique();
|
|
|
|
while (p++ < _size)
|
|
|
|
_str[p - 1] = _str[p];
|
|
|
|
_size--;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::deleteLastChar() {
|
|
|
|
if (_size > 0)
|
|
|
|
deleteChar(_size - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::erase(uint32 p, uint32 len) {
|
2020-11-03 05:49:05 +00:00
|
|
|
if (len == 0)
|
|
|
|
return;
|
|
|
|
|
2020-10-27 22:22:25 +00:00
|
|
|
assert(p < _size);
|
|
|
|
|
|
|
|
makeUnique();
|
|
|
|
// If len == npos or p + len is over the end, remove all the way to the end
|
|
|
|
if (len == npos || p + len >= _size) {
|
|
|
|
// Delete char at p as well. So _size = (p - 1) + 1
|
|
|
|
_size = p;
|
|
|
|
// Null terminate
|
|
|
|
_str[_size] = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( ; p + len <= _size; p++) {
|
|
|
|
_str[p] = _str[p + len];
|
|
|
|
}
|
|
|
|
_size -= len;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE typename BASESTRING::iterator BASESTRING::erase(iterator it) {
|
|
|
|
this->deleteChar(it - _str);
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::clear() {
|
|
|
|
decRefCount(_extern._refCount);
|
|
|
|
|
|
|
|
_size = 0;
|
|
|
|
_str = _storage;
|
|
|
|
_storage[0] = 0;
|
|
|
|
}
|
|
|
|
|
2021-05-04 08:45:03 +00:00
|
|
|
|
2020-10-27 22:22:25 +00:00
|
|
|
TEMPLATE void BASESTRING::setChar(value_type c, uint32 p) {
|
|
|
|
assert(p < _size);
|
|
|
|
|
|
|
|
makeUnique();
|
|
|
|
_str[p] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::assign(const BaseString &str) {
|
|
|
|
if (&str == this)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (str.isStorageIntern()) {
|
|
|
|
decRefCount(_extern._refCount);
|
|
|
|
_size = str._size;
|
|
|
|
_str = _storage;
|
|
|
|
memcpy(_str, str._str, (_size + 1) * sizeof(value_type));
|
|
|
|
} else {
|
|
|
|
str.incRefCount();
|
|
|
|
decRefCount(_extern._refCount);
|
|
|
|
|
|
|
|
_extern._refCount = str._extern._refCount;
|
|
|
|
_extern._capacity = str._extern._capacity;
|
|
|
|
_size = str._size;
|
|
|
|
_str = str._str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::assign(value_type c) {
|
|
|
|
decRefCount(_extern._refCount);
|
|
|
|
_str = _storage;
|
|
|
|
|
|
|
|
_str[0] = c;
|
|
|
|
_str[1] = 0;
|
|
|
|
|
|
|
|
_size = (c == 0) ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::insertString(const value_type *s, uint32 p) {
|
|
|
|
while (*s != '\0') {
|
|
|
|
BaseString::insertChar(*s++, p++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::insertString(const BaseString &s, uint32 p) {
|
2020-11-05 23:10:52 +00:00
|
|
|
for (uint i = 0; i < s._size; i++) {
|
2020-10-27 22:22:25 +00:00
|
|
|
BaseString::insertChar(s[i], p+i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE uint32 BASESTRING::find(value_type x, uint32 pos) const {
|
|
|
|
for (uint32 i = pos; i < _size; ++i) {
|
|
|
|
if (_str[i] == x) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE uint32 BASESTRING::find(const BaseString &str, uint32 pos) const {
|
|
|
|
if (pos >= _size) {
|
|
|
|
return npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
const value_type *strP = str.c_str();
|
|
|
|
|
|
|
|
for (const_iterator cur = begin() + pos; cur != end(); ++cur) {
|
|
|
|
uint i = 0;
|
|
|
|
while (true) {
|
|
|
|
if (!strP[i]) {
|
|
|
|
return cur - begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur[i] != strP[i]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE size_t BASESTRING::find(const value_type *strP, uint32 pos) const {
|
2020-11-05 23:10:52 +00:00
|
|
|
if (pos >= _size) {
|
2020-10-27 22:22:25 +00:00
|
|
|
return npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const_iterator cur = begin() + pos; cur != end(); ++cur) {
|
|
|
|
uint i = 0;
|
|
|
|
while (true) {
|
|
|
|
if (!strP[i]) {
|
|
|
|
return cur - begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur[i] != strP[i]) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return npos;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE uint64 BASESTRING::asUint64() const {
|
|
|
|
uint64 result = 0;
|
|
|
|
for (uint32 i = 0; i < _size; ++i) {
|
|
|
|
if (_str[i] < '0' || _str[i] > '9') break;
|
|
|
|
result = result * 10L + (_str[i] - '0');
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE uint64 BASESTRING::asUint64Ext() const {
|
|
|
|
uint64 result = 0;
|
|
|
|
uint64 base = 10;
|
|
|
|
uint32 skip = 0;
|
2021-05-04 08:45:03 +00:00
|
|
|
|
2020-10-27 22:22:25 +00:00
|
|
|
if (_size >= 3 && _str[0] == '0' && _str[1] == 'x') {
|
|
|
|
base = 16;
|
|
|
|
skip = 2;
|
|
|
|
} else if (_size >= 2 && _str[0] == '0') {
|
|
|
|
base = 8;
|
|
|
|
skip = 1;
|
|
|
|
} else {
|
|
|
|
base = 10;
|
|
|
|
skip = 0;
|
|
|
|
}
|
|
|
|
for (uint32 i = skip; i < _size; ++i) {
|
|
|
|
char digit = _str[i];
|
|
|
|
uint64 digitval = 17; // sentinel
|
|
|
|
if (digit >= '0' && digit <= '9')
|
|
|
|
digitval = digit - '0';
|
|
|
|
else if (digit >= 'a' && digit <= 'f')
|
|
|
|
digitval = digit - 'a' + 10;
|
|
|
|
else if (digit >= 'A' && digit <= 'F')
|
|
|
|
digitval = digit - 'A' + 10;
|
|
|
|
if (digitval > base)
|
|
|
|
break;
|
|
|
|
result = result * base + digitval;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef SCUMMVM_UTIL
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::wordWrap(const uint32 maxLength) {
|
|
|
|
if (_size < maxLength) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
makeUnique();
|
|
|
|
|
|
|
|
const uint32 kNoSpace = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
uint32 i = 0;
|
|
|
|
while (i < _size) {
|
|
|
|
uint32 lastSpace = kNoSpace;
|
|
|
|
uint32 x = 0;
|
|
|
|
while (i < _size && x <= maxLength) {
|
|
|
|
const char c = _str[i];
|
|
|
|
if (c == '\n') {
|
|
|
|
lastSpace = kNoSpace;
|
|
|
|
x = 0;
|
|
|
|
} else {
|
|
|
|
if (Common::isSpace(c)) {
|
|
|
|
lastSpace = i;
|
|
|
|
}
|
|
|
|
++x;
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x > maxLength) {
|
|
|
|
if (lastSpace == kNoSpace) {
|
|
|
|
insertChar('\n', i - 1);
|
|
|
|
} else {
|
|
|
|
setChar('\n', lastSpace);
|
|
|
|
i = lastSpace + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::toLowercase() {
|
|
|
|
makeUnique();
|
|
|
|
for (uint32 i = 0; i < _size; ++i) {
|
|
|
|
if (_str[i] > 0 && _str[i] < 128) {
|
|
|
|
_str[i] = tolower(_str[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::toUppercase() {
|
|
|
|
makeUnique();
|
|
|
|
for (uint32 i = 0; i < _size; ++i) {
|
2020-11-05 23:10:52 +00:00
|
|
|
if (_str[i] > 0 && _str[i] < 128) {
|
2020-10-27 22:22:25 +00:00
|
|
|
_str[i] = toupper(_str[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef SCUMMVM_UTIL
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::trim() {
|
|
|
|
if (_size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
makeUnique();
|
|
|
|
|
|
|
|
// Trim trailing whitespace
|
|
|
|
while (_size >= 1 && isSpace(_str[_size - 1]))
|
|
|
|
--_size;
|
|
|
|
_str[_size] = 0;
|
|
|
|
|
|
|
|
// Trim leading whitespace
|
|
|
|
value_type *t = _str;
|
|
|
|
while (isSpace(*t))
|
|
|
|
t++;
|
|
|
|
|
|
|
|
if (t != _str) {
|
|
|
|
_size -= t - _str;
|
|
|
|
memmove(_str, t, (_size + 1) * sizeof(_str[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::assignAppend(value_type c) {
|
COMMON: Add warning when adding \0 to a String
Some engines do this, and it's not really known what should happen. We
need to decide on a behavior, and stick to that. This warning is to help
find where it happens.
From my own tests, it can happen in at least ADL, Avalanche, Chewy,
Director, Blazing Dragons, MacVenture, MADS, Prince, Stark, Startrek,
and Trecision. Only ADL is known to be broken by the current behavior.
The result of my test can be found at
http://www.update.uu.se/~d91tan/tmp/string.txt
2021-06-29 11:33:17 +00:00
|
|
|
if (c == 0) {
|
2021-06-29 14:39:27 +00:00
|
|
|
#ifndef SCUMMVM_UTIL
|
2021-07-13 08:27:59 +00:00
|
|
|
warning("Adding \\0 to String. This is permitted, but can have unwanted consequences. (This warning will be removed later.)");
|
2021-06-29 14:39:27 +00:00
|
|
|
#endif
|
COMMON: Add warning when adding \0 to a String
Some engines do this, and it's not really known what should happen. We
need to decide on a behavior, and stick to that. This warning is to help
find where it happens.
From my own tests, it can happen in at least ADL, Avalanche, Chewy,
Director, Blazing Dragons, MacVenture, MADS, Prince, Stark, Startrek,
and Trecision. Only ADL is known to be broken by the current behavior.
The result of my test can be found at
http://www.update.uu.se/~d91tan/tmp/string.txt
2021-06-29 11:33:17 +00:00
|
|
|
}
|
2020-10-27 22:22:25 +00:00
|
|
|
ensureCapacity(_size + 1, true);
|
|
|
|
|
|
|
|
_str[_size++] = c;
|
|
|
|
_str[_size] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::assignAppend(const BaseString &str) {
|
2020-11-05 23:10:52 +00:00
|
|
|
if (&str == this) {
|
2020-10-27 22:22:25 +00:00
|
|
|
assignAppend(BaseString(str));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int len = str._size;
|
|
|
|
if (len > 0) {
|
|
|
|
ensureCapacity(_size + len, true);
|
|
|
|
|
|
|
|
memcpy(_str + _size, str._str, (len + 1) * sizeof(value_type));
|
|
|
|
_size += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE bool BASESTRING::pointerInOwnBuffer(const value_type *str) const {
|
|
|
|
//compared pointers must be in the same array or UB
|
|
|
|
//cast to intptr however is IB
|
|
|
|
//which includes comparision of the values
|
|
|
|
uintptr ownBuffStart = (uintptr)_str;
|
|
|
|
uintptr ownBuffEnd = (uintptr)(_str + _size);
|
|
|
|
uintptr candidateAddr = (uintptr)str;
|
|
|
|
return ownBuffStart <= candidateAddr && candidateAddr <= ownBuffEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::assignAppend(const value_type *str) {
|
|
|
|
if (pointerInOwnBuffer(str)) {
|
|
|
|
assignAppend(BaseString(str));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 len;
|
|
|
|
for (len = 0; str[len]; len++);
|
|
|
|
if (len > 0) {
|
|
|
|
ensureCapacity(_size + len, true);
|
|
|
|
|
|
|
|
memcpy(_str + _size, str, (len + 1) * sizeof(value_type));
|
|
|
|
_size += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE void BASESTRING::assign(const value_type *str) {
|
|
|
|
uint32 len;
|
|
|
|
for (len = 0; str[len]; len++);
|
|
|
|
ensureCapacity(len, false);
|
|
|
|
_size = len;
|
|
|
|
memmove(_str, str, (len + 1) * sizeof(value_type));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMPLATE uint BASESTRING::getUnsignedValue(uint pos) const {
|
|
|
|
const int shift = (sizeof(uint) - sizeof(value_type)) * 8;
|
|
|
|
return ((uint)_str[pos]) << shift >> shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hash function for strings, taken from CPython.
|
|
|
|
TEMPLATE uint BASESTRING::hash() const {
|
2020-11-01 13:58:46 +00:00
|
|
|
uint hashResult = getUnsignedValue(0) << 7;
|
2020-10-27 22:22:25 +00:00
|
|
|
for (uint i = 0; i < _size; i++) {
|
2020-11-01 13:58:46 +00:00
|
|
|
hashResult = (1000003 * hashResult) ^ getUnsignedValue(i);
|
2020-10-27 22:22:25 +00:00
|
|
|
}
|
2020-11-01 13:58:46 +00:00
|
|
|
return hashResult ^ _size;
|
2020-10-27 22:22:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template class BaseString<char>;
|
|
|
|
template class BaseString<u32char_type_t>;
|
|
|
|
|
|
|
|
}
|