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:
Vladimir Serbinenko 2020-10-27 23:22:25 +01:00 committed by Eugene Sandulenko
parent a286db2d3c
commit 29cfa7bb0f
31 changed files with 1216 additions and 3378 deletions

View file

@ -57,5 +57,5 @@ Common::String MusicDevice::getCompleteId() {
}
MidiDriver::DeviceHandle MusicDevice::getHandle() {
return (MidiDriver::DeviceHandle)Common::hashit(getCompleteId());
return (MidiDriver::DeviceHandle)getCompleteId().hash();
}

803
common/base-str.cpp Normal file
View 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
View 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

View file

@ -30,7 +30,6 @@ namespace Common {
uint hashit(const char *str);
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()); }
// FIXME: The following functors obviously are not consistently named
@ -40,7 +39,7 @@ struct CaseSensitiveString_EqualTo {
};
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<>
struct Hash<String> {
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();
}
};

View file

@ -3,6 +3,7 @@ MODULE := common
MODULE_OBJS := \
achievements.o \
archive.o \
base-str.o \
config-manager.o \
coroutines.o \
dcl.o \

View file

@ -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>
void syncArray(T *arr, size_t entries, void (*serializer)(Serializer &, T &), Version minVersion = 0, Version maxVersion = kLastVersion) {
if (_version < minVersion || _version > maxVersion)

View file

@ -29,95 +29,8 @@
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)
: _size(0), _str(_storage) {
: BaseString() {
_storage[0] = c;
_storage[1] = 0;
@ -125,214 +38,45 @@ String::String(char c)
_size = (c == 0) ? 0 : 1;
}
#ifndef SCUMMVM_UTIL
String::String(const U32String &str)
: _size(0), _str(_storage) {
: BaseString() {
_storage[0] = 0;
*this = String(str.encode());
}
#endif
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;
// 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) {
uint32 len = strlen(str);
ensureCapacity(len, false);
_size = len;
memmove(_str, str, len + 1);
assign(str);
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;
}
assign(str);
return *this;
}
String &String::operator=(char c) {
decRefCount(_extern._refCount);
_str = _storage;
_str[0] = c;
_str[1] = 0;
_size = (c == 0) ? 0 : 1;
assign(c);
return *this;
}
String &String::operator+=(const char *str) {
if (pointerInOwnBuffer(str))
return operator+=(String(str));
int len = strlen(str);
if (len > 0) {
ensureCapacity(_size + len, true);
memcpy(_str + _size, str, len + 1);
_size += len;
}
assignAppend(str);
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) {
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;
}
assignAppend(str);
return *this;
}
String &String::operator+=(char c) {
ensureCapacity(_size + 1, true);
_str[_size++] = c;
_str[_size] = 0;
assignAppend(c);
return *this;
}
@ -432,45 +176,7 @@ bool String::contains(uint32 x) const {
return false;
}
uint64 String::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;
}
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;
}
#ifndef SCUMMVM_UTIL
bool String::matchString(const char *pat, bool ignoreCase, bool pathMode) const {
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);
}
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) {
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());
}
#endif
void String::replace(uint32 pos, uint32 count, const String &str) {
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,
uint32 posDest, uint32 countDest) {
ensureCapacity(_size + countDest - countOri, true);
// Prepare string for the replaced text.
if (countOri < countDest) {
uint32 offset = countDest - countOri; ///< Offset to copy the characters
uint32 newSize = _size + offset;
ensureCapacity(newSize, true);
_size = newSize;
// 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){
uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back
makeUnique();
// 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];
_size -= offset;
} else {
makeUnique();
}
// 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
String String::format(const char *fmt, ...) {
String output;
@ -762,17 +309,6 @@ String String::vformat(const char *fmt, va_list args) {
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 {
int sLen = strlen(s);
@ -874,42 +410,6 @@ String String::substr(size_t pos, size_t len) const {
#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) {
return (x == y);
}
@ -920,15 +420,6 @@ bool operator!=(const char* y, const String &x) {
#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 {
return (0 == compareToIgnoreCase(x));
}
@ -938,15 +429,6 @@ bool String::equalsIgnoreCase(const char *x) const {
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 {
return compareToIgnoreCase(x.c_str());
}
@ -997,6 +479,8 @@ String operator+(const String &x, char y) {
return temp;
}
#ifndef SCUMMVM_UTIL
char *ltrim(char *t) {
while (isSpace(*t))
t++;
@ -1014,6 +498,8 @@ char *trim(char *t) {
return rtrim(ltrim(t));
}
#endif
String lastPathComponent(const String &path, const char sep) {
const char *str = path.c_str();
const char *last = str + path.size();
@ -1089,6 +575,8 @@ String normalizePath(const String &path, const char sep) {
return result;
}
#ifndef SCUMMVM_UTIL
bool matchString(const char *str, const char *pat, bool ignoreCase, bool pathMode) {
assert(str);
assert(pat);
@ -1193,6 +681,8 @@ String tag2string(uint32 tag) {
return String(str);
}
#endif
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.

View file

@ -26,6 +26,7 @@
#include "common/scummsys.h"
#include "common/str-enc.h"
#include "common/ustr.h"
#include "common/base-str.h"
#include <stdarg.h>
@ -56,84 +57,29 @@ class U32String;
* The presence of \0 characters in the string will cause undefined
* behavior in some operations.
*/
class String {
class String : public BaseString<char> {
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
* individual string characters to bigger integer types without sign
* extension happening.
*/
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. */
String() : _size(0), _str(_storage) { _storage[0] = 0; }
String() : BaseString() {}
/** 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. */
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). */
String(const char *beginP, const char *endP);
String(const char *beginP, const char *endP) : BaseString(beginP, endP) {}
/** 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. */
explicit String(char c);
@ -150,24 +96,10 @@ public:
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
int compareDictionary(const String &x) const;
int compareDictionary(const char *x) const;
@ -187,12 +119,6 @@ public:
bool contains(char 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).
* 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 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.
*
@ -326,13 +193,6 @@ public:
*/
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 */
size_t rfind(const char *s) const;
size_t rfind(const String &s) const {
@ -381,42 +241,10 @@ public:
/** Return a substring of this string */
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 **/
U32String decode(CodePage page = kUtf8) const;
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 decodeOneByte(U32String &dst, CodePage page) const;
};

View file

@ -28,53 +28,7 @@
namespace Common {
extern MemoryPool *g_refCountPool;
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) {
U32String::U32String(const char *str) : BaseString() {
if (str == nullptr) {
_storage[0] = 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);
}
U32String::U32String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
U32String::U32String(const char *beginP, const char *endP) : BaseString() {
assert(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());
}
U32String::U32String(const UnicodeBiDiText &txt) : _size(0), _str(_storage) {
initWithCStr(txt.visual.c_str(), txt.visual.size());
U32String::U32String(const UnicodeBiDiText &txt) : BaseString() {
initWithValueTypeStr(txt.visual.c_str(), txt.visual.size());
}
U32String::~U32String() {
@ -105,24 +59,7 @@ U32String::~U32String() {
}
U32String &U32String::operator=(const U32String &str) {
if (&str == this)
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;
}
assign(str);
return *this;
}
@ -166,465 +103,43 @@ U32String &U32String::operator+=(value_type c) {
return *this;
}
bool U32String::operator==(const U32String &x) const {
return equals(x);
}
bool U32String::operator==(const String &x) const {
return equals(x);
}
bool U32String::operator==(const value_type *x) const {
return equals(U32String(x));
return equalsC(x.c_str());
}
bool U32String::operator==(const char *x) const {
return equals(x);
}
bool U32String::operator!=(const U32String &x) const {
return !equals(x);
return equalsC(x);
}
bool U32String::operator!=(const String &x) const {
return !equals(x);
}
bool U32String::operator!=(const value_type *x) const {
return !equals(U32String(x));
return !equalsC(x.c_str());
}
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 temp(x);
temp += y;
return temp;
}
void U32String::wordWrap(const uint32 maxLength) {
if (_size < maxLength) {
return;
}
U32String operator+(const U32String &x, const uint32 y) {
U32String temp(x);
temp += y;
return temp;
}
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;
}
}
void U32String::insertString(const char *s, uint32 p) {
while (*s != '\0') {
BaseString::insertChar(*s++, p++);
}
}
uint64 U32String::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;
}
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);
void U32String::insertString(const String &s, uint32 p) {
for (uint32 i = 0; i < s.size(); ++i) {
BaseString::insertChar(s[i], p++);
}
}
@ -633,7 +148,7 @@ U32String U32String::format(U32String fmt, ...) {
va_list va;
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);
return output;
@ -645,13 +160,14 @@ U32String U32String::format(const char *fmt, ...) {
Common::U32String fmtU32(fmt);
va_list va;
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);
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;
char *string_temp;
@ -664,7 +180,7 @@ int U32String::vformat(U32String::const_iterator fmt, const U32String::const_ite
char buffer[512];
while (fmt != inputItrEnd) {
while (fmt != fmtEnd) {
ch = *fmt++;
if (ch == '%') {
switch (ch = *fmt++) {
@ -680,10 +196,10 @@ int U32String::vformat(U32String::const_iterator fmt, const U32String::const_ite
break;
case 's':
string_temp = va_arg(args, char *);
len = strlen(string_temp);
length += len;
tempPos = output.size();
output.insertString(string_temp, pos);
len = output.size() - tempPos;
length += len;
pos += len - 1;
break;
case 'i':

View file

@ -25,6 +25,7 @@
#include "common/scummsys.h"
#include "common/str-enc.h"
#include "common/base-str.h"
namespace Common {
@ -52,66 +53,36 @@ class UnicodeBiDiText;
* The presence of \0 characters in the string will cause undefined
* 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:
static const uint32 npos = 0xFFFFFFFF;
typedef uint32 value_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:
/** 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. */
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. */
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). */
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. */
U32String(const U32String &str);
U32String(const U32String &str) : BaseString(str) {}
/** Construct a copy of the given unicode BiDi converted string. */
U32String(const UnicodeBiDiText &txt);
@ -136,128 +107,16 @@ public:
U32String &operator=(const char *str);
U32String &operator+=(const U32String &str);
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 value_type *x) const;
bool operator==(const char *x) const;
bool operator!=(const U32String &x) const;
bool operator!=(const String &x) const;
bool operator!=(const value_type *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 **/
String encode(CodePage page = kUtf8) const;
void wordWrap(const uint32 maxLength);
uint64 asUint64() const;
void trim();
/**
* Print formatted data into a U32String object.
*/
@ -268,7 +127,7 @@ public:
* Print formatted data into a U32String object. It takes in the
* 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
@ -276,19 +135,21 @@ public:
*/
static char* itoa(int num, char* str, int base);
private:
void makeUnique();
void ensureCapacity(uint32 new_size, bool keep_old);
void incRefCount() const;
void decRefCount(int *oldRefCount);
void initWithCStr(const value_type *str, uint32 len);
void initWithCStr(const char *str, uint32 len);
using BaseString<value_type>::insertString;
void insertString(const char *s, uint32 p);
void insertString(const String &s, uint32 p);
const uint32 *u32_str() const {
return (const uint32 *) _str;
}
private:
void encodeUTF8(String &dst) const;
void encodeOneByte(String &dst, CodePage page) const;
};
U32String operator+(const U32String &x, const U32String &y);
U32String operator+(const U32String &x, U32String::value_type y);
/** @} */

View file

@ -94,7 +94,7 @@ private:
};
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 {

View file

@ -0,0 +1,2 @@
#define SCUMMVM_UTIL 1
#include "../common/base-str.cpp"

View file

@ -24,7 +24,7 @@
#define COMMON_HASH_STR_H
#include "hashmap.h"
#include "str.h"
#include "common/str.h"
namespace Common {

View file

@ -12,6 +12,7 @@ MODULE_OBJS := \
script_ranges.o \
script_responses.o \
script_states.o \
base-str.o \
str.o \
tag_maps.o \
winexe.o \

View file

@ -1,786 +1,2 @@
/* 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/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;
}
#define SCUMMVM_UTIL
#include "../common/str.cpp"

View file

@ -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

View file

@ -22,7 +22,7 @@
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "str.h"
#include "common/str.h"
#include "winexe.h"
namespace Common {

View file

@ -25,7 +25,7 @@
#include "file.h"
#include "hash-str.h"
#include "str.h"
#include "common/str.h"
namespace Common {

View file

@ -23,7 +23,7 @@
#define FORBIDDEN_SYMBOL_ALLOW_ALL
#include "file.h"
#include "str.h"
#include "common/str.h"
#include "winexe_pe.h"
#include "common/array.h"
#include "common/endian.h"

View file

@ -26,7 +26,7 @@
#include "file.h"
#include "hash-str.h"
#include "hashmap.h"
#include "str.h"
#include "common/str.h"
#include "winexe.h"
namespace Common {

View file

@ -0,0 +1,2 @@
#define SCUMMVM_UTIL 1
#include "../common/base-str.cpp"

View file

@ -9,6 +9,7 @@ MODULE_OBJS := \
map.o \
memorypool.o \
str.o \
base-str.o \
swords.o
# Set the name of the executable

View file

@ -1,786 +1,2 @@
/* 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/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;
}
#define SCUMMVM_UTIL
#include "../common/str.cpp"

View file

@ -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
* 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)) {
event_t event;

View file

@ -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
// want any of the intro text displayed by the startup code to show
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) {

View file

@ -139,7 +139,7 @@ void Comprehend::print(const Common::U32String fmt, ...) {
va_end(argp);
glk_put_string_stream_uni(glk_window_get_stream(_bottomWindow),
outputMsg.c_str());
outputMsg.u32_str());
}
void Comprehend::readLine(char *buffer, size_t maxLen) {

View file

@ -60,7 +60,7 @@ GlkAPI::GlkAPI(OSystem *syst, const GlkGameDescription &gameDesc) :
}
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();
// Trigger a ScumMVM shutdown of game

View file

@ -176,7 +176,7 @@ void Scott::display(winid_t w, const Common::U32String fmt, ...) {
Common::U32String::vformat(fmt.begin(), fmt.end(), msg, 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) {

View file

@ -1304,7 +1304,7 @@ void TextBufferWindow::acceptReadLine(uint32 arg) {
if (_historyPos < 0)
_historyPos += HISTORYLEN;
s = _history[_historyPos];
putTextUni(s.c_str(), s.size(), _inFence, _numChars - _inFence);
putTextUni(s.u32_str(), s.size(), _inFence, _numChars - _inFence);
break;
case keycode_Down:
@ -1314,7 +1314,7 @@ void TextBufferWindow::acceptReadLine(uint32 arg) {
if (_historyPos >= HISTORYLEN)
_historyPos -= HISTORYLEN;
s = _history[_historyPos];
putTextUni(s.c_str(), s.size(), _inFence, _numChars - _inFence);
putTextUni(s.u32_str(), s.size(), _inFence, _numChars - _inFence);
break;
// Cursor movement keys, during line input.

View file

@ -142,7 +142,7 @@ Common::Error ZCode::saveGameState(int slot, const Common::String &desc, bool is
file->close();
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;

View file

@ -475,7 +475,7 @@ void PopUpWidget::handleMouseWheel(int x, int y, int direction) {
// Skip separator entries
while ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
_entries[newSelection].name.equals("")) {
_entries[newSelection].name.empty()) {
newSelection += direction;
}