COMMON: Merge code for str.cpp and ustr.cpp
Most of the code in str.cpp and ustr.cpp is actually the same. Do some template magic to merge them.
This commit is contained in:
parent
a286db2d3c
commit
29cfa7bb0f
31 changed files with 1216 additions and 3378 deletions
|
@ -57,5 +57,5 @@ Common::String MusicDevice::getCompleteId() {
|
||||||
}
|
}
|
||||||
|
|
||||||
MidiDriver::DeviceHandle MusicDevice::getHandle() {
|
MidiDriver::DeviceHandle MusicDevice::getHandle() {
|
||||||
return (MidiDriver::DeviceHandle)Common::hashit(getCompleteId());
|
return (MidiDriver::DeviceHandle)getCompleteId().hash();
|
||||||
}
|
}
|
||||||
|
|
803
common/base-str.cpp
Normal file
803
common/base-str.cpp
Normal file
|
@ -0,0 +1,803 @@
|
||||||
|
/* 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.
|
||||||
|
*
|
||||||
|
* 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 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/base-str.h"
|
||||||
|
#include "common/hash-str.h"
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/memorypool.h"
|
||||||
|
#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)
|
||||||
|
: _size(str._size) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (g_refCountPool == nullptr) {
|
||||||
|
g_refCountPool = new MemoryPool(sizeof(int));
|
||||||
|
assert(g_refCountPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
_extern._refCount = (int *)g_refCountPool->allocChunk();
|
||||||
|
*_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) {
|
||||||
|
assert(g_refCountPool);
|
||||||
|
g_refCountPool->freeChunk(oldRefCount);
|
||||||
|
}
|
||||||
|
// 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 void BASESTRING::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;
|
||||||
|
|
||||||
|
_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
|
||||||
|
for (size_t idx = 0; idx < len; ++idx, ++str)
|
||||||
|
_str[idx] = (byte)(*str);
|
||||||
|
|
||||||
|
_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++) {
|
||||||
|
if (_str[i] != *ptr)
|
||||||
|
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 {
|
||||||
|
return equals(BaseString(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEMPLATE bool BASESTRING::operator!=(const BaseString &x) const {
|
||||||
|
return !equals(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEMPLATE bool BASESTRING::operator!=(const value_type *x) const {
|
||||||
|
return !equals(BaseString(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 size = 0;
|
||||||
|
BaseString::const_iterator itr = otherString.begin();
|
||||||
|
|
||||||
|
for (BaseString::const_iterator itr2 = begin(); itr != otherString.end() && itr2 != end(); itr2++) {
|
||||||
|
if (*itr == *itr2) {
|
||||||
|
itr++;
|
||||||
|
size++;
|
||||||
|
if (size == otherString.size())
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
size = 0;
|
||||||
|
itr = otherString.begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
||||||
|
for (uint i = 0; i < s._size; i++) {
|
||||||
|
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 {
|
||||||
|
if (pos >= _size) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (_str[i] > 0 && _str[i] < 128) {
|
||||||
|
_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) {
|
||||||
|
ensureCapacity(_size + 1, true);
|
||||||
|
|
||||||
|
_str[_size++] = c;
|
||||||
|
_str[_size] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEMPLATE void BASESTRING::assignAppend(const BaseString &str) {
|
||||||
|
if (&str == this) {
|
||||||
|
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 {
|
||||||
|
uint hash = getUnsignedValue(0) << 7;
|
||||||
|
for (uint i = 0; i < _size; i++) {
|
||||||
|
hash = (1000003 * hash) ^ getUnsignedValue(i);
|
||||||
|
}
|
||||||
|
return hash ^ _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template class BaseString<char>;
|
||||||
|
template class BaseString<u32char_type_t>;
|
||||||
|
|
||||||
|
}
|
258
common/base-str.h
Normal file
258
common/base-str.h
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
/* 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.
|
||||||
|
*
|
||||||
|
* 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 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMON_BASE_STRING_H
|
||||||
|
#define COMMON_BASE_STRING_H
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
#include "common/str-enc.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
template<class T>
|
||||||
|
class BaseString {
|
||||||
|
public:
|
||||||
|
static void releaseMemoryPoolMutex();
|
||||||
|
|
||||||
|
static const uint32 npos = 0xFFFFFFFF;
|
||||||
|
typedef T value_type;
|
||||||
|
typedef T * iterator;
|
||||||
|
typedef const T * const_iterator;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* The size of the internal storage. Increasing this means less heap
|
||||||
|
* allocations are needed, at the cost of more stack memory usage,
|
||||||
|
* and of course lots of wasted memory.
|
||||||
|
*/
|
||||||
|
static const uint32 _builtinCapacity = 32 - (sizeof(uint32) + sizeof(char *)) / sizeof(value_type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the string. Stored to avoid having to call strlen
|
||||||
|
* a lot. Yes, we limit ourselves to strings shorter than 4GB --
|
||||||
|
* on purpose :-).
|
||||||
|
*/
|
||||||
|
uint32 _size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to the actual string storage. Either points to _storage,
|
||||||
|
* or to a block allocated on the heap via malloc.
|
||||||
|
*/
|
||||||
|
value_type *_str;
|
||||||
|
|
||||||
|
|
||||||
|
union {
|
||||||
|
/**
|
||||||
|
* Internal string storage.
|
||||||
|
*/
|
||||||
|
value_type _storage[_builtinCapacity];
|
||||||
|
/**
|
||||||
|
* External string storage data -- the refcounter, and the
|
||||||
|
* capacity of the string _str points to.
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
mutable int *_refCount;
|
||||||
|
uint32 _capacity;
|
||||||
|
} _extern;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool isStorageIntern() const {
|
||||||
|
return _str == _storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Construct a new empty string. */
|
||||||
|
BaseString() : _size(0), _str(_storage) { _storage[0] = 0; }
|
||||||
|
|
||||||
|
/** Construct a copy of the given string. */
|
||||||
|
BaseString(const BaseString &str);
|
||||||
|
|
||||||
|
/** Construct a new string from the given NULL-terminated C string. */
|
||||||
|
explicit BaseString(const value_type *str);
|
||||||
|
|
||||||
|
/** Construct a new string containing exactly len characters read from address str. */
|
||||||
|
BaseString(const value_type *str, uint32 len);
|
||||||
|
|
||||||
|
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
|
||||||
|
BaseString(const value_type *beginP, const value_type *endP);
|
||||||
|
|
||||||
|
bool operator==(const BaseString &x) const;
|
||||||
|
bool operator==(const value_type *x) const;
|
||||||
|
bool operator!=(const BaseString &x) const;
|
||||||
|
bool operator!=(const value_type *x) const;
|
||||||
|
|
||||||
|
bool operator<(const BaseString &x) const;
|
||||||
|
bool operator<(const value_type *x) const;
|
||||||
|
bool operator<=(const BaseString &x) const;
|
||||||
|
bool operator<=(const value_type *x) const;
|
||||||
|
bool operator>(const BaseString &x) const;
|
||||||
|
bool operator>(const value_type *x) const;
|
||||||
|
bool operator>=(const BaseString &x) const;
|
||||||
|
bool operator>=(const value_type *x) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares whether two BaseString are the same based on memory comparison.
|
||||||
|
* This does *not* do comparison based on canonical equivalence.
|
||||||
|
*/
|
||||||
|
bool equals(const BaseString &x) const;
|
||||||
|
bool equals(const value_type *x) const;
|
||||||
|
bool equalsC(const char *x) const;
|
||||||
|
int compareTo(const BaseString &x) const; // strcmp clone
|
||||||
|
int compareTo(const value_type *x) const; // strcmp clone
|
||||||
|
int compareToC(const char *x) const; // strcmp clone
|
||||||
|
|
||||||
|
/** Set character c at position p, replacing the previous character there. */
|
||||||
|
void setChar(value_type c, uint32 p);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the value at position p from the string.
|
||||||
|
* Using this on decomposed characters will not remove the whole
|
||||||
|
* character!
|
||||||
|
*/
|
||||||
|
void deleteChar(uint32 p);
|
||||||
|
|
||||||
|
/** Remove the last character from the string. */
|
||||||
|
void deleteLastChar();
|
||||||
|
|
||||||
|
/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
|
||||||
|
void erase(uint32 p, uint32 len = npos);
|
||||||
|
|
||||||
|
/** Erases the character at the given iterator location */
|
||||||
|
iterator erase(iterator it);
|
||||||
|
|
||||||
|
/** Clears the string, making it empty. */
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
iterator begin() {
|
||||||
|
// Since the user could potentially
|
||||||
|
// change the string via the returned
|
||||||
|
// iterator we have to assure we are
|
||||||
|
// pointing to a unique storage.
|
||||||
|
makeUnique();
|
||||||
|
|
||||||
|
return _str;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() {
|
||||||
|
return begin() + size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const {
|
||||||
|
return _str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator end() const {
|
||||||
|
return begin() + size();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const value_type *c_str() const { return _str; }
|
||||||
|
inline uint size() const { return _size; }
|
||||||
|
|
||||||
|
inline bool empty() const { return (_size == 0); }
|
||||||
|
value_type firstChar() const { return (_size > 0) ? _str[0] : 0; }
|
||||||
|
value_type lastChar() const { return (_size > 0) ? _str[_size - 1] : 0; }
|
||||||
|
|
||||||
|
value_type operator[](int idx) const {
|
||||||
|
assert(_str && idx >= 0 && idx < (int)_size);
|
||||||
|
return _str[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given string is present in the internal string or not.
|
||||||
|
*/
|
||||||
|
bool contains(const BaseString &otherString) const;
|
||||||
|
bool contains(value_type x) const;
|
||||||
|
|
||||||
|
/** Insert character c before position p. */
|
||||||
|
void insertChar(value_type c, uint32 p);
|
||||||
|
void insertString(const value_type *s, uint32 p);
|
||||||
|
void insertString(const BaseString &s, uint32 p);
|
||||||
|
|
||||||
|
/** Finds the index of a character in the string */
|
||||||
|
uint32 find(value_type x, uint32 pos = 0) const;
|
||||||
|
/** Does a find for the passed string */
|
||||||
|
size_t find(const value_type *s, uint32 pos = 0) const;
|
||||||
|
uint32 find(const BaseString &str, uint32 pos = 0) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the text in the string to the given line maximum. Lines will be
|
||||||
|
* broken at any whitespace character. New lines are assumed to be
|
||||||
|
* represented using '\n'.
|
||||||
|
*
|
||||||
|
* This is a very basic line wrap which does not perform tab stop
|
||||||
|
* calculation, consecutive whitespace collapsing, auto-hyphenation, or line
|
||||||
|
* balancing.
|
||||||
|
*/
|
||||||
|
void wordWrap(const uint32 maxLength);
|
||||||
|
|
||||||
|
/** Return uint64 corrensponding to String's contents. */
|
||||||
|
uint64 asUint64() const;
|
||||||
|
|
||||||
|
/** Return uint64 corrensponding to String's contents. This variant recognizes 0 (oct) and 0x (hex) prefixes. */
|
||||||
|
uint64 asUint64Ext() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert all characters in the string to lowercase.
|
||||||
|
*
|
||||||
|
* Be aware that this only affects the case of ASCII characters. All
|
||||||
|
* other characters will not be touched at all.
|
||||||
|
*/
|
||||||
|
void toLowercase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert all characters in the string to uppercase.
|
||||||
|
*
|
||||||
|
* Be aware that this only affects the case of ASCII characters. All
|
||||||
|
* other characters will not be touched at all.
|
||||||
|
*/
|
||||||
|
void toUppercase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes trailing and leading whitespaces. Uses isspace() to decide
|
||||||
|
* what is whitespace and what not.
|
||||||
|
*/
|
||||||
|
void trim();
|
||||||
|
|
||||||
|
uint hash() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void makeUnique();
|
||||||
|
void ensureCapacity(uint32 new_size, bool keep_old);
|
||||||
|
void incRefCount() const;
|
||||||
|
void decRefCount(int *oldRefCount);
|
||||||
|
void initWithValueTypeStr(const value_type *str, uint32 len);
|
||||||
|
void initWithCStr(const char *str, uint32 len);
|
||||||
|
|
||||||
|
void assignAppend(const value_type *str);
|
||||||
|
void assignAppend(value_type c);
|
||||||
|
void assignAppend(const BaseString &str);
|
||||||
|
void assign(const BaseString &str);
|
||||||
|
void assign(value_type c);
|
||||||
|
void assign(const value_type *str);
|
||||||
|
|
||||||
|
bool pointerInOwnBuffer(const value_type *str) const;
|
||||||
|
|
||||||
|
uint getUnsignedValue(uint pos) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -30,7 +30,6 @@ namespace Common {
|
||||||
|
|
||||||
uint hashit(const char *str);
|
uint hashit(const char *str);
|
||||||
uint hashit_lower(const char *str); // Generate a hash based on the lowercase version of the string
|
uint hashit_lower(const char *str); // Generate a hash based on the lowercase version of the string
|
||||||
inline uint hashit(const String &str) { return hashit(str.c_str()); }
|
|
||||||
inline uint hashit_lower(const String &str) { return hashit_lower(str.c_str()); }
|
inline uint hashit_lower(const String &str) { return hashit_lower(str.c_str()); }
|
||||||
|
|
||||||
// FIXME: The following functors obviously are not consistently named
|
// FIXME: The following functors obviously are not consistently named
|
||||||
|
@ -40,7 +39,7 @@ struct CaseSensitiveString_EqualTo {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CaseSensitiveString_Hash {
|
struct CaseSensitiveString_Hash {
|
||||||
uint operator()(const String& x) const { return hashit(x.c_str()); }
|
uint operator()(const String& x) const { return x.hash(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,7 +60,14 @@ struct IgnoreCase_Hash {
|
||||||
template<>
|
template<>
|
||||||
struct Hash<String> {
|
struct Hash<String> {
|
||||||
uint operator()(const String& s) const {
|
uint operator()(const String& s) const {
|
||||||
return hashit(s.c_str());
|
return s.hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Hash<U32String> {
|
||||||
|
uint operator()(const U32String& s) const {
|
||||||
|
return s.hash();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ MODULE := common
|
||||||
MODULE_OBJS := \
|
MODULE_OBJS := \
|
||||||
achievements.o \
|
achievements.o \
|
||||||
archive.o \
|
archive.o \
|
||||||
|
base-str.o \
|
||||||
config-manager.o \
|
config-manager.o \
|
||||||
coroutines.o \
|
coroutines.o \
|
||||||
dcl.o \
|
dcl.o \
|
||||||
|
|
|
@ -264,6 +264,29 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync a U32-string
|
||||||
|
*/
|
||||||
|
void syncString32(U32String &str, Version minVersion = 0, Version maxVersion = kLastVersion) {
|
||||||
|
if (_version < minVersion || _version > maxVersion)
|
||||||
|
return; // Ignore anything which is not supposed to be present in this save game version
|
||||||
|
|
||||||
|
uint32 len = str.size();
|
||||||
|
|
||||||
|
syncAsUint32LE(len);
|
||||||
|
|
||||||
|
if (isLoading()) {
|
||||||
|
U32String::value_type *sl = new U32String::value_type[len];
|
||||||
|
for (uint i = 0; i < len; i++)
|
||||||
|
syncAsUint32LE(sl[i]);
|
||||||
|
str = U32String(sl, len);
|
||||||
|
} else {
|
||||||
|
for (uint i = 0; i < len; i++)
|
||||||
|
_saveStream->writeUint32LE(str[i]);
|
||||||
|
_bytesSynced += 4 * len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void syncArray(T *arr, size_t entries, void (*serializer)(Serializer &, T &), Version minVersion = 0, Version maxVersion = kLastVersion) {
|
void syncArray(T *arr, size_t entries, void (*serializer)(Serializer &, T &), Version minVersion = 0, Version maxVersion = kLastVersion) {
|
||||||
if (_version < minVersion || _version > maxVersion)
|
if (_version < minVersion || _version > maxVersion)
|
||||||
|
|
566
common/str.cpp
566
common/str.cpp
|
@ -29,95 +29,8 @@
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
MemoryPool *g_refCountPool = nullptr; // FIXME: This is never freed right now
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::releaseMemoryPoolMutex() {
|
|
||||||
if (g_refCountPoolMutex){
|
|
||||||
delete g_refCountPoolMutex;
|
|
||||||
g_refCountPoolMutex = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32 computeCapacity(uint32 len) {
|
|
||||||
// By default, for the capacity we use the next multiple of 32
|
|
||||||
return ((len + 32 - 1) & ~0x1F);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *str) : _size(0), _str(_storage) {
|
|
||||||
if (str == nullptr) {
|
|
||||||
_storage[0] = 0;
|
|
||||||
_size = 0;
|
|
||||||
} else
|
|
||||||
initWithCStr(str, strlen(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
|
|
||||||
initWithCStr(str, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *beginP, const char *endP) : _size(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;
|
|
||||||
|
|
||||||
_size = len;
|
|
||||||
|
|
||||||
if (len >= _builtinCapacity) {
|
|
||||||
// Not enough internal storage, so allocate more
|
|
||||||
_extern._capacity = computeCapacity(len + 1);
|
|
||||||
_extern._refCount = nullptr;
|
|
||||||
_str = new char[_extern._capacity];
|
|
||||||
assert(_str != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the string into the storage area
|
|
||||||
memmove(_str, str, len);
|
|
||||||
_str[len] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const String &str)
|
|
||||||
: _size(str._size) {
|
|
||||||
if (str.isStorageIntern()) {
|
|
||||||
// String in internal storage: just copy it
|
|
||||||
memcpy(_storage, str._storage, _builtinCapacity);
|
|
||||||
_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(char c)
|
String::String(char c)
|
||||||
: _size(0), _str(_storage) {
|
: BaseString() {
|
||||||
|
|
||||||
_storage[0] = c;
|
_storage[0] = c;
|
||||||
_storage[1] = 0;
|
_storage[1] = 0;
|
||||||
|
@ -125,214 +38,45 @@ String::String(char c)
|
||||||
_size = (c == 0) ? 0 : 1;
|
_size = (c == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef SCUMMVM_UTIL
|
||||||
String::String(const U32String &str)
|
String::String(const U32String &str)
|
||||||
: _size(0), _str(_storage) {
|
: BaseString() {
|
||||||
_storage[0] = 0;
|
_storage[0] = 0;
|
||||||
*this = String(str.encode());
|
*this = String(str.encode());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
String::~String() {
|
String::~String() {
|
||||||
decRefCount(_extern._refCount);
|
decRefCount(_extern._refCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::makeUnique() {
|
|
||||||
ensureCapacity(_size, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that enough storage is available to store at least new_size
|
|
||||||
* 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_size, bool keep_old) {
|
|
||||||
bool isShared;
|
|
||||||
uint32 curCapacity, newCapacity;
|
|
||||||
char *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;
|
|
||||||
|
|
||||||
// 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 char[newCapacity];
|
|
||||||
assert(newStorage);
|
|
||||||
|
|
||||||
|
|
||||||
// Copy old data if needed, elsewise reset the new storage.
|
|
||||||
if (keep_old) {
|
|
||||||
assert(_size < newCapacity);
|
|
||||||
memcpy(newStorage, _str, _size + 1);
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::incRefCount() const {
|
|
||||||
assert(!isStorageIntern());
|
|
||||||
if (_extern._refCount == nullptr) {
|
|
||||||
lockMemoryPoolMutex();
|
|
||||||
if (g_refCountPool == nullptr) {
|
|
||||||
g_refCountPool = new MemoryPool(sizeof(int));
|
|
||||||
assert(g_refCountPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
_extern._refCount = (int *)g_refCountPool->allocChunk();
|
|
||||||
unlockMemoryPoolMutex();
|
|
||||||
*_extern._refCount = 2;
|
|
||||||
} else {
|
|
||||||
++(*_extern._refCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::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) {
|
|
||||||
lockMemoryPoolMutex();
|
|
||||||
assert(g_refCountPool);
|
|
||||||
g_refCountPool->freeChunk(oldRefCount);
|
|
||||||
unlockMemoryPoolMutex();
|
|
||||||
}
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator=(const char *str) {
|
String &String::operator=(const char *str) {
|
||||||
uint32 len = strlen(str);
|
assign(str);
|
||||||
ensureCapacity(len, false);
|
|
||||||
_size = len;
|
|
||||||
memmove(_str, str, len + 1);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String &String::operator=(const String &str) {
|
String &String::operator=(const String &str) {
|
||||||
if (&str == this)
|
assign(str);
|
||||||
return *this;
|
|
||||||
|
|
||||||
if (str.isStorageIntern()) {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
_size = str._size;
|
|
||||||
_str = _storage;
|
|
||||||
memcpy(_str, str._str, _size + 1);
|
|
||||||
} else {
|
|
||||||
str.incRefCount();
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
|
|
||||||
_extern._refCount = str._extern._refCount;
|
|
||||||
_extern._capacity = str._extern._capacity;
|
|
||||||
_size = str._size;
|
|
||||||
_str = str._str;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String &String::operator=(char c) {
|
String &String::operator=(char c) {
|
||||||
decRefCount(_extern._refCount);
|
assign(c);
|
||||||
_str = _storage;
|
|
||||||
|
|
||||||
_str[0] = c;
|
|
||||||
_str[1] = 0;
|
|
||||||
|
|
||||||
_size = (c == 0) ? 0 : 1;
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String &String::operator+=(const char *str) {
|
String &String::operator+=(const char *str) {
|
||||||
if (pointerInOwnBuffer(str))
|
assignAppend(str);
|
||||||
return operator+=(String(str));
|
|
||||||
|
|
||||||
int len = strlen(str);
|
|
||||||
if (len > 0) {
|
|
||||||
ensureCapacity(_size + len, true);
|
|
||||||
|
|
||||||
memcpy(_str + _size, str, len + 1);
|
|
||||||
_size += len;
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool String::pointerInOwnBuffer(const char *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;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator+=(const String &str) {
|
String &String::operator+=(const String &str) {
|
||||||
if (&str == this)
|
assignAppend(str);
|
||||||
return operator+=(String(str));
|
|
||||||
|
|
||||||
int len = str._size;
|
|
||||||
if (len > 0) {
|
|
||||||
ensureCapacity(_size + len, true);
|
|
||||||
|
|
||||||
memcpy(_str + _size, str._str, len + 1);
|
|
||||||
_size += len;
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String &String::operator+=(char c) {
|
String &String::operator+=(char c) {
|
||||||
ensureCapacity(_size + 1, true);
|
assignAppend(c);
|
||||||
|
|
||||||
_str[_size++] = c;
|
|
||||||
_str[_size] = 0;
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,45 +176,7 @@ bool String::contains(uint32 x) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 String::asUint64() const {
|
#ifndef SCUMMVM_UTIL
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64 String::asUint64Ext() const {
|
|
||||||
uint64 result = 0;
|
|
||||||
uint64 base = 10;
|
|
||||||
uint32 skip = 0;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::matchString(const char *pat, bool ignoreCase, bool pathMode) const {
|
bool String::matchString(const char *pat, bool ignoreCase, bool pathMode) const {
|
||||||
return Common::matchString(c_str(), pat, ignoreCase, pathMode);
|
return Common::matchString(c_str(), pat, ignoreCase, pathMode);
|
||||||
|
@ -480,146 +186,7 @@ bool String::matchString(const String &pat, bool ignoreCase, bool pathMode) cons
|
||||||
return Common::matchString(c_str(), pat.c_str(), ignoreCase, pathMode);
|
return Common::matchString(c_str(), pat.c_str(), ignoreCase, pathMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::deleteLastChar() {
|
#endif
|
||||||
if (_size > 0)
|
|
||||||
deleteChar(_size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::deleteChar(uint32 p) {
|
|
||||||
assert(p < _size);
|
|
||||||
|
|
||||||
makeUnique();
|
|
||||||
while (p++ < _size)
|
|
||||||
_str[p - 1] = _str[p];
|
|
||||||
_size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::erase(uint32 p, uint32 len) {
|
|
||||||
if (p == npos || len == 0)
|
|
||||||
return;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::iterator String::erase(iterator it) {
|
|
||||||
this->deleteChar(it - _str);
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::clear() {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
|
|
||||||
_size = 0;
|
|
||||||
_str = _storage;
|
|
||||||
_storage[0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::setChar(char c, uint32 p) {
|
|
||||||
assert(p < _size);
|
|
||||||
|
|
||||||
makeUnique();
|
|
||||||
_str[p] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::insertChar(char 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::toLowercase() {
|
|
||||||
makeUnique();
|
|
||||||
for (uint32 i = 0; i < _size; ++i)
|
|
||||||
_str[i] = tolower(_str[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::toUppercase() {
|
|
||||||
makeUnique();
|
|
||||||
for (uint32 i = 0; i < _size; ++i)
|
|
||||||
_str[i] = toupper(_str[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::trim() {
|
|
||||||
if (_size == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
makeUnique();
|
|
||||||
|
|
||||||
// Trim trailing whitespace
|
|
||||||
while (_size >= 1 && isSpace(_str[_size - 1]))
|
|
||||||
--_size;
|
|
||||||
_str[_size] = 0;
|
|
||||||
|
|
||||||
// Trim leading whitespace
|
|
||||||
char *t = _str;
|
|
||||||
while (isSpace(*t))
|
|
||||||
t++;
|
|
||||||
|
|
||||||
if (t != _str) {
|
|
||||||
_size -= t - _str;
|
|
||||||
memmove(_str, t, _size + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint String::hash() const {
|
|
||||||
return hashit(c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::replace(uint32 pos, uint32 count, const String &str) {
|
void String::replace(uint32 pos, uint32 count, const String &str) {
|
||||||
replace(pos, count, str, 0, str._size);
|
replace(pos, count, str, 0, str._size);
|
||||||
|
@ -645,12 +212,13 @@ void String::replace(uint32 posOri, uint32 countOri, const String &str,
|
||||||
void String::replace(uint32 posOri, uint32 countOri, const char *str,
|
void String::replace(uint32 posOri, uint32 countOri, const char *str,
|
||||||
uint32 posDest, uint32 countDest) {
|
uint32 posDest, uint32 countDest) {
|
||||||
|
|
||||||
ensureCapacity(_size + countDest - countOri, true);
|
|
||||||
|
|
||||||
// Prepare string for the replaced text.
|
// Prepare string for the replaced text.
|
||||||
if (countOri < countDest) {
|
if (countOri < countDest) {
|
||||||
uint32 offset = countDest - countOri; ///< Offset to copy the characters
|
uint32 offset = countDest - countOri; ///< Offset to copy the characters
|
||||||
uint32 newSize = _size + offset;
|
uint32 newSize = _size + offset;
|
||||||
|
|
||||||
|
ensureCapacity(newSize, true);
|
||||||
|
|
||||||
_size = newSize;
|
_size = newSize;
|
||||||
|
|
||||||
// Push the old characters to the end of the string
|
// Push the old characters to the end of the string
|
||||||
|
@ -660,11 +228,15 @@ void String::replace(uint32 posOri, uint32 countOri, const char *str,
|
||||||
} else if (countOri > countDest){
|
} else if (countOri > countDest){
|
||||||
uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back
|
uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back
|
||||||
|
|
||||||
|
makeUnique();
|
||||||
|
|
||||||
// Pull the remainder string back
|
// Pull the remainder string back
|
||||||
for (uint32 i = posOri + countDest; i < _size; i++)
|
for (uint32 i = posOri + countDest; i + offset <= _size; i++)
|
||||||
_str[i] = _str[i + offset];
|
_str[i] = _str[i + offset];
|
||||||
|
|
||||||
_size -= offset;
|
_size -= offset;
|
||||||
|
} else {
|
||||||
|
makeUnique();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the replaced part of the string
|
// Copy the replaced part of the string
|
||||||
|
@ -673,31 +245,6 @@ void String::replace(uint32 posOri, uint32 countOri, const char *str,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 String::find(const String &str, uint32 pos) const {
|
|
||||||
if (pos >= _size) {
|
|
||||||
return npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *strP = str.c_str();
|
|
||||||
|
|
||||||
for (const_iterator cur = begin() + pos; *cur; ++cur) {
|
|
||||||
uint i = 0;
|
|
||||||
while (true) {
|
|
||||||
if (!strP[i]) {
|
|
||||||
return cur - begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur[i] != strP[i]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
String String::format(const char *fmt, ...) {
|
String String::format(const char *fmt, ...) {
|
||||||
String output;
|
String output;
|
||||||
|
@ -762,17 +309,6 @@ String String::vformat(const char *fmt, va_list args) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t String::find(char c, size_t pos) const {
|
|
||||||
const char *p = strchr(_str + pos, c);
|
|
||||||
return p ? p - _str : npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t String::find(const char *s) const {
|
|
||||||
const char *str = strstr(_str, s);
|
|
||||||
return str ? str - _str : npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t String::rfind(const char *s) const {
|
size_t String::rfind(const char *s) const {
|
||||||
int sLen = strlen(s);
|
int sLen = strlen(s);
|
||||||
|
|
||||||
|
@ -874,42 +410,6 @@ String String::substr(size_t pos, size_t len) const {
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
bool String::operator==(const String &x) const {
|
|
||||||
return equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator==(const char *x) const {
|
|
||||||
assert(x != nullptr);
|
|
||||||
return equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator!=(const String &x) const {
|
|
||||||
return !equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator !=(const char *x) const {
|
|
||||||
assert(x != nullptr);
|
|
||||||
return !equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator<(const String &x) const {
|
|
||||||
return compareTo(x) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator<=(const String &x) const {
|
|
||||||
return compareTo(x) <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator>(const String &x) const {
|
|
||||||
return compareTo(x) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator>=(const String &x) const {
|
|
||||||
return compareTo(x) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
bool operator==(const char* y, const String &x) {
|
bool operator==(const char* y, const String &x) {
|
||||||
return (x == y);
|
return (x == y);
|
||||||
}
|
}
|
||||||
|
@ -920,15 +420,6 @@ bool operator!=(const char* y, const String &x) {
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
bool String::equals(const String &x) const {
|
|
||||||
return (0 == compareTo(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::equals(const char *x) const {
|
|
||||||
assert(x != nullptr);
|
|
||||||
return (0 == compareTo(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::equalsIgnoreCase(const String &x) const {
|
bool String::equalsIgnoreCase(const String &x) const {
|
||||||
return (0 == compareToIgnoreCase(x));
|
return (0 == compareToIgnoreCase(x));
|
||||||
}
|
}
|
||||||
|
@ -938,15 +429,6 @@ bool String::equalsIgnoreCase(const char *x) const {
|
||||||
return (0 == compareToIgnoreCase(x));
|
return (0 == compareToIgnoreCase(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
int String::compareTo(const String &x) const {
|
|
||||||
return compareTo(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareTo(const char *x) const {
|
|
||||||
assert(x != nullptr);
|
|
||||||
return strcmp(c_str(), x);
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareToIgnoreCase(const String &x) const {
|
int String::compareToIgnoreCase(const String &x) const {
|
||||||
return compareToIgnoreCase(x.c_str());
|
return compareToIgnoreCase(x.c_str());
|
||||||
}
|
}
|
||||||
|
@ -997,6 +479,8 @@ String operator+(const String &x, char y) {
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef SCUMMVM_UTIL
|
||||||
|
|
||||||
char *ltrim(char *t) {
|
char *ltrim(char *t) {
|
||||||
while (isSpace(*t))
|
while (isSpace(*t))
|
||||||
t++;
|
t++;
|
||||||
|
@ -1014,6 +498,8 @@ char *trim(char *t) {
|
||||||
return rtrim(ltrim(t));
|
return rtrim(ltrim(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
String lastPathComponent(const String &path, const char sep) {
|
String lastPathComponent(const String &path, const char sep) {
|
||||||
const char *str = path.c_str();
|
const char *str = path.c_str();
|
||||||
const char *last = str + path.size();
|
const char *last = str + path.size();
|
||||||
|
@ -1089,6 +575,8 @@ String normalizePath(const String &path, const char sep) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef SCUMMVM_UTIL
|
||||||
|
|
||||||
bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMode) {
|
bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMode) {
|
||||||
assert(str);
|
assert(str);
|
||||||
assert(pat);
|
assert(pat);
|
||||||
|
@ -1193,6 +681,8 @@ String tag2string(uint32 tag) {
|
||||||
return String(str);
|
return String(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t strlcpy(char *dst, const char *src, size_t size) {
|
size_t strlcpy(char *dst, const char *src, size_t size) {
|
||||||
// Our backup of the source's start, we need this
|
// Our backup of the source's start, we need this
|
||||||
// to calculate the source's length.
|
// to calculate the source's length.
|
||||||
|
|
186
common/str.h
186
common/str.h
|
@ -26,6 +26,7 @@
|
||||||
#include "common/scummsys.h"
|
#include "common/scummsys.h"
|
||||||
#include "common/str-enc.h"
|
#include "common/str-enc.h"
|
||||||
#include "common/ustr.h"
|
#include "common/ustr.h"
|
||||||
|
#include "common/base-str.h"
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
@ -56,84 +57,29 @@ class U32String;
|
||||||
* The presence of \0 characters in the string will cause undefined
|
* The presence of \0 characters in the string will cause undefined
|
||||||
* behavior in some operations.
|
* behavior in some operations.
|
||||||
*/
|
*/
|
||||||
class String {
|
class String : public BaseString<char> {
|
||||||
public:
|
public:
|
||||||
static const uint32 npos = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
static void releaseMemoryPoolMutex();
|
|
||||||
|
|
||||||
typedef char value_type;
|
|
||||||
/**
|
/**
|
||||||
* Unsigned version of the underlying type. This can be used to cast
|
* Unsigned version of the underlying type. This can be used to cast
|
||||||
* individual string characters to bigger integer types without sign
|
* individual string characters to bigger integer types without sign
|
||||||
* extension happening.
|
* extension happening.
|
||||||
*/
|
*/
|
||||||
typedef unsigned char unsigned_type;
|
typedef unsigned char unsigned_type;
|
||||||
typedef char * iterator;
|
|
||||||
typedef const char * const_iterator;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* The size of the internal storage. Increasing this means less heap
|
|
||||||
* allocations are needed, at the cost of more stack memory usage,
|
|
||||||
* and of course lots of wasted memory. Empirically, 90% or more of
|
|
||||||
* all String instances are less than 32 chars long. If a platform
|
|
||||||
* is very short on stack space, it would be possible to lower this.
|
|
||||||
* A value of 24 still seems acceptable, though considerably worse,
|
|
||||||
* while 16 seems to be the lowest you want to go... Anything lower
|
|
||||||
* than 8 makes no sense, since that's the size of member _extern
|
|
||||||
* (on 32 bit machines; 12 bytes on systems with 64bit pointers).
|
|
||||||
*/
|
|
||||||
static const uint32 _builtinCapacity = 32 - sizeof(uint32) - sizeof(char *);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Length of the string. Stored to avoid having to call strlen
|
|
||||||
* a lot. Yes, we limit ourselves to strings shorter than 4GB --
|
|
||||||
* on purpose :-).
|
|
||||||
*/
|
|
||||||
uint32 _size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pointer to the actual string storage. Either points to _storage,
|
|
||||||
* or to a block allocated on the heap via malloc.
|
|
||||||
*/
|
|
||||||
char *_str;
|
|
||||||
|
|
||||||
|
|
||||||
union {
|
|
||||||
/**
|
|
||||||
* Internal string storage.
|
|
||||||
*/
|
|
||||||
char _storage[_builtinCapacity];
|
|
||||||
/**
|
|
||||||
* External string storage data -- the refcounter, and the
|
|
||||||
* capacity of the string _str points to.
|
|
||||||
*/
|
|
||||||
struct {
|
|
||||||
mutable int *_refCount;
|
|
||||||
uint32 _capacity;
|
|
||||||
} _extern;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool isStorageIntern() const {
|
|
||||||
return _str == _storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/** Construct a new empty string. */
|
/** Construct a new empty string. */
|
||||||
String() : _size(0), _str(_storage) { _storage[0] = 0; }
|
String() : BaseString() {}
|
||||||
|
|
||||||
/** Construct a new string from the given NULL-terminated C string. */
|
/** Construct a new string from the given NULL-terminated C string. */
|
||||||
String(const char *str);
|
String(const char *str) : BaseString(str) {}
|
||||||
|
|
||||||
/** Construct a new string containing exactly len characters read from address str. */
|
/** Construct a new string containing exactly len characters read from address str. */
|
||||||
String(const char *str, uint32 len);
|
String(const char *str, uint32 len) : BaseString(str, len) {}
|
||||||
|
|
||||||
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
|
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
|
||||||
String(const char *beginP, const char *endP);
|
String(const char *beginP, const char *endP) : BaseString(beginP, endP) {}
|
||||||
|
|
||||||
/** Construct a copy of the given string. */
|
/** Construct a copy of the given string. */
|
||||||
String(const String &str);
|
String(const String &str) : BaseString(str) {};
|
||||||
|
|
||||||
/** Construct a string consisting of the given character. */
|
/** Construct a string consisting of the given character. */
|
||||||
explicit String(char c);
|
explicit String(char c);
|
||||||
|
@ -150,24 +96,10 @@ public:
|
||||||
String &operator+=(const String &str);
|
String &operator+=(const String &str);
|
||||||
String &operator+=(char c);
|
String &operator+=(char c);
|
||||||
|
|
||||||
bool operator==(const String &x) const;
|
|
||||||
bool operator==(const char *x) const;
|
|
||||||
bool operator!=(const String &x) const;
|
|
||||||
bool operator!=(const char *x) const;
|
|
||||||
|
|
||||||
bool operator<(const String &x) const;
|
|
||||||
bool operator<=(const String &x) const;
|
|
||||||
bool operator>(const String &x) const;
|
|
||||||
bool operator>=(const String &x) const;
|
|
||||||
|
|
||||||
bool equals(const String &x) const;
|
|
||||||
bool equalsIgnoreCase(const String &x) const;
|
bool equalsIgnoreCase(const String &x) const;
|
||||||
int compareTo(const String &x) const; // strcmp clone
|
|
||||||
int compareToIgnoreCase(const String &x) const; // stricmp clone
|
int compareToIgnoreCase(const String &x) const; // stricmp clone
|
||||||
|
|
||||||
bool equals(const char *x) const;
|
|
||||||
bool equalsIgnoreCase(const char *x) const;
|
bool equalsIgnoreCase(const char *x) const;
|
||||||
int compareTo(const char *x) const; // strcmp clone
|
|
||||||
int compareToIgnoreCase(const char *x) const; // stricmp clone
|
int compareToIgnoreCase(const char *x) const; // stricmp clone
|
||||||
int compareDictionary(const String &x) const;
|
int compareDictionary(const String &x) const;
|
||||||
int compareDictionary(const char *x) const;
|
int compareDictionary(const char *x) const;
|
||||||
|
@ -187,12 +119,6 @@ public:
|
||||||
bool contains(char x) const;
|
bool contains(char x) const;
|
||||||
bool contains(uint32 x) const;
|
bool contains(uint32 x) const;
|
||||||
|
|
||||||
/** Return uint64 corrensponding to String's contents. */
|
|
||||||
uint64 asUint64() const;
|
|
||||||
|
|
||||||
/** Return uint64 corrensponding to String's contents. This variant recognizes 0 (oct) and 0x (hex) prefixes. */
|
|
||||||
uint64 asUint64Ext() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple DOS-style pattern matching function (understands * and ? like used in DOS).
|
* Simple DOS-style pattern matching function (understands * and ? like used in DOS).
|
||||||
* Taken from exult/files/listfiles.cc
|
* Taken from exult/files/listfiles.cc
|
||||||
|
@ -221,65 +147,6 @@ public:
|
||||||
bool matchString(const char *pat, bool ignoreCase = false, bool pathMode = false) const;
|
bool matchString(const char *pat, bool ignoreCase = false, bool pathMode = false) const;
|
||||||
bool matchString(const String &pat, bool ignoreCase = false, bool pathMode = false) const;
|
bool matchString(const String &pat, bool ignoreCase = false, bool pathMode = false) const;
|
||||||
|
|
||||||
|
|
||||||
inline const char *c_str() const { return _str; }
|
|
||||||
inline uint size() const { return _size; }
|
|
||||||
|
|
||||||
inline bool empty() const { return (_size == 0); }
|
|
||||||
char firstChar() const { return (_size > 0) ? _str[0] : 0; }
|
|
||||||
char lastChar() const { return (_size > 0) ? _str[_size - 1] : 0; }
|
|
||||||
|
|
||||||
char operator[](int idx) const {
|
|
||||||
assert(_str && idx >= 0 && idx < (int)_size);
|
|
||||||
return _str[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Remove the last character from the string. */
|
|
||||||
void deleteLastChar();
|
|
||||||
|
|
||||||
/** Remove the character at position p from the string. */
|
|
||||||
void deleteChar(uint32 p);
|
|
||||||
|
|
||||||
/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
|
|
||||||
void erase(uint32 p, uint32 len = npos);
|
|
||||||
|
|
||||||
/** Erases the character at the given iterator location */
|
|
||||||
iterator erase(iterator it);
|
|
||||||
|
|
||||||
/** Set character c at position p, replacing the previous character there. */
|
|
||||||
void setChar(char c, uint32 p);
|
|
||||||
|
|
||||||
/** Insert character c before position p. */
|
|
||||||
void insertChar(char c, uint32 p);
|
|
||||||
|
|
||||||
/** Clears the string, making it empty. */
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/** Convert all characters in the string to lowercase. */
|
|
||||||
void toLowercase();
|
|
||||||
|
|
||||||
/** Convert all characters in the string to uppercase. */
|
|
||||||
void toUppercase();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes trailing and leading whitespaces. Uses isspace() to decide
|
|
||||||
* what is whitespace and what not.
|
|
||||||
*/
|
|
||||||
void trim();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps the text in the string to the given line maximum. Lines will be
|
|
||||||
* broken at any whitespace character. New lines are assumed to be
|
|
||||||
* represented using '\n'.
|
|
||||||
*
|
|
||||||
* This is a very basic line wrap which does not perform tab stop
|
|
||||||
* calculation, consecutive whitespace collapsing, auto-hyphenation, or line
|
|
||||||
* balancing.
|
|
||||||
*/
|
|
||||||
void wordWrap(const uint32 maxLength);
|
|
||||||
|
|
||||||
uint hash() const;
|
|
||||||
|
|
||||||
/**@{
|
/**@{
|
||||||
* Functions to replace some amount of chars with chars from some other string.
|
* Functions to replace some amount of chars with chars from some other string.
|
||||||
*
|
*
|
||||||
|
@ -326,13 +193,6 @@ public:
|
||||||
*/
|
*/
|
||||||
static String vformat(const char *fmt, va_list args);
|
static String vformat(const char *fmt, va_list args);
|
||||||
|
|
||||||
/** Finds the index of a character in the string */
|
|
||||||
size_t find(char c, size_t pos = 0) const;
|
|
||||||
|
|
||||||
/** Does a find for the passed string */
|
|
||||||
size_t find(const char *s) const;
|
|
||||||
uint32 find(const String &str, uint32 pos = 0) const;
|
|
||||||
|
|
||||||
/** Does a reverse find for the passed string */
|
/** Does a reverse find for the passed string */
|
||||||
size_t rfind(const char *s) const;
|
size_t rfind(const char *s) const;
|
||||||
size_t rfind(const String &s) const {
|
size_t rfind(const String &s) const {
|
||||||
|
@ -381,42 +241,10 @@ public:
|
||||||
/** Return a substring of this string */
|
/** Return a substring of this string */
|
||||||
String substr(size_t pos = 0, size_t len = npos) const;
|
String substr(size_t pos = 0, size_t len = npos) const;
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
iterator begin() {
|
|
||||||
// Since the user could potentially
|
|
||||||
// change the string via the returned
|
|
||||||
// iterator we have to assure we are
|
|
||||||
// pointing to a unique storage.
|
|
||||||
makeUnique();
|
|
||||||
|
|
||||||
return _str;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator end() {
|
|
||||||
return begin() + size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator begin() const {
|
|
||||||
return _str;
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator end() const {
|
|
||||||
return begin() + size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Python-like method **/
|
/** Python-like method **/
|
||||||
U32String decode(CodePage page = kUtf8) const;
|
U32String decode(CodePage page = kUtf8) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void makeUnique();
|
|
||||||
void ensureCapacity(uint32 new_size, bool keep_old);
|
|
||||||
void incRefCount() const;
|
|
||||||
void decRefCount(int *oldRefCount);
|
|
||||||
void initWithCStr(const char *str, uint32 len);
|
|
||||||
|
|
||||||
bool pointerInOwnBuffer(const char *str) const;
|
|
||||||
|
|
||||||
void decodeUTF8(U32String &dst) const;
|
void decodeUTF8(U32String &dst) const;
|
||||||
void decodeOneByte(U32String &dst, CodePage page) const;
|
void decodeOneByte(U32String &dst, CodePage page) const;
|
||||||
};
|
};
|
||||||
|
|
544
common/ustr.cpp
544
common/ustr.cpp
|
@ -28,53 +28,7 @@
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
extern MemoryPool *g_refCountPool;
|
U32String::U32String(const char *str) : BaseString() {
|
||||||
|
|
||||||
static uint32 computeCapacity(uint32 len) {
|
|
||||||
// By default, for the capacity we use the next multiple of 32
|
|
||||||
return ((len + 32 - 1) & ~0x1F);
|
|
||||||
}
|
|
||||||
|
|
||||||
U32String::U32String(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;
|
|
||||||
}
|
|
||||||
initWithCStr(str, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
U32String::U32String(const value_type *str, uint32 len) : _size(0), _str(_storage) {
|
|
||||||
initWithCStr(str, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
U32String::U32String(const value_type *beginP, const value_type *endP) : _size(0), _str(_storage) {
|
|
||||||
assert(endP >= beginP);
|
|
||||||
initWithCStr(beginP, endP - beginP);
|
|
||||||
}
|
|
||||||
|
|
||||||
U32String::U32String(const U32String &str)
|
|
||||||
: _size(str._size) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
U32String::U32String(const char *str) : _size(0), _str(_storage) {
|
|
||||||
if (str == nullptr) {
|
if (str == nullptr) {
|
||||||
_storage[0] = 0;
|
_storage[0] = 0;
|
||||||
_size = 0;
|
_size = 0;
|
||||||
|
@ -83,21 +37,21 @@ U32String::U32String(const char *str) : _size(0), _str(_storage) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
U32String::U32String(const char *str, uint32 len) : _size(0), _str(_storage) {
|
U32String::U32String(const char *str, uint32 len) : BaseString() {
|
||||||
initWithCStr(str, len);
|
initWithCStr(str, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
U32String::U32String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
|
U32String::U32String(const char *beginP, const char *endP) : BaseString() {
|
||||||
assert(endP >= beginP);
|
assert(endP >= beginP);
|
||||||
initWithCStr(beginP, endP - beginP);
|
initWithCStr(beginP, endP - beginP);
|
||||||
}
|
}
|
||||||
|
|
||||||
U32String::U32String(const String &str) : _size(0), _str(_storage) {
|
U32String::U32String(const String &str) : BaseString() {
|
||||||
initWithCStr(str.c_str(), str.size());
|
initWithCStr(str.c_str(), str.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
U32String::U32String(const UnicodeBiDiText &txt) : _size(0), _str(_storage) {
|
U32String::U32String(const UnicodeBiDiText &txt) : BaseString() {
|
||||||
initWithCStr(txt.visual.c_str(), txt.visual.size());
|
initWithValueTypeStr(txt.visual.c_str(), txt.visual.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
U32String::~U32String() {
|
U32String::~U32String() {
|
||||||
|
@ -105,24 +59,7 @@ U32String::~U32String() {
|
||||||
}
|
}
|
||||||
|
|
||||||
U32String &U32String::operator=(const U32String &str) {
|
U32String &U32String::operator=(const U32String &str) {
|
||||||
if (&str == this)
|
assign(str);
|
||||||
return *this;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,465 +103,43 @@ U32String &U32String::operator+=(value_type c) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool U32String::operator==(const U32String &x) const {
|
|
||||||
return equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::operator==(const String &x) const {
|
bool U32String::operator==(const String &x) const {
|
||||||
return equals(x);
|
return equalsC(x.c_str());
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::operator==(const value_type *x) const {
|
|
||||||
return equals(U32String(x));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool U32String::operator==(const char *x) const {
|
bool U32String::operator==(const char *x) const {
|
||||||
return equals(x);
|
return equalsC(x);
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::operator!=(const U32String &x) const {
|
|
||||||
return !equals(x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool U32String::operator!=(const String &x) const {
|
bool U32String::operator!=(const String &x) const {
|
||||||
return !equals(x);
|
return !equalsC(x.c_str());
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::operator!=(const value_type *x) const {
|
|
||||||
return !equals(U32String(x));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool U32String::operator!=(const char *x) const {
|
bool U32String::operator!=(const char *x) const {
|
||||||
return !equals(x);
|
return !equalsC(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool U32String::operator<(const U32String &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 true;
|
|
||||||
else if (sc > xc)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (_size < x.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::operator<=(const U32String &x) const {
|
|
||||||
return !operator>(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::operator>(const U32String &x) const {
|
|
||||||
for (uint i = 0, n = x.size(); i < _size && i < n; ++i) {
|
|
||||||
uint32 sc = _str[i];
|
|
||||||
uint32 xc = x[i];
|
|
||||||
if (sc > xc)
|
|
||||||
return true;
|
|
||||||
else if (sc < xc)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (_size > x.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::operator>=(const U32String &x) const {
|
|
||||||
return !operator<(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::equals(const U32String &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));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::equals(const String &x) const {
|
|
||||||
if (x.size() != _size)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (uint32 idx = 0; idx < _size; ++idx)
|
|
||||||
if (_str[idx] != static_cast<value_type>(x[idx]))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::contains(value_type x) const {
|
|
||||||
for (uint32 i = 0; i < _size; ++i) {
|
|
||||||
if (_str[i] == x) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool U32String::contains(const U32String &otherString) const {
|
|
||||||
if (empty() || otherString.empty() || _size < otherString.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 size = 0;
|
|
||||||
U32String::const_iterator itr = otherString.begin();
|
|
||||||
|
|
||||||
for (U32String::const_iterator itr2 = begin(); itr != otherString.end() && itr2 != end(); itr2++) {
|
|
||||||
if (*itr == *itr2) {
|
|
||||||
itr++;
|
|
||||||
size++;
|
|
||||||
if (size == otherString.size())
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
size = 0;
|
|
||||||
itr = otherString.begin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::insertString(String s, uint32 p) {
|
|
||||||
for (String::iterator i = s.begin(); i != s.end(); i++) {
|
|
||||||
U32String::insertChar(*i, p++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::insertString(value_type *s, uint32 p) {
|
|
||||||
while (*s != '\0') {
|
|
||||||
U32String::insertChar(*s++, p++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::deleteChar(uint32 p) {
|
|
||||||
assert(p < _size);
|
|
||||||
|
|
||||||
makeUnique();
|
|
||||||
while (p++ < _size)
|
|
||||||
_str[p - 1] = _str[p];
|
|
||||||
_size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::deleteLastChar() {
|
|
||||||
if (_size > 0)
|
|
||||||
deleteChar(_size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::erase(uint32 p, uint32 len) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::clear() {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
|
|
||||||
_size = 0;
|
|
||||||
_str = _storage;
|
|
||||||
_storage[0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::toLowercase() {
|
|
||||||
makeUnique();
|
|
||||||
for (uint32 i = 0; i < _size; ++i) {
|
|
||||||
if (_str[i] < 128) {
|
|
||||||
_str[i] = tolower(_str[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::toUppercase() {
|
|
||||||
makeUnique();
|
|
||||||
for (uint32 i = 0; i < _size; ++i) {
|
|
||||||
if (_str[i] < 128) {
|
|
||||||
_str[i] = toupper(_str[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 U32String::find(value_type x, uint32 pos) const {
|
|
||||||
for (uint32 i = pos; i < _size; ++i) {
|
|
||||||
if (_str[i] == x) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 U32String::find(const U32String &str, uint32 pos) const {
|
|
||||||
if (pos >= _size) {
|
|
||||||
return npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const value_type *strP = str.c_str();
|
|
||||||
|
|
||||||
for (const_iterator cur = begin() + pos; *cur; ++cur) {
|
|
||||||
uint i = 0;
|
|
||||||
while (true) {
|
|
||||||
if (!strP[i]) {
|
|
||||||
return cur - begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur[i] != strP[i]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::makeUnique() {
|
|
||||||
ensureCapacity(_size, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::incRefCount() const {
|
|
||||||
assert(!isStorageIntern());
|
|
||||||
if (_extern._refCount == nullptr) {
|
|
||||||
if (g_refCountPool == nullptr) {
|
|
||||||
g_refCountPool = new MemoryPool(sizeof(int));
|
|
||||||
assert(g_refCountPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
_extern._refCount = (int *)g_refCountPool->allocChunk();
|
|
||||||
*_extern._refCount = 2;
|
|
||||||
} else {
|
|
||||||
++(*_extern._refCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::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) {
|
|
||||||
assert(g_refCountPool);
|
|
||||||
g_refCountPool->freeChunk(oldRefCount);
|
|
||||||
}
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::initWithCStr(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::initWithCStr(const char *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
|
|
||||||
for (size_t idx = 0; idx < len; ++idx, ++str)
|
|
||||||
_str[idx] = (byte)(*str);
|
|
||||||
|
|
||||||
_str[len] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
U32String operator+(const U32String &x, const U32String &y) {
|
U32String operator+(const U32String &x, const U32String &y) {
|
||||||
U32String temp(x);
|
U32String temp(x);
|
||||||
temp += y;
|
temp += y;
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void U32String::wordWrap(const uint32 maxLength) {
|
U32String operator+(const U32String &x, const uint32 y) {
|
||||||
if (_size < maxLength) {
|
U32String temp(x);
|
||||||
return;
|
temp += y;
|
||||||
}
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
makeUnique();
|
void U32String::insertString(const char *s, uint32 p) {
|
||||||
|
while (*s != '\0') {
|
||||||
const uint32 kNoSpace = 0xFFFFFFFF;
|
BaseString::insertChar(*s++, p++);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 U32String::asUint64() const {
|
void U32String::insertString(const String &s, uint32 p) {
|
||||||
uint64 result = 0;
|
for (uint32 i = 0; i < s.size(); ++i) {
|
||||||
for (uint32 i = 0; i < _size; ++i) {
|
BaseString::insertChar(s[i], p++);
|
||||||
if (_str[i] < '0' || _str[i] > '9') break;
|
|
||||||
result = result * 10L + (_str[i] - '0');
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void U32String::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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,7 +148,7 @@ U32String U32String::format(U32String fmt, ...) {
|
||||||
|
|
||||||
va_list va;
|
va_list va;
|
||||||
va_start(va, fmt);
|
va_start(va, fmt);
|
||||||
U32String::vformat(fmt.begin(), fmt.end(), output, va);
|
U32String::vformat(fmt.c_str(), fmt.c_str() + fmt.size(), output, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
@ -645,13 +160,14 @@ U32String U32String::format(const char *fmt, ...) {
|
||||||
Common::U32String fmtU32(fmt);
|
Common::U32String fmtU32(fmt);
|
||||||
va_list va;
|
va_list va;
|
||||||
va_start(va, fmt);
|
va_start(va, fmt);
|
||||||
U32String::vformat(fmtU32.begin(), fmtU32.end(), output, va);
|
U32String::vformat(fmtU32.c_str(), fmtU32.c_str() + fmtU32.size(),
|
||||||
|
output, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
int U32String::vformat(U32String::const_iterator fmt, const U32String::const_iterator inputItrEnd, U32String &output, va_list args) {
|
int U32String::vformat(const value_type *fmt, const value_type *fmtEnd, U32String &output, va_list args) {
|
||||||
int int_temp;
|
int int_temp;
|
||||||
char *string_temp;
|
char *string_temp;
|
||||||
|
|
||||||
|
@ -664,7 +180,7 @@ int U32String::vformat(U32String::const_iterator fmt, const U32String::const_ite
|
||||||
|
|
||||||
char buffer[512];
|
char buffer[512];
|
||||||
|
|
||||||
while (fmt != inputItrEnd) {
|
while (fmt != fmtEnd) {
|
||||||
ch = *fmt++;
|
ch = *fmt++;
|
||||||
if (ch == '%') {
|
if (ch == '%') {
|
||||||
switch (ch = *fmt++) {
|
switch (ch = *fmt++) {
|
||||||
|
@ -680,10 +196,10 @@ int U32String::vformat(U32String::const_iterator fmt, const U32String::const_ite
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
string_temp = va_arg(args, char *);
|
string_temp = va_arg(args, char *);
|
||||||
len = strlen(string_temp);
|
tempPos = output.size();
|
||||||
length += len;
|
|
||||||
|
|
||||||
output.insertString(string_temp, pos);
|
output.insertString(string_temp, pos);
|
||||||
|
len = output.size() - tempPos;
|
||||||
|
length += len;
|
||||||
pos += len - 1;
|
pos += len - 1;
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
|
|
201
common/ustr.h
201
common/ustr.h
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "common/scummsys.h"
|
#include "common/scummsys.h"
|
||||||
#include "common/str-enc.h"
|
#include "common/str-enc.h"
|
||||||
|
#include "common/base-str.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
@ -52,66 +53,36 @@ class UnicodeBiDiText;
|
||||||
* The presence of \0 characters in the string will cause undefined
|
* The presence of \0 characters in the string will cause undefined
|
||||||
* behavior in some operations.
|
* behavior in some operations.
|
||||||
*/
|
*/
|
||||||
class U32String {
|
#ifdef USE_CXX11
|
||||||
|
typedef char32_t u32char_type_t;
|
||||||
|
#else
|
||||||
|
typedef uint32 u32char_type_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class U32String : public BaseString<u32char_type_t> {
|
||||||
public:
|
public:
|
||||||
static const uint32 npos = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
typedef uint32 value_type;
|
|
||||||
typedef uint32 unsigned_type;
|
typedef uint32 unsigned_type;
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* The size of the internal storage. Increasing this means less heap
|
|
||||||
* allocations are needed, at the cost of more stack memory usage,
|
|
||||||
* and of course lots of wasted memory.
|
|
||||||
*/
|
|
||||||
static const uint32 _builtinCapacity = 32;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Length of the string.
|
|
||||||
*/
|
|
||||||
uint32 _size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pointer to the actual string storage. Either points to _storage,
|
|
||||||
* or to a block allocated on the heap via malloc.
|
|
||||||
*/
|
|
||||||
value_type *_str;
|
|
||||||
|
|
||||||
|
|
||||||
union {
|
|
||||||
/**
|
|
||||||
* Internal string storage.
|
|
||||||
*/
|
|
||||||
value_type _storage[_builtinCapacity];
|
|
||||||
/**
|
|
||||||
* External string storage data -- the refcounter, and the
|
|
||||||
* capacity of the string _str points to.
|
|
||||||
*/
|
|
||||||
struct {
|
|
||||||
mutable int *_refCount;
|
|
||||||
uint32 _capacity;
|
|
||||||
} _extern;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool isStorageIntern() const {
|
|
||||||
return _str == _storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Construct a new empty string. */
|
/** Construct a new empty string. */
|
||||||
U32String() : _size(0), _str(_storage) { _storage[0] = 0; }
|
U32String() : BaseString() {}
|
||||||
|
|
||||||
/** Construct a new string from the given NULL-terminated C string. */
|
/** Construct a new string from the given NULL-terminated C string. */
|
||||||
explicit U32String(const value_type *str);
|
explicit U32String(const value_type *str) : BaseString(str) {}
|
||||||
|
|
||||||
/** Construct a new string containing exactly len characters read from address str. */
|
/** Construct a new string containing exactly len characters read from address str. */
|
||||||
U32String(const value_type *str, uint32 len);
|
U32String(const value_type *str, uint32 len) : BaseString(str, len) {}
|
||||||
|
|
||||||
|
#ifdef USE_CXX11
|
||||||
|
explicit U32String(const uint32 *str) : BaseString((const value_type *) str) {}
|
||||||
|
U32String(const uint32 *str, uint32 len) : BaseString((const value_type *) str, len) {}
|
||||||
|
U32String(const uint32 *beginP, const uint32 *endP) : BaseString((const value_type *) beginP, (const value_type *) endP) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
|
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
|
||||||
U32String(const value_type *beginP, const value_type *endP);
|
U32String(const value_type *beginP, const value_type *endP) : BaseString(beginP, endP) {}
|
||||||
|
|
||||||
/** Construct a copy of the given string. */
|
/** Construct a copy of the given string. */
|
||||||
U32String(const U32String &str);
|
U32String(const U32String &str) : BaseString(str) {}
|
||||||
|
|
||||||
/** Construct a copy of the given unicode BiDi converted string. */
|
/** Construct a copy of the given unicode BiDi converted string. */
|
||||||
U32String(const UnicodeBiDiText &txt);
|
U32String(const UnicodeBiDiText &txt);
|
||||||
|
@ -136,128 +107,16 @@ public:
|
||||||
U32String &operator=(const char *str);
|
U32String &operator=(const char *str);
|
||||||
U32String &operator+=(const U32String &str);
|
U32String &operator+=(const U32String &str);
|
||||||
U32String &operator+=(value_type c);
|
U32String &operator+=(value_type c);
|
||||||
bool operator==(const U32String &x) const;
|
using BaseString<value_type>::operator==;
|
||||||
|
using BaseString<value_type>::operator!=;
|
||||||
bool operator==(const String &x) const;
|
bool operator==(const String &x) const;
|
||||||
bool operator==(const value_type *x) const;
|
|
||||||
bool operator==(const char *x) const;
|
bool operator==(const char *x) const;
|
||||||
bool operator!=(const U32String &x) const;
|
|
||||||
bool operator!=(const String &x) const;
|
bool operator!=(const String &x) const;
|
||||||
bool operator!=(const value_type *x) const;
|
|
||||||
bool operator!=(const char *x) const;
|
bool operator!=(const char *x) const;
|
||||||
|
|
||||||
bool operator<(const U32String &x) const;
|
|
||||||
bool operator<=(const U32String &x) const;
|
|
||||||
bool operator>(const U32String &x) const;
|
|
||||||
bool operator>=(const U32String &x) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares whether two U32String are the same based on memory comparison.
|
|
||||||
* This does *not* do comparison based on canonical equivalence.
|
|
||||||
*/
|
|
||||||
bool equals(const U32String &x) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares whether two U32String are the same based on memory comparison.
|
|
||||||
* This does *not* do comparison based on canonical equivalence.
|
|
||||||
*/
|
|
||||||
bool equals(const String &x) const;
|
|
||||||
|
|
||||||
bool contains(value_type x) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a given string is present in the internal string or not.
|
|
||||||
*/
|
|
||||||
bool contains(const U32String &otherString) const;
|
|
||||||
|
|
||||||
inline const value_type *c_str() const { return _str; }
|
|
||||||
inline uint32 size() const { return _size; }
|
|
||||||
|
|
||||||
inline bool empty() const { return (_size == 0); }
|
|
||||||
|
|
||||||
value_type operator[](int idx) const {
|
|
||||||
assert(_str && idx >= 0 && idx < (int)_size);
|
|
||||||
return _str[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set character c at position p, replacing the previous character there. */
|
|
||||||
void setChar(value_type c, uint32 p) {
|
|
||||||
_str[p] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Insert character c before position p. */
|
|
||||||
void insertChar(value_type c, uint32 p);
|
|
||||||
void insertString(String s, uint32 p);
|
|
||||||
void insertString(value_type *s, uint32 p);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the value at position p from the string.
|
|
||||||
* Using this on decomposed characters will not remove the whole
|
|
||||||
* character!
|
|
||||||
*/
|
|
||||||
void deleteChar(uint32 p);
|
|
||||||
|
|
||||||
/** Remove the last character from the string. */
|
|
||||||
void deleteLastChar();
|
|
||||||
|
|
||||||
/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
|
|
||||||
void erase(uint32 p, uint32 len = npos);
|
|
||||||
|
|
||||||
/** Clears the string, making it empty. */
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert all characters in the string to lowercase.
|
|
||||||
*
|
|
||||||
* Be aware that this only affects the case of ASCII characters. All
|
|
||||||
* other characters will not be touched at all.
|
|
||||||
*/
|
|
||||||
void toLowercase();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert all characters in the string to uppercase.
|
|
||||||
*
|
|
||||||
* Be aware that this only affects the case of ASCII characters. All
|
|
||||||
* other characters will not be touched at all.
|
|
||||||
*/
|
|
||||||
void toUppercase();
|
|
||||||
|
|
||||||
uint32 find(value_type x, uint32 pos = 0) const;
|
|
||||||
uint32 find(const U32String &str, uint32 pos = 0) const;
|
|
||||||
|
|
||||||
typedef value_type * iterator;
|
|
||||||
typedef const value_type * const_iterator;
|
|
||||||
|
|
||||||
iterator begin() {
|
|
||||||
// Since the user could potentially
|
|
||||||
// change the string via the returned
|
|
||||||
// iterator we have to assure we are
|
|
||||||
// pointing to a unique storage.
|
|
||||||
makeUnique();
|
|
||||||
|
|
||||||
return _str;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator end() {
|
|
||||||
return begin() + size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator begin() const {
|
|
||||||
return _str;
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator end() const {
|
|
||||||
return begin() + size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Python-like method **/
|
/** Python-like method **/
|
||||||
String encode(CodePage page = kUtf8) const;
|
String encode(CodePage page = kUtf8) const;
|
||||||
|
|
||||||
void wordWrap(const uint32 maxLength);
|
|
||||||
|
|
||||||
uint64 asUint64() const;
|
|
||||||
|
|
||||||
void trim();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print formatted data into a U32String object.
|
* Print formatted data into a U32String object.
|
||||||
*/
|
*/
|
||||||
|
@ -268,7 +127,7 @@ public:
|
||||||
* Print formatted data into a U32String object. It takes in the
|
* Print formatted data into a U32String object. It takes in the
|
||||||
* output by reference and works with iterators.
|
* output by reference and works with iterators.
|
||||||
*/
|
*/
|
||||||
static int vformat(U32String::const_iterator fmt, const U32String::const_iterator inputItrEnd, U32String &output, va_list args);
|
static int vformat(const value_type *fmt, const value_type *fmtEnd, U32String &output, va_list args);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for vformat, convert an int to string
|
* Helper function for vformat, convert an int to string
|
||||||
|
@ -276,19 +135,21 @@ public:
|
||||||
*/
|
*/
|
||||||
static char* itoa(int num, char* str, int base);
|
static char* itoa(int num, char* str, int base);
|
||||||
|
|
||||||
private:
|
using BaseString<value_type>::insertString;
|
||||||
void makeUnique();
|
void insertString(const char *s, uint32 p);
|
||||||
void ensureCapacity(uint32 new_size, bool keep_old);
|
void insertString(const String &s, uint32 p);
|
||||||
void incRefCount() const;
|
|
||||||
void decRefCount(int *oldRefCount);
|
|
||||||
void initWithCStr(const value_type *str, uint32 len);
|
|
||||||
void initWithCStr(const char *str, uint32 len);
|
|
||||||
|
|
||||||
|
const uint32 *u32_str() const {
|
||||||
|
return (const uint32 *) _str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
void encodeUTF8(String &dst) const;
|
void encodeUTF8(String &dst) const;
|
||||||
void encodeOneByte(String &dst, CodePage page) const;
|
void encodeOneByte(String &dst, CodePage page) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
U32String operator+(const U32String &x, const U32String &y);
|
U32String operator+(const U32String &x, const U32String &y);
|
||||||
|
U32String operator+(const U32String &x, U32String::value_type y);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WinResourceID_Hash {
|
struct WinResourceID_Hash {
|
||||||
uint operator()(const WinResourceID &id) const { return hashit(id.toString()); }
|
uint operator()(const WinResourceID &id) const { return id.toString().hash(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WinResourceID_EqualTo {
|
struct WinResourceID_EqualTo {
|
||||||
|
|
2
devtools/create_titanic/base-str.cpp
Normal file
2
devtools/create_titanic/base-str.cpp
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#define SCUMMVM_UTIL 1
|
||||||
|
#include "../common/base-str.cpp"
|
|
@ -24,7 +24,7 @@
|
||||||
#define COMMON_HASH_STR_H
|
#define COMMON_HASH_STR_H
|
||||||
|
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "str.h"
|
#include "common/str.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ MODULE_OBJS := \
|
||||||
script_ranges.o \
|
script_ranges.o \
|
||||||
script_responses.o \
|
script_responses.o \
|
||||||
script_states.o \
|
script_states.o \
|
||||||
|
base-str.o \
|
||||||
str.o \
|
str.o \
|
||||||
tag_maps.o \
|
tag_maps.o \
|
||||||
winexe.o \
|
winexe.o \
|
||||||
|
|
|
@ -1,786 +1,2 @@
|
||||||
/* ScummVM - Graphic Adventure Engine
|
#define SCUMMVM_UTIL
|
||||||
*
|
#include "../common/str.cpp"
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "common/hash-str.h"
|
|
||||||
#include "common/list.h"
|
|
||||||
#include "memorypool.h"
|
|
||||||
#include "common/str.h"
|
|
||||||
#include "common/util.h"
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
|
|
||||||
|
|
||||||
static uint32 computeCapacity(uint32 len) {
|
|
||||||
// By default, for the capacity we use the next multiple of 32
|
|
||||||
return ((len + 32 - 1) & ~0x1F);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *str) : _size(0), _str(_storage) {
|
|
||||||
if (str == 0) {
|
|
||||||
_storage[0] = 0;
|
|
||||||
_size = 0;
|
|
||||||
} else
|
|
||||||
initWithCStr(str, strlen(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
|
|
||||||
initWithCStr(str, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *beginP, const char *endP) : _size(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;
|
|
||||||
|
|
||||||
_size = len;
|
|
||||||
|
|
||||||
if (len >= _builtinCapacity) {
|
|
||||||
// Not enough internal storage, so allocate more
|
|
||||||
_extern._capacity = computeCapacity(len+1);
|
|
||||||
_extern._refCount = 0;
|
|
||||||
_str = new char[_extern._capacity];
|
|
||||||
assert(_str != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the string into the storage area
|
|
||||||
memmove(_str, str, len);
|
|
||||||
_str[len] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const String &str)
|
|
||||||
: _size(str._size) {
|
|
||||||
if (str.isStorageIntern()) {
|
|
||||||
// String in internal storage: just copy it
|
|
||||||
memcpy(_storage, str._storage, _builtinCapacity);
|
|
||||||
_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 != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(char c)
|
|
||||||
: _size(0), _str(_storage) {
|
|
||||||
|
|
||||||
_storage[0] = c;
|
|
||||||
_storage[1] = 0;
|
|
||||||
|
|
||||||
_size = (c == 0) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::~String() {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::makeUnique() {
|
|
||||||
ensureCapacity(_size, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that enough storage is available to store at least new_size
|
|
||||||
* 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_size, bool keep_old) {
|
|
||||||
bool isShared;
|
|
||||||
uint32 curCapacity, newCapacity;
|
|
||||||
char *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 char[newCapacity];
|
|
||||||
assert(newStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy old data if needed, elsewise reset the new storage.
|
|
||||||
if (keep_old) {
|
|
||||||
assert(_size < newCapacity);
|
|
||||||
memcpy(newStorage, _str, _size + 1);
|
|
||||||
} 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 = 0;
|
|
||||||
_extern._capacity = newCapacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::incRefCount() const {
|
|
||||||
assert(!isStorageIntern());
|
|
||||||
if (_extern._refCount == 0) {
|
|
||||||
if (g_refCountPool == 0) {
|
|
||||||
g_refCountPool = new MemoryPool(sizeof(int));
|
|
||||||
assert(g_refCountPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
_extern._refCount = (int *)g_refCountPool->allocChunk();
|
|
||||||
*_extern._refCount = 2;
|
|
||||||
} else {
|
|
||||||
++(*_extern._refCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::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) {
|
|
||||||
assert(g_refCountPool);
|
|
||||||
g_refCountPool->freeChunk(oldRefCount);
|
|
||||||
}
|
|
||||||
delete[] _str;
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator=(const char *str) {
|
|
||||||
uint32 len = strlen(str);
|
|
||||||
ensureCapacity(len, false);
|
|
||||||
_size = len;
|
|
||||||
memmove(_str, str, len + 1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator=(const String &str) {
|
|
||||||
if (&str == this)
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
if (str.isStorageIntern()) {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
_size = str._size;
|
|
||||||
_str = _storage;
|
|
||||||
memcpy(_str, str._str, _size + 1);
|
|
||||||
} else {
|
|
||||||
str.incRefCount();
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
|
|
||||||
_extern._refCount = str._extern._refCount;
|
|
||||||
_extern._capacity = str._extern._capacity;
|
|
||||||
_size = str._size;
|
|
||||||
_str = str._str;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator=(char c) {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
_str = _storage;
|
|
||||||
|
|
||||||
_str[0] = c;
|
|
||||||
_str[1] = 0;
|
|
||||||
|
|
||||||
_size = (c == 0) ? 0 : 1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator+=(const char *str) {
|
|
||||||
if (_str <= str && str <= _str + _size)
|
|
||||||
return operator+=(String(str));
|
|
||||||
|
|
||||||
int len = strlen(str);
|
|
||||||
if (len > 0) {
|
|
||||||
ensureCapacity(_size + len, true);
|
|
||||||
|
|
||||||
memcpy(_str + _size, str, len + 1);
|
|
||||||
_size += len;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator+=(const String &str) {
|
|
||||||
if (&str == this)
|
|
||||||
return operator+=(String(str));
|
|
||||||
|
|
||||||
int len = str._size;
|
|
||||||
if (len > 0) {
|
|
||||||
ensureCapacity(_size + len, true);
|
|
||||||
|
|
||||||
memcpy(_str + _size, str._str, len + 1);
|
|
||||||
_size += len;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator+=(char c) {
|
|
||||||
ensureCapacity(_size + 1, true);
|
|
||||||
|
|
||||||
_str[_size++] = c;
|
|
||||||
_str[_size] = 0;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::hasPrefix(const String &x) const {
|
|
||||||
return hasPrefix(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::hasPrefix(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
// Compare x with the start of _str.
|
|
||||||
const char *y = c_str();
|
|
||||||
while (*x && *x == *y) {
|
|
||||||
++x;
|
|
||||||
++y;
|
|
||||||
}
|
|
||||||
// It's a prefix, if and only if all letters in x are 'used up' before
|
|
||||||
// _str ends.
|
|
||||||
return *x == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::hasSuffix(const String &x) const {
|
|
||||||
return hasSuffix(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::hasSuffix(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
// Compare x with the end of _str.
|
|
||||||
const uint32 x_size = strlen(x);
|
|
||||||
if (x_size > _size)
|
|
||||||
return false;
|
|
||||||
const char *y = c_str() + _size - x_size;
|
|
||||||
while (*x && *x == *y) {
|
|
||||||
++x;
|
|
||||||
++y;
|
|
||||||
}
|
|
||||||
// It's a suffix, if and only if all letters in x are 'used up' before
|
|
||||||
// _str ends.
|
|
||||||
return *x == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::contains(const String &x) const {
|
|
||||||
return strstr(c_str(), x.c_str()) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::contains(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return strstr(c_str(), x) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::contains(char x) const {
|
|
||||||
return strchr(c_str(), x) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::deleteLastChar() {
|
|
||||||
if (_size > 0)
|
|
||||||
deleteChar(_size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::deleteChar(uint32 p) {
|
|
||||||
assert(p < _size);
|
|
||||||
|
|
||||||
makeUnique();
|
|
||||||
while (p++ < _size)
|
|
||||||
_str[p - 1] = _str[p];
|
|
||||||
_size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::erase(uint32 p, uint32 len) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::clear() {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
|
|
||||||
_size = 0;
|
|
||||||
_str = _storage;
|
|
||||||
_storage[0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::setChar(char c, uint32 p) {
|
|
||||||
assert(p < _size);
|
|
||||||
|
|
||||||
makeUnique();
|
|
||||||
_str[p] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::insertChar(char 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::toLowercase() {
|
|
||||||
makeUnique();
|
|
||||||
for (uint32 i = 0; i < _size; ++i)
|
|
||||||
_str[i] = tolower(_str[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::toUppercase() {
|
|
||||||
makeUnique();
|
|
||||||
for (uint32 i = 0; i < _size; ++i)
|
|
||||||
_str[i] = toupper(_str[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint String::hash() const {
|
|
||||||
return hashit(c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
String String::format(const char *fmt, ...) {
|
|
||||||
String output;
|
|
||||||
|
|
||||||
va_list va;
|
|
||||||
va_start(va, fmt);
|
|
||||||
output = String::vformat(fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
String String::vformat(const char *fmt, va_list args) {
|
|
||||||
String output;
|
|
||||||
assert(output.isStorageIntern());
|
|
||||||
|
|
||||||
va_list va;
|
|
||||||
scumm_va_copy(va, args);
|
|
||||||
int len = vsnprintf(output._str, _builtinCapacity, fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
|
|
||||||
if (len == -1 || len == _builtinCapacity - 1) {
|
|
||||||
// MSVC and IRIX don't return the size the full string would take up.
|
|
||||||
// MSVC returns -1, IRIX returns the number of characters actually written,
|
|
||||||
// which is at the most the size of the buffer minus one, as the string is
|
|
||||||
// truncated to fit.
|
|
||||||
|
|
||||||
// We assume MSVC failed to output the correct, null-terminated string
|
|
||||||
// if the return value is either -1 or size.
|
|
||||||
// For IRIX, because we lack a better mechanism, we assume failure
|
|
||||||
// if the return value equals size - 1.
|
|
||||||
// The downside to this is that whenever we try to format a string where the
|
|
||||||
// size is 1 below the built-in capacity, the size is needlessly increased.
|
|
||||||
|
|
||||||
// Try increasing the size of the string until it fits.
|
|
||||||
int size = _builtinCapacity;
|
|
||||||
do {
|
|
||||||
size *= 2;
|
|
||||||
output.ensureCapacity(size - 1, false);
|
|
||||||
assert(!output.isStorageIntern());
|
|
||||||
size = output._extern._capacity;
|
|
||||||
|
|
||||||
scumm_va_copy(va, args);
|
|
||||||
len = vsnprintf(output._str, size, fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
} while (len == -1 || len >= size - 1);
|
|
||||||
output._size = len;
|
|
||||||
} else if (len < (int)_builtinCapacity) {
|
|
||||||
// vsnprintf succeeded
|
|
||||||
output._size = len;
|
|
||||||
} else {
|
|
||||||
// vsnprintf didn't have enough space, so grow buffer
|
|
||||||
output.ensureCapacity(len, false);
|
|
||||||
scumm_va_copy(va, args);
|
|
||||||
int len2 = vsnprintf(output._str, len+1, fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
assert(len == len2);
|
|
||||||
output._size = len2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
bool String::operator==(const String &x) const {
|
|
||||||
return equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator==(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator!=(const String &x) const {
|
|
||||||
return !equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator !=(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return !equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator<(const String &x) const {
|
|
||||||
return compareTo(x) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator<=(const String &x) const {
|
|
||||||
return compareTo(x) <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator>(const String &x) const {
|
|
||||||
return compareTo(x) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator>=(const String &x) const {
|
|
||||||
return compareTo(x) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
bool operator==(const char* y, const String &x) {
|
|
||||||
return (x == y);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const char* y, const String &x) {
|
|
||||||
return x != y;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
bool String::equals(const String &x) const {
|
|
||||||
return (0 == compareTo(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::equals(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return (0 == compareTo(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::equalsIgnoreCase(const String &x) const {
|
|
||||||
return (0 == compareToIgnoreCase(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::equalsIgnoreCase(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return (0 == compareToIgnoreCase(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareTo(const String &x) const {
|
|
||||||
return compareTo(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareTo(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return strcmp(c_str(), x);
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareToIgnoreCase(const String &x) const {
|
|
||||||
return compareToIgnoreCase(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareToIgnoreCase(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return scumm_stricmp(c_str(), x);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
String operator+(const String &x, const String &y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String operator+(const char *x, const String &y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String operator+(const String &x, const char *y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String operator+(char x, const String &y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String operator+(const String &x, char y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String lastPathComponent(const String &path, const char sep) {
|
|
||||||
const char *str = path.c_str();
|
|
||||||
const char *last = str + path.size();
|
|
||||||
|
|
||||||
// Skip over trailing slashes
|
|
||||||
while (last > str && *(last-1) == sep)
|
|
||||||
--last;
|
|
||||||
|
|
||||||
// Path consisted of only slashes -> return empty string
|
|
||||||
if (last == str)
|
|
||||||
return String();
|
|
||||||
|
|
||||||
// Now scan the whole component
|
|
||||||
const char *first = last - 1;
|
|
||||||
while (first > str && *first != sep)
|
|
||||||
--first;
|
|
||||||
|
|
||||||
if (*first == sep)
|
|
||||||
first++;
|
|
||||||
|
|
||||||
return String(first, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
String normalizePath(const String &path, const char sep) {
|
|
||||||
if (path.empty())
|
|
||||||
return path;
|
|
||||||
|
|
||||||
const char *cur = path.c_str();
|
|
||||||
String result;
|
|
||||||
|
|
||||||
// If there is a leading slash, preserve that:
|
|
||||||
if (*cur == sep) {
|
|
||||||
result += sep;
|
|
||||||
// Skip over multiple leading slashes, so "//" equals "/"
|
|
||||||
while (*cur == sep)
|
|
||||||
++cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan for path components till the end of the String
|
|
||||||
List<String> comps;
|
|
||||||
while (*cur != 0) {
|
|
||||||
const char *start = cur;
|
|
||||||
|
|
||||||
// Scan till the next path separator resp. the end of the string
|
|
||||||
while (*cur != sep && *cur != 0)
|
|
||||||
cur++;
|
|
||||||
|
|
||||||
const String component(start, cur);
|
|
||||||
|
|
||||||
if (component.empty() || component == ".") {
|
|
||||||
// Skip empty components and dot components
|
|
||||||
} else if (!comps.empty() && component == ".." && comps.back() != "..") {
|
|
||||||
// If stack is non-empty and top is not "..", remove top
|
|
||||||
comps.pop_back();
|
|
||||||
} else {
|
|
||||||
// Add the component to the stack
|
|
||||||
comps.push_back(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip over separator chars
|
|
||||||
while (*cur == sep)
|
|
||||||
cur++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, assemble all components back into a path
|
|
||||||
while (!comps.empty()) {
|
|
||||||
result += comps.front();
|
|
||||||
comps.pop_front();
|
|
||||||
if (!comps.empty())
|
|
||||||
result += sep;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t strlcpy(char *dst, const char *src, size_t size) {
|
|
||||||
// Our backup of the source's start, we need this
|
|
||||||
// to calculate the source's length.
|
|
||||||
const char * const srcStart = src;
|
|
||||||
|
|
||||||
// In case a non-empty size was specified we
|
|
||||||
// copy over (size - 1) bytes at max.
|
|
||||||
if (size != 0) {
|
|
||||||
// Copy over (size - 1) bytes at max.
|
|
||||||
while (--size != 0) {
|
|
||||||
if ((*dst++ = *src) == 0)
|
|
||||||
break;
|
|
||||||
++src;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the source string was longer than the
|
|
||||||
// destination, we need to add a terminating
|
|
||||||
// zero.
|
|
||||||
if (size == 0)
|
|
||||||
*dst = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the terminating zero of the source
|
|
||||||
// string, we need this to determine the length
|
|
||||||
// of the source string.
|
|
||||||
while (*src)
|
|
||||||
++src;
|
|
||||||
|
|
||||||
// Return the source string's length.
|
|
||||||
return src - srcStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t strlcat(char *dst, const char *src, size_t size) {
|
|
||||||
// In case the destination buffer does not contain
|
|
||||||
// space for at least 1 character, we will just
|
|
||||||
// return the source string's length.
|
|
||||||
if (size == 0)
|
|
||||||
return strlen(src);
|
|
||||||
|
|
||||||
// Our backup of the source's start, we need this
|
|
||||||
// to calculate the source's length.
|
|
||||||
const char * const srcStart = src;
|
|
||||||
|
|
||||||
// Our backup of the destination's start, we need
|
|
||||||
// this to calculate the destination's length.
|
|
||||||
const char * const dstStart = dst;
|
|
||||||
|
|
||||||
// Search the end of the destination, but do not
|
|
||||||
// move past the terminating zero.
|
|
||||||
while (size-- != 0 && *dst != 0)
|
|
||||||
++dst;
|
|
||||||
|
|
||||||
// Calculate the destination's length;
|
|
||||||
const size_t dstLength = dst - dstStart;
|
|
||||||
|
|
||||||
// In case we reached the end of the destination
|
|
||||||
// buffer before we had a chance to append any
|
|
||||||
// characters we will just return the destination
|
|
||||||
// length plus the source string's length.
|
|
||||||
if (size == 0)
|
|
||||||
return dstLength + strlen(srcStart);
|
|
||||||
|
|
||||||
// Copy over all of the source that fits
|
|
||||||
// the destination buffer. We also need
|
|
||||||
// to take the terminating zero we will
|
|
||||||
// add into consideration.
|
|
||||||
while (size-- != 0 && *src != 0)
|
|
||||||
*dst++ = *src++;
|
|
||||||
*dst = 0;
|
|
||||||
|
|
||||||
// Move to the terminating zero of the source
|
|
||||||
// string, we need this to determine the length
|
|
||||||
// of the source string.
|
|
||||||
while (*src)
|
|
||||||
++src;
|
|
||||||
|
|
||||||
// Return the total length of the result string
|
|
||||||
return dstLength + (src - srcStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // End of namespace Common
|
|
||||||
|
|
||||||
// Portable implementation of stricmp / strcasecmp / strcmpi.
|
|
||||||
// TODO: Rename this to Common::strcasecmp
|
|
||||||
int scumm_stricmp(const char *s1, const char *s2) {
|
|
||||||
byte l1, l2;
|
|
||||||
do {
|
|
||||||
// Don't use ++ inside tolower, in case the macro uses its
|
|
||||||
// arguments more than once.
|
|
||||||
l1 = (byte)*s1++;
|
|
||||||
l1 = tolower(l1);
|
|
||||||
l2 = (byte)*s2++;
|
|
||||||
l2 = tolower(l2);
|
|
||||||
} while (l1 == l2 && l1 != 0);
|
|
||||||
return l1 - l2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Portable implementation of strnicmp / strncasecmp / strncmpi.
|
|
||||||
// TODO: Rename this to Common::strncasecmp
|
|
||||||
int scumm_strnicmp(const char *s1, const char *s2, uint n) {
|
|
||||||
byte l1, l2;
|
|
||||||
do {
|
|
||||||
if (n-- == 0)
|
|
||||||
return 0; // no difference found so far -> signal equality
|
|
||||||
|
|
||||||
// Don't use ++ inside tolower, in case the macro uses its
|
|
||||||
// arguments more than once.
|
|
||||||
l1 = (byte)*s1++;
|
|
||||||
l1 = tolower(l1);
|
|
||||||
l2 = (byte)*s2++;
|
|
||||||
l2 = tolower(l2);
|
|
||||||
} while (l1 == l2 && l1 != 0);
|
|
||||||
return l1 - l2;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,386 +0,0 @@
|
||||||
/* 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.
|
|
||||||
*
|
|
||||||
* 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 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef COMMON_STRING_H
|
|
||||||
#define COMMON_STRING_H
|
|
||||||
|
|
||||||
#include "common/scummsys.h"
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple string class for ScummVM. Provides automatic storage managment,
|
|
||||||
* and overloads several operators in a 'natural' fashion, mimicking
|
|
||||||
* the std::string class. Even provides simple iterators.
|
|
||||||
*
|
|
||||||
* This class tries to avoid allocating lots of small blocks on the heap,
|
|
||||||
* since that is inefficient on several platforms supported by ScummVM.
|
|
||||||
* Instead, small strings are stored 'inside' the string object (i.e. on
|
|
||||||
* the stack, for stack allocated objects), and only for strings exceeding
|
|
||||||
* a certain length do we allocate a buffer on the heap.
|
|
||||||
*
|
|
||||||
* The presence of \0 characters in the string will cause undefined
|
|
||||||
* behavior in some operations.
|
|
||||||
*/
|
|
||||||
class String {
|
|
||||||
public:
|
|
||||||
static const uint32 npos = 0xFFFFFFFF;
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* The size of the internal storage. Increasing this means less heap
|
|
||||||
* allocations are needed, at the cost of more stack memory usage,
|
|
||||||
* and of course lots of wasted memory. Empirically, 90% or more of
|
|
||||||
* all String instances are less than 32 chars long. If a platform
|
|
||||||
* is very short on stack space, it would be possible to lower this.
|
|
||||||
* A value of 24 still seems acceptable, though considerably worse,
|
|
||||||
* while 16 seems to be the lowest you want to go... Anything lower
|
|
||||||
* than 8 makes no sense, since that's the size of member _extern
|
|
||||||
* (on 32 bit machines; 12 bytes on systems with 64bit pointers).
|
|
||||||
*/
|
|
||||||
static const uint32 _builtinCapacity = 32 - sizeof(uint32) - sizeof(char *);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Length of the string. Stored to avoid having to call strlen
|
|
||||||
* a lot. Yes, we limit ourselves to strings shorter than 4GB --
|
|
||||||
* on purpose :-).
|
|
||||||
*/
|
|
||||||
uint32 _size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pointer to the actual string storage. Either points to _storage,
|
|
||||||
* or to a block allocated on the heap via malloc.
|
|
||||||
*/
|
|
||||||
char *_str;
|
|
||||||
|
|
||||||
|
|
||||||
union {
|
|
||||||
/**
|
|
||||||
* Internal string storage.
|
|
||||||
*/
|
|
||||||
char _storage[_builtinCapacity];
|
|
||||||
/**
|
|
||||||
* External string storage data -- the refcounter, and the
|
|
||||||
* capacity of the string _str points to.
|
|
||||||
*/
|
|
||||||
struct {
|
|
||||||
mutable int *_refCount;
|
|
||||||
uint32 _capacity;
|
|
||||||
} _extern;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool isStorageIntern() const {
|
|
||||||
return _str == _storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/** Construct a new empty string. */
|
|
||||||
String() : _size(0), _str(_storage) { _storage[0] = 0; }
|
|
||||||
|
|
||||||
/** Construct a new string from the given NULL-terminated C string. */
|
|
||||||
String(const char *str);
|
|
||||||
|
|
||||||
/** Construct a new string containing exactly len characters read from address str. */
|
|
||||||
String(const char *str, uint32 len);
|
|
||||||
|
|
||||||
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */
|
|
||||||
String(const char *beginP, const char *endP);
|
|
||||||
|
|
||||||
/** Construct a copy of the given string. */
|
|
||||||
String(const String &str);
|
|
||||||
|
|
||||||
/** Construct a string consisting of the given character. */
|
|
||||||
explicit String(char c);
|
|
||||||
|
|
||||||
~String();
|
|
||||||
|
|
||||||
String &operator=(const char *str);
|
|
||||||
String &operator=(const String &str);
|
|
||||||
String &operator=(char c);
|
|
||||||
String &operator+=(const char *str);
|
|
||||||
String &operator+=(const String &str);
|
|
||||||
String &operator+=(char c);
|
|
||||||
|
|
||||||
bool operator==(const String &x) const;
|
|
||||||
bool operator==(const char *x) const;
|
|
||||||
bool operator!=(const String &x) const;
|
|
||||||
bool operator!=(const char *x) const;
|
|
||||||
|
|
||||||
bool operator<(const String &x) const;
|
|
||||||
bool operator<=(const String &x) const;
|
|
||||||
bool operator>(const String &x) const;
|
|
||||||
bool operator>=(const String &x) const;
|
|
||||||
|
|
||||||
bool equals(const String &x) const;
|
|
||||||
bool equalsIgnoreCase(const String &x) const;
|
|
||||||
int compareTo(const String &x) const; // strcmp clone
|
|
||||||
int compareToIgnoreCase(const String &x) const; // stricmp clone
|
|
||||||
|
|
||||||
bool equals(const char *x) const;
|
|
||||||
bool equalsIgnoreCase(const char *x) const;
|
|
||||||
int compareTo(const char *x) const; // strcmp clone
|
|
||||||
int compareToIgnoreCase(const char *x) const; // stricmp clone
|
|
||||||
|
|
||||||
bool hasSuffix(const String &x) const;
|
|
||||||
bool hasSuffix(const char *x) const;
|
|
||||||
|
|
||||||
bool hasPrefix(const String &x) const;
|
|
||||||
bool hasPrefix(const char *x) const;
|
|
||||||
|
|
||||||
bool contains(const String &x) const;
|
|
||||||
bool contains(const char *x) const;
|
|
||||||
bool contains(char x) const;
|
|
||||||
|
|
||||||
inline const char *c_str() const { return _str; }
|
|
||||||
inline uint size() const { return _size; }
|
|
||||||
|
|
||||||
inline bool empty() const { return (_size == 0); }
|
|
||||||
char firstChar() const { return (_size > 0) ? _str[0] : 0; }
|
|
||||||
char lastChar() const { return (_size > 0) ? _str[_size - 1] : 0; }
|
|
||||||
|
|
||||||
char operator[](int idx) const {
|
|
||||||
assert(_str && idx >= 0 && idx < (int)_size);
|
|
||||||
return _str[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Remove the last character from the string. */
|
|
||||||
void deleteLastChar();
|
|
||||||
|
|
||||||
/** Remove the character at position p from the string. */
|
|
||||||
void deleteChar(uint32 p);
|
|
||||||
|
|
||||||
/** Remove all characters from position p to the p + len. If len = String::npos, removes all characters to the end */
|
|
||||||
void erase(uint32 p, uint32 len = npos);
|
|
||||||
|
|
||||||
/** Set character c at position p, replacing the previous character there. */
|
|
||||||
void setChar(char c, uint32 p);
|
|
||||||
|
|
||||||
/** Insert character c before position p. */
|
|
||||||
void insertChar(char c, uint32 p);
|
|
||||||
|
|
||||||
/** Clears the string, making it empty. */
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
/** Convert all characters in the string to lowercase. */
|
|
||||||
void toLowercase();
|
|
||||||
|
|
||||||
/** Convert all characters in the string to uppercase. */
|
|
||||||
void toUppercase();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes trailing and leading whitespaces. Uses isspace() to decide
|
|
||||||
* what is whitespace and what not.
|
|
||||||
*/
|
|
||||||
void trim();
|
|
||||||
|
|
||||||
uint hash() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print formatted data into a String object. Similar to sprintf,
|
|
||||||
* except that it stores the result in (variably sized) String
|
|
||||||
* instead of a fixed size buffer.
|
|
||||||
*/
|
|
||||||
static String format(const char *fmt, ...) GCC_PRINTF(1,2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print formatted data into a String object. Similar to vsprintf,
|
|
||||||
* except that it stores the result in (variably sized) String
|
|
||||||
* instead of a fixed size buffer.
|
|
||||||
*/
|
|
||||||
static String vformat(const char *fmt, va_list args);
|
|
||||||
|
|
||||||
public:
|
|
||||||
typedef char value_type;
|
|
||||||
/**
|
|
||||||
* Unsigned version of the underlying type. This can be used to cast
|
|
||||||
* individual string characters to bigger integer types without sign
|
|
||||||
* extension happening.
|
|
||||||
*/
|
|
||||||
typedef unsigned char unsigned_type;
|
|
||||||
typedef char * iterator;
|
|
||||||
typedef const char * const_iterator;
|
|
||||||
|
|
||||||
iterator begin() {
|
|
||||||
// Since the user could potentially
|
|
||||||
// change the string via the returned
|
|
||||||
// iterator we have to assure we are
|
|
||||||
// pointing to a unique storage.
|
|
||||||
makeUnique();
|
|
||||||
|
|
||||||
return _str;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator end() {
|
|
||||||
return begin() + size();
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator begin() const {
|
|
||||||
return _str;
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator end() const {
|
|
||||||
return begin() + size();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void makeUnique();
|
|
||||||
void ensureCapacity(uint32 new_size, bool keep_old);
|
|
||||||
void incRefCount() const;
|
|
||||||
void decRefCount(int *oldRefCount);
|
|
||||||
void initWithCStr(const char *str, uint32 len);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Append two strings to form a new (temp) string
|
|
||||||
String operator+(const String &x, const String &y);
|
|
||||||
|
|
||||||
String operator+(const char *x, const String &y);
|
|
||||||
String operator+(const String &x, const char *y);
|
|
||||||
|
|
||||||
String operator+(const String &x, char y);
|
|
||||||
String operator+(char x, const String &y);
|
|
||||||
|
|
||||||
// Some useful additional comparison operators for Strings
|
|
||||||
bool operator==(const char *x, const String &y);
|
|
||||||
bool operator!=(const char *x, const String &y);
|
|
||||||
|
|
||||||
// Utility functions to remove leading and trailing whitespaces
|
|
||||||
extern char *ltrim(char *t);
|
|
||||||
extern char *rtrim(char *t);
|
|
||||||
extern char *trim(char *t);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the last component of a given path.
|
|
||||||
*
|
|
||||||
* Examples:
|
|
||||||
* /foo/bar.txt would return 'bar.txt'
|
|
||||||
* /foo/bar/ would return 'bar'
|
|
||||||
* /foo/./bar// would return 'bar'
|
|
||||||
*
|
|
||||||
* @param path the path of which we want to know the last component
|
|
||||||
* @param sep character used to separate path components
|
|
||||||
* @return The last component of the path.
|
|
||||||
*/
|
|
||||||
String lastPathComponent(const String &path, const char sep);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize a given path to a canonical form. In particular:
|
|
||||||
* - trailing separators are removed: /foo/bar/ -> /foo/bar
|
|
||||||
* - double separators (= empty components) are removed: /foo//bar -> /foo/bar
|
|
||||||
* - dot components are removed: /foo/./bar -> /foo/bar
|
|
||||||
*
|
|
||||||
* @todo remove double dot components: /foo/baz/../bar -> /foo/bar
|
|
||||||
*
|
|
||||||
* @param path the path to normalize
|
|
||||||
* @param sep the separator token (usually '/' on Unix-style systems, or '\\' on Windows based stuff)
|
|
||||||
* @return the normalized path
|
|
||||||
*/
|
|
||||||
String normalizePath(const String &path, const char sep);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple DOS-style pattern matching function (understands * and ? like used in DOS).
|
|
||||||
* Taken from exult/files/listfiles.cc
|
|
||||||
*
|
|
||||||
* Token meaning:
|
|
||||||
* "*": any character, any amount of times.
|
|
||||||
* "?": any character, only once.
|
|
||||||
* "#": any decimal digit, only once.
|
|
||||||
*
|
|
||||||
* Example strings/patterns:
|
|
||||||
* String: monkey.s01 Pattern: monkey.s?? => true
|
|
||||||
* String: monkey.s101 Pattern: monkey.s?? => false
|
|
||||||
* String: monkey.s99 Pattern: monkey.s?1 => false
|
|
||||||
* String: monkey.s101 Pattern: monkey.s* => true
|
|
||||||
* String: monkey.s99 Pattern: monkey.s*1 => false
|
|
||||||
* String: monkey.s01 Pattern: monkey.s## => true
|
|
||||||
* String: monkey.s01 Pattern: monkey.### => false
|
|
||||||
*
|
|
||||||
* @param str Text to be matched against the given pattern.
|
|
||||||
* @param pat Glob pattern.
|
|
||||||
* @param ignoreCase Whether to ignore the case when doing pattern match
|
|
||||||
* @param pathMode Whether to use path mode, i.e., whether slashes must be matched explicitly.
|
|
||||||
*
|
|
||||||
* @return true if str matches the pattern, false otherwise.
|
|
||||||
*/
|
|
||||||
bool matchString(const char *str, const char *pat, bool ignoreCase = false, bool pathMode = false);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Take a 32 bit value and turn it into a four character string, where each of
|
|
||||||
* the four bytes is turned into one character. Most significant byte is printed
|
|
||||||
* first.
|
|
||||||
*/
|
|
||||||
String tag2string(uint32 tag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copy up to size - 1 characters from src to dst and also zero terminate the
|
|
||||||
* result. Note that src must be a zero terminated string.
|
|
||||||
*
|
|
||||||
* In case size is zero this function just returns the length of the source
|
|
||||||
* string.
|
|
||||||
*
|
|
||||||
* @note This is modeled after OpenBSD's strlcpy. See the manpage here:
|
|
||||||
* http://www.openbsd.org/cgi-bin/man.cgi?query=strlcpy
|
|
||||||
*
|
|
||||||
* @param dst The destination buffer.
|
|
||||||
* @param src The source string.
|
|
||||||
* @param size The size of the destination buffer.
|
|
||||||
* @return The length of the (non-truncated) result, i.e. strlen(src).
|
|
||||||
*/
|
|
||||||
size_t strlcpy(char *dst, const char *src, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append the string src to the string dst. Note that both src and dst must be
|
|
||||||
* zero terminated. The result will be zero terminated. At most
|
|
||||||
* "size - strlen(dst) - 1" bytes will be appended.
|
|
||||||
*
|
|
||||||
* In case the dst string does not contain a zero within the first "size" bytes
|
|
||||||
* the dst string will not be changed and size + strlen(src) is returned.
|
|
||||||
*
|
|
||||||
* @note This is modeled after OpenBSD's strlcat. See the manpage here:
|
|
||||||
* http://www.openbsd.org/cgi-bin/man.cgi?query=strlcat
|
|
||||||
*
|
|
||||||
* @param dst The string the source string should be appended to.
|
|
||||||
* @param src The source string.
|
|
||||||
* @param size The (total) size of the destination buffer.
|
|
||||||
* @return The length of the (non-truncated) result. That is
|
|
||||||
* strlen(dst) + strlen(src). In case strlen(dst) > size
|
|
||||||
* size + strlen(src) is returned.
|
|
||||||
*/
|
|
||||||
size_t strlcat(char *dst, const char *src, size_t size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience wrapper for tag2string which "returns" a C string.
|
|
||||||
* Note: It is *NOT* safe to do anything with the return value other than directly
|
|
||||||
* copying or printing it.
|
|
||||||
*/
|
|
||||||
#define tag2str(x) Common::tag2string(x).c_str()
|
|
||||||
|
|
||||||
|
|
||||||
} // End of namespace Common
|
|
||||||
|
|
||||||
extern int scumm_stricmp(const char *s1, const char *s2);
|
|
||||||
extern int scumm_strnicmp(const char *s1, const char *s2, uint n);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||||
|
|
||||||
#include "str.h"
|
#include "common/str.h"
|
||||||
#include "winexe.h"
|
#include "winexe.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "hash-str.h"
|
#include "hash-str.h"
|
||||||
#include "str.h"
|
#include "common/str.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||||
|
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "str.h"
|
#include "common/str.h"
|
||||||
#include "winexe_pe.h"
|
#include "winexe_pe.h"
|
||||||
#include "common/array.h"
|
#include "common/array.h"
|
||||||
#include "common/endian.h"
|
#include "common/endian.h"
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "hash-str.h"
|
#include "hash-str.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "str.h"
|
#include "common/str.h"
|
||||||
#include "winexe.h"
|
#include "winexe.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
2
devtools/create_xeen/base-str.cpp
Normal file
2
devtools/create_xeen/base-str.cpp
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#define SCUMMVM_UTIL 1
|
||||||
|
#include "../common/base-str.cpp"
|
|
@ -9,6 +9,7 @@ MODULE_OBJS := \
|
||||||
map.o \
|
map.o \
|
||||||
memorypool.o \
|
memorypool.o \
|
||||||
str.o \
|
str.o \
|
||||||
|
base-str.o \
|
||||||
swords.o
|
swords.o
|
||||||
|
|
||||||
# Set the name of the executable
|
# Set the name of the executable
|
||||||
|
|
|
@ -1,786 +1,2 @@
|
||||||
/* ScummVM - Graphic Adventure Engine
|
#define SCUMMVM_UTIL
|
||||||
*
|
#include "../common/str.cpp"
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* 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 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "common/hash-str.h"
|
|
||||||
#include "common/list.h"
|
|
||||||
#include "memorypool.h"
|
|
||||||
#include "common/str.h"
|
|
||||||
#include "common/util.h"
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
|
|
||||||
|
|
||||||
static uint32 computeCapacity(uint32 len) {
|
|
||||||
// By default, for the capacity we use the next multiple of 32
|
|
||||||
return ((len + 32 - 1) & ~0x1F);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *str) : _size(0), _str(_storage) {
|
|
||||||
if (str == 0) {
|
|
||||||
_storage[0] = 0;
|
|
||||||
_size = 0;
|
|
||||||
} else
|
|
||||||
initWithCStr(str, strlen(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
|
|
||||||
initWithCStr(str, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const char *beginP, const char *endP) : _size(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;
|
|
||||||
|
|
||||||
_size = len;
|
|
||||||
|
|
||||||
if (len >= _builtinCapacity) {
|
|
||||||
// Not enough internal storage, so allocate more
|
|
||||||
_extern._capacity = computeCapacity(len+1);
|
|
||||||
_extern._refCount = 0;
|
|
||||||
_str = new char[_extern._capacity];
|
|
||||||
assert(_str != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the string into the storage area
|
|
||||||
memmove(_str, str, len);
|
|
||||||
_str[len] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(const String &str)
|
|
||||||
: _size(str._size) {
|
|
||||||
if (str.isStorageIntern()) {
|
|
||||||
// String in internal storage: just copy it
|
|
||||||
memcpy(_storage, str._storage, _builtinCapacity);
|
|
||||||
_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 != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
String::String(char c)
|
|
||||||
: _size(0), _str(_storage) {
|
|
||||||
|
|
||||||
_storage[0] = c;
|
|
||||||
_storage[1] = 0;
|
|
||||||
|
|
||||||
_size = (c == 0) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String::~String() {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::makeUnique() {
|
|
||||||
ensureCapacity(_size, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that enough storage is available to store at least new_size
|
|
||||||
* 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_size, bool keep_old) {
|
|
||||||
bool isShared;
|
|
||||||
uint32 curCapacity, newCapacity;
|
|
||||||
char *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 char[newCapacity];
|
|
||||||
assert(newStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy old data if needed, elsewise reset the new storage.
|
|
||||||
if (keep_old) {
|
|
||||||
assert(_size < newCapacity);
|
|
||||||
memcpy(newStorage, _str, _size + 1);
|
|
||||||
} 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 = 0;
|
|
||||||
_extern._capacity = newCapacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::incRefCount() const {
|
|
||||||
assert(!isStorageIntern());
|
|
||||||
if (_extern._refCount == 0) {
|
|
||||||
if (g_refCountPool == 0) {
|
|
||||||
g_refCountPool = new MemoryPool(sizeof(int));
|
|
||||||
assert(g_refCountPool);
|
|
||||||
}
|
|
||||||
|
|
||||||
_extern._refCount = (int *)g_refCountPool->allocChunk();
|
|
||||||
*_extern._refCount = 2;
|
|
||||||
} else {
|
|
||||||
++(*_extern._refCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::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) {
|
|
||||||
assert(g_refCountPool);
|
|
||||||
g_refCountPool->freeChunk(oldRefCount);
|
|
||||||
}
|
|
||||||
delete[] _str;
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator=(const char *str) {
|
|
||||||
uint32 len = strlen(str);
|
|
||||||
ensureCapacity(len, false);
|
|
||||||
_size = len;
|
|
||||||
memmove(_str, str, len + 1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator=(const String &str) {
|
|
||||||
if (&str == this)
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
if (str.isStorageIntern()) {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
_size = str._size;
|
|
||||||
_str = _storage;
|
|
||||||
memcpy(_str, str._str, _size + 1);
|
|
||||||
} else {
|
|
||||||
str.incRefCount();
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
|
|
||||||
_extern._refCount = str._extern._refCount;
|
|
||||||
_extern._capacity = str._extern._capacity;
|
|
||||||
_size = str._size;
|
|
||||||
_str = str._str;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator=(char c) {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
_str = _storage;
|
|
||||||
|
|
||||||
_str[0] = c;
|
|
||||||
_str[1] = 0;
|
|
||||||
|
|
||||||
_size = (c == 0) ? 0 : 1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator+=(const char *str) {
|
|
||||||
if (_str <= str && str <= _str + _size)
|
|
||||||
return operator+=(String(str));
|
|
||||||
|
|
||||||
int len = strlen(str);
|
|
||||||
if (len > 0) {
|
|
||||||
ensureCapacity(_size + len, true);
|
|
||||||
|
|
||||||
memcpy(_str + _size, str, len + 1);
|
|
||||||
_size += len;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator+=(const String &str) {
|
|
||||||
if (&str == this)
|
|
||||||
return operator+=(String(str));
|
|
||||||
|
|
||||||
int len = str._size;
|
|
||||||
if (len > 0) {
|
|
||||||
ensureCapacity(_size + len, true);
|
|
||||||
|
|
||||||
memcpy(_str + _size, str._str, len + 1);
|
|
||||||
_size += len;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
String &String::operator+=(char c) {
|
|
||||||
ensureCapacity(_size + 1, true);
|
|
||||||
|
|
||||||
_str[_size++] = c;
|
|
||||||
_str[_size] = 0;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::hasPrefix(const String &x) const {
|
|
||||||
return hasPrefix(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::hasPrefix(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
// Compare x with the start of _str.
|
|
||||||
const char *y = c_str();
|
|
||||||
while (*x && *x == *y) {
|
|
||||||
++x;
|
|
||||||
++y;
|
|
||||||
}
|
|
||||||
// It's a prefix, if and only if all letters in x are 'used up' before
|
|
||||||
// _str ends.
|
|
||||||
return *x == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::hasSuffix(const String &x) const {
|
|
||||||
return hasSuffix(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::hasSuffix(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
// Compare x with the end of _str.
|
|
||||||
const uint32 x_size = strlen(x);
|
|
||||||
if (x_size > _size)
|
|
||||||
return false;
|
|
||||||
const char *y = c_str() + _size - x_size;
|
|
||||||
while (*x && *x == *y) {
|
|
||||||
++x;
|
|
||||||
++y;
|
|
||||||
}
|
|
||||||
// It's a suffix, if and only if all letters in x are 'used up' before
|
|
||||||
// _str ends.
|
|
||||||
return *x == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::contains(const String &x) const {
|
|
||||||
return strstr(c_str(), x.c_str()) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::contains(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return strstr(c_str(), x) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::contains(char x) const {
|
|
||||||
return strchr(c_str(), x) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::deleteLastChar() {
|
|
||||||
if (_size > 0)
|
|
||||||
deleteChar(_size - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::deleteChar(uint32 p) {
|
|
||||||
assert(p < _size);
|
|
||||||
|
|
||||||
makeUnique();
|
|
||||||
while (p++ < _size)
|
|
||||||
_str[p - 1] = _str[p];
|
|
||||||
_size--;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::erase(uint32 p, uint32 len) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::clear() {
|
|
||||||
decRefCount(_extern._refCount);
|
|
||||||
|
|
||||||
_size = 0;
|
|
||||||
_str = _storage;
|
|
||||||
_storage[0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::setChar(char c, uint32 p) {
|
|
||||||
assert(p < _size);
|
|
||||||
|
|
||||||
makeUnique();
|
|
||||||
_str[p] = c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::insertChar(char 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::toLowercase() {
|
|
||||||
makeUnique();
|
|
||||||
for (uint32 i = 0; i < _size; ++i)
|
|
||||||
_str[i] = tolower(_str[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void String::toUppercase() {
|
|
||||||
makeUnique();
|
|
||||||
for (uint32 i = 0; i < _size; ++i)
|
|
||||||
_str[i] = toupper(_str[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint String::hash() const {
|
|
||||||
return hashit(c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
String String::format(const char *fmt, ...) {
|
|
||||||
String output;
|
|
||||||
|
|
||||||
va_list va;
|
|
||||||
va_start(va, fmt);
|
|
||||||
output = String::vformat(fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
String String::vformat(const char *fmt, va_list args) {
|
|
||||||
String output;
|
|
||||||
assert(output.isStorageIntern());
|
|
||||||
|
|
||||||
va_list va;
|
|
||||||
scumm_va_copy(va, args);
|
|
||||||
int len = vsnprintf(output._str, _builtinCapacity, fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
|
|
||||||
if (len == -1 || len == _builtinCapacity - 1) {
|
|
||||||
// MSVC and IRIX don't return the size the full string would take up.
|
|
||||||
// MSVC returns -1, IRIX returns the number of characters actually written,
|
|
||||||
// which is at the most the size of the buffer minus one, as the string is
|
|
||||||
// truncated to fit.
|
|
||||||
|
|
||||||
// We assume MSVC failed to output the correct, null-terminated string
|
|
||||||
// if the return value is either -1 or size.
|
|
||||||
// For IRIX, because we lack a better mechanism, we assume failure
|
|
||||||
// if the return value equals size - 1.
|
|
||||||
// The downside to this is that whenever we try to format a string where the
|
|
||||||
// size is 1 below the built-in capacity, the size is needlessly increased.
|
|
||||||
|
|
||||||
// Try increasing the size of the string until it fits.
|
|
||||||
int size = _builtinCapacity;
|
|
||||||
do {
|
|
||||||
size *= 2;
|
|
||||||
output.ensureCapacity(size - 1, false);
|
|
||||||
assert(!output.isStorageIntern());
|
|
||||||
size = output._extern._capacity;
|
|
||||||
|
|
||||||
scumm_va_copy(va, args);
|
|
||||||
len = vsnprintf(output._str, size, fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
} while (len == -1 || len >= size - 1);
|
|
||||||
output._size = len;
|
|
||||||
} else if (len < (int)_builtinCapacity) {
|
|
||||||
// vsnprintf succeeded
|
|
||||||
output._size = len;
|
|
||||||
} else {
|
|
||||||
// vsnprintf didn't have enough space, so grow buffer
|
|
||||||
output.ensureCapacity(len, false);
|
|
||||||
scumm_va_copy(va, args);
|
|
||||||
int len2 = vsnprintf(output._str, len+1, fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
assert(len == len2);
|
|
||||||
output._size = len2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
bool String::operator==(const String &x) const {
|
|
||||||
return equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator==(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator!=(const String &x) const {
|
|
||||||
return !equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator !=(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return !equals(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator<(const String &x) const {
|
|
||||||
return compareTo(x) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator<=(const String &x) const {
|
|
||||||
return compareTo(x) <= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator>(const String &x) const {
|
|
||||||
return compareTo(x) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::operator>=(const String &x) const {
|
|
||||||
return compareTo(x) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
bool operator==(const char* y, const String &x) {
|
|
||||||
return (x == y);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const char* y, const String &x) {
|
|
||||||
return x != y;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
bool String::equals(const String &x) const {
|
|
||||||
return (0 == compareTo(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::equals(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return (0 == compareTo(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::equalsIgnoreCase(const String &x) const {
|
|
||||||
return (0 == compareToIgnoreCase(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool String::equalsIgnoreCase(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return (0 == compareToIgnoreCase(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareTo(const String &x) const {
|
|
||||||
return compareTo(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareTo(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return strcmp(c_str(), x);
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareToIgnoreCase(const String &x) const {
|
|
||||||
return compareToIgnoreCase(x.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
int String::compareToIgnoreCase(const char *x) const {
|
|
||||||
assert(x != 0);
|
|
||||||
return scumm_stricmp(c_str(), x);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
|
|
||||||
String operator+(const String &x, const String &y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String operator+(const char *x, const String &y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String operator+(const String &x, const char *y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String operator+(char x, const String &y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String operator+(const String &x, char y) {
|
|
||||||
String temp(x);
|
|
||||||
temp += y;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
String lastPathComponent(const String &path, const char sep) {
|
|
||||||
const char *str = path.c_str();
|
|
||||||
const char *last = str + path.size();
|
|
||||||
|
|
||||||
// Skip over trailing slashes
|
|
||||||
while (last > str && *(last-1) == sep)
|
|
||||||
--last;
|
|
||||||
|
|
||||||
// Path consisted of only slashes -> return empty string
|
|
||||||
if (last == str)
|
|
||||||
return String();
|
|
||||||
|
|
||||||
// Now scan the whole component
|
|
||||||
const char *first = last - 1;
|
|
||||||
while (first > str && *first != sep)
|
|
||||||
--first;
|
|
||||||
|
|
||||||
if (*first == sep)
|
|
||||||
first++;
|
|
||||||
|
|
||||||
return String(first, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
String normalizePath(const String &path, const char sep) {
|
|
||||||
if (path.empty())
|
|
||||||
return path;
|
|
||||||
|
|
||||||
const char *cur = path.c_str();
|
|
||||||
String result;
|
|
||||||
|
|
||||||
// If there is a leading slash, preserve that:
|
|
||||||
if (*cur == sep) {
|
|
||||||
result += sep;
|
|
||||||
// Skip over multiple leading slashes, so "//" equals "/"
|
|
||||||
while (*cur == sep)
|
|
||||||
++cur;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan for path components till the end of the String
|
|
||||||
List<String> comps;
|
|
||||||
while (*cur != 0) {
|
|
||||||
const char *start = cur;
|
|
||||||
|
|
||||||
// Scan till the next path separator resp. the end of the string
|
|
||||||
while (*cur != sep && *cur != 0)
|
|
||||||
cur++;
|
|
||||||
|
|
||||||
const String component(start, cur);
|
|
||||||
|
|
||||||
if (component.empty() || component == ".") {
|
|
||||||
// Skip empty components and dot components
|
|
||||||
} else if (!comps.empty() && component == ".." && comps.back() != "..") {
|
|
||||||
// If stack is non-empty and top is not "..", remove top
|
|
||||||
comps.pop_back();
|
|
||||||
} else {
|
|
||||||
// Add the component to the stack
|
|
||||||
comps.push_back(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip over separator chars
|
|
||||||
while (*cur == sep)
|
|
||||||
cur++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, assemble all components back into a path
|
|
||||||
while (!comps.empty()) {
|
|
||||||
result += comps.front();
|
|
||||||
comps.pop_front();
|
|
||||||
if (!comps.empty())
|
|
||||||
result += sep;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t strlcpy(char *dst, const char *src, size_t size) {
|
|
||||||
// Our backup of the source's start, we need this
|
|
||||||
// to calculate the source's length.
|
|
||||||
const char * const srcStart = src;
|
|
||||||
|
|
||||||
// In case a non-empty size was specified we
|
|
||||||
// copy over (size - 1) bytes at max.
|
|
||||||
if (size != 0) {
|
|
||||||
// Copy over (size - 1) bytes at max.
|
|
||||||
while (--size != 0) {
|
|
||||||
if ((*dst++ = *src) == 0)
|
|
||||||
break;
|
|
||||||
++src;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the source string was longer than the
|
|
||||||
// destination, we need to add a terminating
|
|
||||||
// zero.
|
|
||||||
if (size == 0)
|
|
||||||
*dst = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the terminating zero of the source
|
|
||||||
// string, we need this to determine the length
|
|
||||||
// of the source string.
|
|
||||||
while (*src)
|
|
||||||
++src;
|
|
||||||
|
|
||||||
// Return the source string's length.
|
|
||||||
return src - srcStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t strlcat(char *dst, const char *src, size_t size) {
|
|
||||||
// In case the destination buffer does not contain
|
|
||||||
// space for at least 1 character, we will just
|
|
||||||
// return the source string's length.
|
|
||||||
if (size == 0)
|
|
||||||
return strlen(src);
|
|
||||||
|
|
||||||
// Our backup of the source's start, we need this
|
|
||||||
// to calculate the source's length.
|
|
||||||
const char * const srcStart = src;
|
|
||||||
|
|
||||||
// Our backup of the destination's start, we need
|
|
||||||
// this to calculate the destination's length.
|
|
||||||
const char * const dstStart = dst;
|
|
||||||
|
|
||||||
// Search the end of the destination, but do not
|
|
||||||
// move past the terminating zero.
|
|
||||||
while (size-- != 0 && *dst != 0)
|
|
||||||
++dst;
|
|
||||||
|
|
||||||
// Calculate the destination's length;
|
|
||||||
const size_t dstLength = dst - dstStart;
|
|
||||||
|
|
||||||
// In case we reached the end of the destination
|
|
||||||
// buffer before we had a chance to append any
|
|
||||||
// characters we will just return the destination
|
|
||||||
// length plus the source string's length.
|
|
||||||
if (size == 0)
|
|
||||||
return dstLength + strlen(srcStart);
|
|
||||||
|
|
||||||
// Copy over all of the source that fits
|
|
||||||
// the destination buffer. We also need
|
|
||||||
// to take the terminating zero we will
|
|
||||||
// add into consideration.
|
|
||||||
while (size-- != 0 && *src != 0)
|
|
||||||
*dst++ = *src++;
|
|
||||||
*dst = 0;
|
|
||||||
|
|
||||||
// Move to the terminating zero of the source
|
|
||||||
// string, we need this to determine the length
|
|
||||||
// of the source string.
|
|
||||||
while (*src)
|
|
||||||
++src;
|
|
||||||
|
|
||||||
// Return the total length of the result string
|
|
||||||
return dstLength + (src - srcStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // End of namespace Common
|
|
||||||
|
|
||||||
// Portable implementation of stricmp / strcasecmp / strcmpi.
|
|
||||||
// TODO: Rename this to Common::strcasecmp
|
|
||||||
int scumm_stricmp(const char *s1, const char *s2) {
|
|
||||||
byte l1, l2;
|
|
||||||
do {
|
|
||||||
// Don't use ++ inside tolower, in case the macro uses its
|
|
||||||
// arguments more than once.
|
|
||||||
l1 = (byte)*s1++;
|
|
||||||
l1 = tolower(l1);
|
|
||||||
l2 = (byte)*s2++;
|
|
||||||
l2 = tolower(l2);
|
|
||||||
} while (l1 == l2 && l1 != 0);
|
|
||||||
return l1 - l2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Portable implementation of strnicmp / strncasecmp / strncmpi.
|
|
||||||
// TODO: Rename this to Common::strncasecmp
|
|
||||||
int scumm_strnicmp(const char *s1, const char *s2, uint n) {
|
|
||||||
byte l1, l2;
|
|
||||||
do {
|
|
||||||
if (n-- == 0)
|
|
||||||
return 0; // no difference found so far -> signal equality
|
|
||||||
|
|
||||||
// Don't use ++ inside tolower, in case the macro uses its
|
|
||||||
// arguments more than once.
|
|
||||||
l1 = (byte)*s1++;
|
|
||||||
l1 = tolower(l1);
|
|
||||||
l2 = (byte)*s2++;
|
|
||||||
l2 = tolower(l2);
|
|
||||||
} while (l1 == l2 && l1 != 0);
|
|
||||||
return l1 - l2;
|
|
||||||
}
|
|
||||||
|
|
|
@ -2811,7 +2811,7 @@ static int gsc_startup_code(Common::SeekableReadStream *game_stream, int restore
|
||||||
* Display a brief loading game message; here we have to use a timeout
|
* Display a brief loading game message; here we have to use a timeout
|
||||||
* to ensure that the text is flushed to Glk.
|
* to ensure that the text is flushed to Glk.
|
||||||
*/
|
*/
|
||||||
g_vm->glk_put_string_uni(_("Loading game...\n").c_str());
|
g_vm->glk_put_string_uni(_("Loading game...\n").u32_str());
|
||||||
if (g_vm->glk_gestalt(gestalt_Timer, 0)) {
|
if (g_vm->glk_gestalt(gestalt_Timer, 0)) {
|
||||||
event_t event;
|
event_t event;
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ void GlkInterface::print(const Common::U32String &msg) {
|
||||||
// Don't print out text if loading a savegame directly from the launcher, since we don't
|
// Don't print out text if loading a savegame directly from the launcher, since we don't
|
||||||
// want any of the intro text displayed by the startup code to show
|
// want any of the intro text displayed by the startup code to show
|
||||||
if (_saveSlot == -1)
|
if (_saveSlot == -1)
|
||||||
glk_put_string_stream_uni(glk_window_get_stream(_window), msg.c_str());
|
glk_put_string_stream_uni(glk_window_get_stream(_window), msg.u32_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlkInterface::print(int number) {
|
void GlkInterface::print(int number) {
|
||||||
|
|
|
@ -139,7 +139,7 @@ void Comprehend::print(const Common::U32String fmt, ...) {
|
||||||
va_end(argp);
|
va_end(argp);
|
||||||
|
|
||||||
glk_put_string_stream_uni(glk_window_get_stream(_bottomWindow),
|
glk_put_string_stream_uni(glk_window_get_stream(_bottomWindow),
|
||||||
outputMsg.c_str());
|
outputMsg.u32_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Comprehend::readLine(char *buffer, size_t maxLen) {
|
void Comprehend::readLine(char *buffer, size_t maxLen) {
|
||||||
|
|
|
@ -60,7 +60,7 @@ GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc) :
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlkAPI::glk_exit(void) {
|
void GlkAPI::glk_exit(void) {
|
||||||
glk_put_string_uni(_("[ press any key to exit ]").c_str());
|
glk_put_string_uni(_("[ press any key to exit ]").u32_str());
|
||||||
_events->waitForPress();
|
_events->waitForPress();
|
||||||
|
|
||||||
// Trigger a ScumMVM shutdown of game
|
// Trigger a ScumMVM shutdown of game
|
||||||
|
|
|
@ -176,7 +176,7 @@ void Scott::display(winid_t w, const Common::U32String fmt, ...) {
|
||||||
Common::U32String::vformat(fmt.begin(), fmt.end(), msg, ap);
|
Common::U32String::vformat(fmt.begin(), fmt.end(), msg, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
glk_put_string_stream_uni(glk_window_get_stream(w), msg.c_str());
|
glk_put_string_stream_uni(glk_window_get_stream(w), msg.u32_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scott::delay(int seconds) {
|
void Scott::delay(int seconds) {
|
||||||
|
|
|
@ -1304,7 +1304,7 @@ void TextBufferWindow::acceptReadLine(uint32 arg) {
|
||||||
if (_historyPos < 0)
|
if (_historyPos < 0)
|
||||||
_historyPos += HISTORYLEN;
|
_historyPos += HISTORYLEN;
|
||||||
s = _history[_historyPos];
|
s = _history[_historyPos];
|
||||||
putTextUni(s.c_str(), s.size(), _inFence, _numChars - _inFence);
|
putTextUni(s.u32_str(), s.size(), _inFence, _numChars - _inFence);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case keycode_Down:
|
case keycode_Down:
|
||||||
|
@ -1314,7 +1314,7 @@ void TextBufferWindow::acceptReadLine(uint32 arg) {
|
||||||
if (_historyPos >= HISTORYLEN)
|
if (_historyPos >= HISTORYLEN)
|
||||||
_historyPos -= HISTORYLEN;
|
_historyPos -= HISTORYLEN;
|
||||||
s = _history[_historyPos];
|
s = _history[_historyPos];
|
||||||
putTextUni(s.c_str(), s.size(), _inFence, _numChars - _inFence);
|
putTextUni(s.u32_str(), s.size(), _inFence, _numChars - _inFence);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Cursor movement keys, during line input.
|
// Cursor movement keys, during line input.
|
||||||
|
|
|
@ -142,7 +142,7 @@ Common::Error ZCode::saveGameState(int slot, const Common::String &desc, bool is
|
||||||
file->close();
|
file->close();
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
print_string_uni(_("Error writing save file\n").c_str());
|
print_string_uni(_("Error writing save file\n").u32_str());
|
||||||
|
|
||||||
return Common::kNoError;
|
return Common::kNoError;
|
||||||
|
|
||||||
|
|
|
@ -475,7 +475,7 @@ void PopUpWidget::handleMouseWheel(int x, int y, int direction) {
|
||||||
|
|
||||||
// Skip separator entries
|
// Skip separator entries
|
||||||
while ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
|
while ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
|
||||||
_entries[newSelection].name.equals("")) {
|
_entries[newSelection].name.empty()) {
|
||||||
newSelection += direction;
|
newSelection += direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue