synced some parts of scummvm "common" code

This commit is contained in:
Pawel Kolodziejski 2009-01-13 22:25:33 +00:00
parent fb3e42fd4b
commit 6ea0539d4f
16 changed files with 998 additions and 410 deletions

View File

@ -82,7 +82,7 @@ static void debugHelper(const char *in_buf, bool caret = true) {
fflush(stdout);
}
void CDECL debug(const char *s, ...) {
void debug(const char *s, ...) {
char buf[STRINGBUFLEN];
va_list va;
@ -93,7 +93,7 @@ void CDECL debug(const char *s, ...) {
debugHelper(buf);
}
void CDECL debug(int level, const char *s, ...) {
void debug(int level, const char *s, ...) {
char buf[STRINGBUFLEN];
va_list va;
@ -107,7 +107,7 @@ void CDECL debug(int level, const char *s, ...) {
debugHelper(buf);
}
void NORETURN CDECL error(const char *s, ...) {
void NORETURN error(const char *s, ...) {
char buf_input[STRINGBUFLEN];
char buf_output[STRINGBUFLEN];
va_list va;
@ -153,7 +153,7 @@ void NORETURN CDECL error(const char *s, ...) {
exit(1);
}
void CDECL warning(const char *fmt, ...) {
void warning(const char *fmt, ...) {
char buf[STRINGBUFLEN];
va_list va;

View File

@ -55,7 +55,7 @@ extern bool ZBUFFER_GLOBAL, SHOWFPS_GLOBAL;
void warning(const char *fmt, ...);
void error(const char *fmt, ...);
void CDECL debug(int level, const char *s, ...);
void CDECL debug(const char *s, ...);
void debug(int level, const char *s, ...);
void debug(const char *s, ...);
#endif

View File

@ -29,12 +29,18 @@
namespace Common {
/**
* Generic unary function.
*/
template<class Arg, class Result>
struct UnaryFunction {
typedef Arg ArgumenType;
typedef Result ResultType;
};
/**
* Generic binary function.
*/
template<class Arg1, class Arg2, class Result>
struct BinaryFunction {
typedef Arg1 FirstArgumentType;
@ -42,16 +48,25 @@ struct BinaryFunction {
typedef Result ResultType;
};
/**
* Predicate to check for equallity of two data elements.
*/
template<class T>
struct EqualTo : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x == y; }
};
/**
* Predicate to check for x being less than y.
*/
template<class T>
struct Less : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x < y; }
};
/**
* Predicate to check for x being greater than y.
*/
template<class T>
struct Greater : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x > y; }
@ -63,15 +78,19 @@ private:
Op _op;
typename Op::FirstArgumentType _arg1;
public:
Binder1st(const Op &op, const typename Op::FirstArgumentType &arg1) : _op(op), _arg1(arg1) {}
Binder1st(const Op &op, typename Op::FirstArgumentType arg1) : _op(op), _arg1(arg1) {}
typename Op::ResultType operator()(typename Op::SecondArgumentType v) const {
return _op(_arg1, v);
}
};
template<class Op, class T>
inline Binder1st<Op> bind1st(const Op &op, const T &t) {
/**
* Transforms a binary function object into an unary function object.
* To achieve that the first parameter is bound to the passed value t.
*/
template<class Op>
inline Binder1st<Op> bind1st(const Op &op, typename Op::FirstArgumentType t) {
return Binder1st<Op>(op, t);
}
@ -81,15 +100,19 @@ private:
Op _op;
typename Op::SecondArgumentType _arg2;
public:
Binder2nd(const Op &op, const typename Op::SecondArgumentType &arg2) : _op(op), _arg2(arg2) {}
Binder2nd(const Op &op, typename Op::SecondArgumentType arg2) : _op(op), _arg2(arg2) {}
typename Op::ResultType operator()(typename Op::FirstArgumentType v) const {
return _op(v, _arg2);
}
};
template<class Op, class T>
inline Binder2nd<Op> bind2nd(const Op &op, const T &t) {
/**
* Transforms a binary function object into an unary function object.
* To achieve that the first parameter is bound to the passed value t.
*/
template<class Op>
inline Binder2nd<Op> bind2nd(const Op &op, typename Op::SecondArgumentType t) {
return Binder2nd<Op>(op, t);
}
@ -119,18 +142,24 @@ public:
}
};
/**
* Creates an unary function object from a function pointer.
*/
template<class Arg, class Result>
inline PointerToUnaryFunc<Arg, Result> ptr_fun(Result (*func)(Arg)) {
return PointerToUnaryFunc<Arg, Result>(func);
}
/**
* Creates an binary function object from a function pointer.
*/
template<class Arg1, class Arg2, class Result>
inline PointerToBinaryFunc<Arg1, Arg2, Result> ptr_fun(Result (*func)(Arg1, Arg2)) {
return PointerToBinaryFunc<Arg1, Arg2, Result>(func);
}
template<class Result, class T>
class MemFunc0 : public UnaryFunction<T*, Result> {
class MemFunc0 : public UnaryFunction<T *, Result> {
private:
Result (T::*_func)();
public:
@ -143,20 +172,20 @@ public:
};
template<class Result, class T>
class ConstMemFunc0 : public UnaryFunction<T*, Result> {
class ConstMemFunc0 : public UnaryFunction<T *, Result> {
private:
Result (T::*_func)() const;
public:
typedef Result (T::*FuncType)() const;
ConstMemFunc0(const FuncType &func) : _func(func) {}
Result operator()(T *v) const {
Result operator()(const T *v) const {
return (v->*_func)();
}
};
template<class Result, class Arg, class T>
class MemFunc1 : public BinaryFunction<T*, Arg, Result> {
class MemFunc1 : public BinaryFunction<T *, Arg, Result> {
private:
Result (T::*_func)(Arg);
public:
@ -169,40 +198,166 @@ public:
};
template<class Result, class Arg, class T>
class ConstMemFunc1 : public BinaryFunction<T*, Arg, Result> {
class ConstMemFunc1 : public BinaryFunction<T *, Arg, Result> {
private:
Result (T::*_func)(Arg) const;
public:
typedef Result (T::*FuncType)(Arg) const;
ConstMemFunc1(const FuncType &func) : _func(func) {}
Result operator()(T *v1, Arg v2) const {
Result operator()(const T *v1, Arg v2) const {
return (v1->*_func)(v2);
}
};
/**
* Creates a unary function object from a class member function pointer.
* The parameter passed to the function object is the 'this' pointer to
* be used for the function call.
*/
template<class Result, class T>
inline MemFunc0<Result, T> mem_fun(Result (T::*f)()) {
return MemFunc0<Result, T>(f);
}
/**
* Creates a unary function object from a class member function pointer.
* The parameter passed to the function object is the 'this' pointer to
* be used for the function call.
*/
template<class Result, class T>
inline ConstMemFunc0<Result, T> mem_fun(Result (T::*f)() const) {
return ConstMemFunc0<Result, T>(f);
}
/**
* Creates a binary function object from a class member function pointer.
* The first parameter passed to the function object is the 'this' pointer to
* be used for the function call.
* The second one is the parameter passed to the member function.
*/
template<class Result, class Arg, class T>
inline MemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg)) {
return MemFunc1<Result, Arg, T>(f);
}
/**
* Creates a binary function object from a class member function pointer.
* The first parameter passed to the function object is the 'this' pointer to
* be used for the function call.
* The second one is the parameter passed to the member function.
*/
template<class Result, class Arg, class T>
inline ConstMemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg) const) {
return ConstMemFunc1<Result, Arg, T>(f);
}
template<class Result, class T>
class MemFuncRef0 : public UnaryFunction<T &, Result> {
private:
Result (T::*_func)();
public:
typedef Result (T::*FuncType)();
MemFuncRef0(const FuncType &func) : _func(func) {}
Result operator()(T &v) const {
return (v.*_func)();
}
};
template<class Result, class T>
class ConstMemFuncRef0 : public UnaryFunction<T &, Result> {
private:
Result (T::*_func)() const;
public:
typedef Result (T::*FuncType)() const;
ConstMemFuncRef0(const FuncType &func) : _func(func) {}
Result operator()(const T &v) const {
return (v.*_func)();
}
};
template<class Result, class Arg, class T>
class MemFuncRef1 : public BinaryFunction<T &, Arg, Result> {
private:
Result (T::*_func)(Arg);
public:
typedef Result (T::*FuncType)(Arg);
MemFuncRef1(const FuncType &func) : _func(func) {}
Result operator()(T &v1, Arg v2) const {
return (v1.*_func)(v2);
}
};
template<class Result, class Arg, class T>
class ConstMemFuncRef1 : public BinaryFunction<T &, Arg, Result> {
private:
Result (T::*_func)(Arg) const;
public:
typedef Result (T::*FuncType)(Arg) const;
ConstMemFuncRef1(const FuncType &func) : _func(func) {}
Result operator()(const T &v1, Arg v2) const {
return (v1.*_func)(v2);
}
};
/**
* Creates a unary function object from a class member function pointer.
* The parameter passed to the function object is the object instance to
* be used for the function call. Note unlike mem_fun, it takes a reference
* as parameter. Note unlike mem_fun, it takes a reference
* as parameter.
*/
template<class Result, class T>
inline MemFuncRef0<Result, T> mem_fun_ref(Result (T::*f)()) {
return MemFuncRef0<Result, T>(f);
}
/**
* Creates a unary function object from a class member function pointer.
* The parameter passed to the function object is the object instance to
* be used for the function call. Note unlike mem_fun, it takes a reference
* as parameter.
*/
template<class Result, class T>
inline ConstMemFuncRef0<Result, T> mem_fun_Ref(Result (T::*f)() const) {
return ConstMemFuncRef0<Result, T>(f);
}
/**
* Creates a binary function object from a class member function pointer.
* The first parameter passed to the function object is the object instance to
* be used for the function call. Note unlike mem_fun, it takes a reference
* as parameter.
* The second one is the parameter passed to the member function.
*/
template<class Result, class Arg, class T>
inline MemFuncRef1<Result, Arg, T> mem_fun_ref(Result (T::*f)(Arg)) {
return MemFuncRef1<Result, Arg, T>(f);
}
/**
* Creates a binary function object from a class member function pointer.
* The first parameter passed to the function object is the object instance to
* be used for the function call. Note unlike mem_fun, it takes a reference
* as parameter.
* The second one is the parameter passed to the member function.
*/
template<class Result, class Arg, class T>
inline ConstMemFuncRef1<Result, Arg, T> mem_fun_ref(Result (T::*f)(Arg) const) {
return ConstMemFuncRef1<Result, Arg, T>(f);
}
// functor code
/**
* Generic functor object for function objects without parameters.
*
* @see Functor1
*/
template<class Res>
struct Functor0 {
virtual ~Functor0() {}
@ -211,6 +366,18 @@ struct Functor0 {
virtual Res operator()() const = 0;
};
/**
* Functor object for a class member function without parameter.
*
* Example creation:
*
* Foo bar;
* Functor0Men<void, Foo> myFunctor(&bar, &Foo::myFunc);
*
* Example usage:
*
* myFunctor();
*/
template<class Res, class T>
class Functor0Mem : public Functor0<Res> {
public:
@ -218,7 +385,7 @@ public:
Functor0Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
bool isValid() const { return _func != 0; }
bool isValid() const { return _func != 0 && _t != 0; }
Res operator()() const {
return (_t->*_func)();
}
@ -227,6 +394,38 @@ private:
const FuncType _func;
};
/**
* Generic functor object for unary function objects.
*
* A typical usage for an unary function object is for executing opcodes
* in a script interpreter. To achieve that one can create an Common::Array
* object with 'Functor1<Arg, Res> *' as type. Now after the right engine version
* has been determined and the opcode table to use is found one could easily
* add the opcode implementations like this:
*
* Common::Array<Functor1<ScriptState, void> *> opcodeTable;
* opcodeTable[0] = new Functor1Mem<ScriptState, void, MyEngine_v1>(&myEngine, &MyEngine_v1::o1_foo);
* opcodeTable[1] = new Functor1Mem<ScriptState, void, MyEngine_v2>(&myEngine, &MyEngine_v2::o2_foo);
* // unimplemented/unused opcode
* opcodeTable[2] = 0;
* etc.
*
* This makes it easy to add member functions of different classes as
* opcode functions to the function table. Since with the generic
* Functor1<ScriptState, void> object the only requirement for an
* function to be used is 'ScriptState' as argument and 'void' as return
* value.
*
* Now for calling the opcodes one has simple to do:
* if (opcodeTable[opcodeNum] && opcodeTable[opcodeNum]->isValid())
* (*opcodeTable[opcodeNum])(scriptState);
* else
* warning("Unimplemented opcode %d", opcodeNum);
*
* If you want to see an real world example check the kyra engine.
* Files: engines/kyra/script.cpp and .h and engine/kyra/script_*.cpp
* are interesting for that matter.
*/
template<class Arg, class Res>
struct Functor1 : public Common::UnaryFunction<Arg, Res> {
virtual ~Functor1() {}
@ -235,6 +434,13 @@ struct Functor1 : public Common::UnaryFunction<Arg, Res> {
virtual Res operator()(Arg) const = 0;
};
/**
* Functor object for an unary class member function.
* Usage is like with Functor0Mem. The resulting functor object
* will take one parameter though.
*
* @see Functor0Men
*/
template<class Arg, class Res, class T>
class Functor1Mem : public Functor1<Arg, Res> {
public:
@ -242,7 +448,7 @@ public:
Functor1Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
bool isValid() const { return _func != 0; }
bool isValid() const { return _func != 0 && _t != 0; }
Res operator()(Arg v1) const {
return (_t->*_func)(v1);
}
@ -251,6 +457,11 @@ private:
const FuncType _func;
};
/**
* Generic functor object for binary function objects.
*
* @see Functor1
*/
template<class Arg1, class Arg2, class Res>
struct Functor2 : public Common::BinaryFunction<Arg1, Arg2, Res> {
virtual ~Functor2() {}
@ -259,6 +470,13 @@ struct Functor2 : public Common::BinaryFunction<Arg1, Arg2, Res> {
virtual Res operator()(Arg1, Arg2) const = 0;
};
/**
* Functor object for a binary class member function.
* Usage is like with Functor0Mem. The resulting functor object
* will take two parameter though.
*
* @see Functor0Men
*/
template<class Arg1, class Arg2, class Res, class T>
class Functor2Mem : public Functor2<Arg1, Arg2, Res> {
public:
@ -266,7 +484,7 @@ public:
Functor2Mem(T *t, const FuncType &func) : _t(t), _func(func) {}
bool isValid() const { return _func != 0; }
bool isValid() const { return _func != 0 && _t != 0; }
Res operator()(Arg1 v1, Arg2 v2) const {
return (_t->*_func)(v1, v2);
}

View File

@ -39,7 +39,7 @@ inline uint hashit_lower(const String &str) { return hashit_lower(str.c_str());
// FIXME: The following functors obviously are not consistently named
struct CaseSensitiveString_EqualTo {
bool operator()(const String& x, const String& y) const { return strcmp(x.c_str(), y.c_str()) == 0; }
bool operator()(const String& x, const String& y) const { return x.equals(y); }
};
struct CaseSensitiveString_Hash {
@ -48,7 +48,7 @@ struct CaseSensitiveString_Hash {
struct IgnoreCase_EqualTo {
bool operator()(const String& x, const String& y) const { return strcasecmp(x.c_str(), y.c_str()) == 0; }
bool operator()(const String& x, const String& y) const { return x.equalsIgnoreCase(y); }
};
struct IgnoreCase_Hash {
@ -77,7 +77,6 @@ struct Hash<const char *> {
}
};
// String map -- by default case insensitive
typedef HashMap<String, String, IgnoreCase_Hash, IgnoreCase_EqualTo> StringMap;

View File

@ -24,70 +24,86 @@
*/
// The hash map (associative array) implementation in this file is
// based on code by Andrew Y. Ng, 1996:
/*
* Copyright (c) 1998-2003 Massachusetts Institute of Technology.
* This code was developed as part of the Haystack research project
* (http://haystack.lcs.mit.edu/). Permission is hereby granted,
* free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
// based on the PyDict implementation of CPython. The erase() method
// is based on example code in the Wikipedia article on Hash tables.
#include "common/hashmap.h"
namespace Common {
// const char *:
// Hash function for strings, taken from CPython.
uint hashit(const char *p) {
uint hash = 0;
uint hash = *p << 7;
byte c;
while ((c = *p++))
hash = (hash * 31 + c);
return hash;
int size = 0;
while ((c = *p++)) {
hash = (1000003 * hash) ^ c;
size++;
}
return hash ^ size;
}
// Like hashit, but converts every char to lowercase before hashing.
uint hashit_lower(const char *p) {
uint hash = 0;
uint hash = tolower(*p) << 7;
byte c;
while ((c = *p++))
hash = (hash * 31 + tolower(c));
return hash;
int size = 0;
while ((c = *p++)) {
hash = (1000003 * hash) ^ tolower(c);
size++;
}
return hash ^ size;
}
// The following table is taken from the GNU ISO C++ Library's hashtable.h file.
static const uint primes[] = {
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
#ifdef DEBUG_HASH_COLLISIONS
static double
g_collisions = 0,
g_lookups = 0,
g_collPerLook = 0,
g_capacity = 0,
g_size = 0;
static int g_max_capacity = 0, g_max_size = 0;
static int g_totalHashmaps = 0;
static int g_stats[4] = {0,0,0,0};
uint nextTableSize(uint x) {
int i = 0;
while (x >= primes[i])
i++;
return primes[i];
void updateHashCollisionStats(int collisions, int lookups, int arrsize, int nele) {
g_collisions += collisions;
g_lookups += lookups;
if (lookups)
g_collPerLook += (double)collisions / (double)lookups;
g_capacity += arrsize;
g_size += nele;
g_totalHashmaps++;
if (3*nele <= 2*8)
g_stats[0]++;
if (3*nele <= 2*16)
g_stats[1]++;
if (3*nele <= 2*32)
g_stats[2]++;
if (3*nele <= 2*64)
g_stats[3]++;
g_max_capacity = MAX(g_max_capacity, arrsize);
g_max_size = MAX(g_max_size, nele);
fprintf(stdout, "%d hashmaps: colls %.1f; lookups %.1f; ratio %.3f%%; size %f (max: %d); capacity %f (max: %d)\n",
g_totalHashmaps,
g_collisions / g_totalHashmaps,
g_lookups / g_totalHashmaps,
100 * g_collPerLook / g_totalHashmaps,
g_size / g_totalHashmaps, g_max_size,
g_capacity / g_totalHashmaps, g_max_capacity);
fprintf(stdout, " %d less than %d; %d less than %d; %d less than %d; %d less than %d\n",
g_stats[0], 2*8/3,
g_stats[1],2*16/3,
g_stats[2],2*32/3,
g_stats[3],2*64/3);
// TODO:
// * Should record the maximal size of the map during its lifetime, not that at its death
// * Should do some statistics: how many maps are less than 2/3*8, 2/3*16, 2/3*32, ...
}
#endif
} // End of namespace Common

View File

@ -51,6 +51,10 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
// The hash map (associative array) implementation in this file is
// based on the PyDict implementation of CPython. The erase() method
// is based on example code in the Wikipedia article on Hash tables.
#ifndef COMMON_HASHMAP_H
#define COMMON_HASHMAP_H
@ -58,29 +62,13 @@
#include "common/str.h"
#include "common/util.h"
// FIXME: Since this define is very system dependant,
// it should be moved to the appropriate H file instead.
// Portdefs might be a good location for example
#if !defined(__SYMBIAN32__)
#define USE_HASHMAP_MEMORY_POOL
#endif
#ifdef USE_HASHMAP_MEMORY_POOL
#include "common/memorypool.h"
// FIXME: we sadly can't assume standard C++ to be present
// on every system we support, so we should get rid of this.
// The solution should be to write a simple placement new
// on our own.
#include <new>
#endif
namespace Common {
// The table sizes ideally are primes. We use a helper function to find
// suitable table sizes.
uint nextTableSize(uint x);
// Enable the following #define if you want to check how many collisions the
// code produces (many collisions indicate either a bad hash function, or a
// hash table that is too small).
@ -115,31 +103,35 @@ public:
Node(const Key &key) : _key(key), _value() {}
};
enum {
HASHMAP_PERTURB_SHIFT = 5,
HASHMAP_MIN_CAPACITY = 16,
#ifdef USE_HASHMAP_MEMORY_POOL
MemoryPool _nodePool;
// The quotient of the next two constants controls how much the
// internal storage of the hashmap may fill up before being
// increased automatically.
// Note: the quotient of these two must be between and different
// from 0 and 1.
HASHMAP_LOADFACTOR_NUMERATOR = 2,
HASHMAP_LOADFACTOR_DENOMINATOR = 3,
HASHMAP_MEMORYPOOL_SIZE = HASHMAP_MIN_CAPACITY * HASHMAP_LOADFACTOR_NUMERATOR / HASHMAP_LOADFACTOR_DENOMINATOR
};
ObjectPool<Node, HASHMAP_MEMORYPOOL_SIZE> _nodePool;
Node *allocNode(const Key &key) {
void* mem = _nodePool.malloc();
return new (mem) Node(key);
}
return new (_nodePool) Node(key);
}
void freeNode(Node *node) {
node->~Node();
_nodePool.free(node);
_nodePool.deleteChunk(node);
}
#else
Node* allocNode(const Key &key) {
return new Node(key);
}
void freeNode(Node *node) {
delete node;
}
#endif
Node **_arr; // hashtable of size arrsize.
uint _arrsize, _nele;
Node **_storage; // hashtable of size arrsize.
uint _mask; /**< Capacity of the HashMap minus one; must be a power of two of minus one */
uint _size;
HashFunc _hash;
EqualFunc _equal;
@ -154,7 +146,7 @@ public:
void assign(const HM_t &map);
int lookup(const Key &key) const;
int lookupAndCreateIfMissing(const Key &key);
void expand_array(uint newsize);
void expandStorage(uint newCapacity);
template<class T> friend class IteratorImpl;
@ -176,8 +168,8 @@ public:
NodeType *deref() const {
assert(_hashmap != 0);
assert(_idx < _hashmap->_arrsize);
Node *node = _hashmap->_arr[_idx];
assert(_idx <= _hashmap->_mask);
Node *node = _hashmap->_storage[_idx];
assert(node != 0);
return node;
}
@ -197,8 +189,8 @@ public:
assert(_hashmap);
do {
_idx++;
} while (_idx < _hashmap->_arrsize && _hashmap->_arr[_idx] == 0);
if (_idx >= _hashmap->_arrsize)
} while (_idx <= _hashmap->_mask && _hashmap->_storage[_idx] == 0);
if (_idx > _hashmap->_mask)
_idx = (uint)-1;
return *this;
@ -225,7 +217,7 @@ public:
// Remove the previous content and ...
clear();
delete[] _arr;
delete[] _storage;
// ... copy the new stuff.
assign(map);
return *this;
@ -244,12 +236,12 @@ public:
void erase(const Key &key);
uint size() const { return _nele; }
uint size() const { return _size; }
iterator begin() {
// Find and return the _key non-empty entry
for (uint ctr = 0; ctr < _arrsize; ++ctr) {
if (_arr[ctr])
for (uint ctr = 0; ctr <= _mask; ++ctr) {
if (_storage[ctr])
return iterator(ctr, this);
}
return end();
@ -260,8 +252,8 @@ public:
const_iterator begin() const {
// Find and return the first non-empty entry
for (uint ctr = 0; ctr < _arrsize; ++ctr) {
if (_arr[ctr])
for (uint ctr = 0; ctr <= _mask; ++ctr) {
if (_storage[ctr])
return const_iterator(ctr, this);
}
return end();
@ -272,14 +264,14 @@ public:
iterator find(const Key &key) {
uint ctr = lookup(key);
if (_arr[ctr])
if (_storage[ctr])
return iterator(ctr, this);
return end();
}
const_iterator find(const Key &key) const {
uint ctr = lookup(key);
if (_arr[ctr])
if (_storage[ctr])
return const_iterator(ctr, this);
return end();
}
@ -287,7 +279,7 @@ public:
// TODO: insert() method?
bool empty() const {
return (_nele == 0);
return (_size == 0);
}
};
@ -299,16 +291,13 @@ public:
*/
template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap() :
#ifdef USE_HASHMAP_MEMORY_POOL
_nodePool(sizeof(Node)),
#endif
_defaultVal() {
_arrsize = nextTableSize(0);
_arr = new Node *[_arrsize];
assert(_arr != NULL);
memset(_arr, 0, _arrsize * sizeof(Node *));
_mask = HASHMAP_MIN_CAPACITY - 1;
_storage = new Node *[HASHMAP_MIN_CAPACITY];
assert(_storage != NULL);
memset(_storage, 0, HASHMAP_MIN_CAPACITY * sizeof(Node *));
_nele = 0;
_size = 0;
#ifdef DEBUG_HASH_COLLISIONS
_collisions = 0;
@ -322,10 +311,7 @@ HashMap<Key, Val, HashFunc, EqualFunc>::HashMap() :
* to heap buffers for the internal storage.
*/
template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap(const HM_t &map) :
#ifdef USE_HASHMAP_MEMORY_POOL
_nodePool(sizeof(Node)),
#endif
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap(const HM_t &map) :
_defaultVal() {
assign(map);
}
@ -335,11 +321,15 @@ HashMap<Key, Val, HashFunc, EqualFunc>::HashMap(const HM_t &map) :
*/
template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::~HashMap() {
for (uint ctr = 0; ctr < _arrsize; ++ctr)
if (_arr[ctr] != NULL)
freeNode(_arr[ctr]);
for (uint ctr = 0; ctr <= _mask; ++ctr)
if (_storage[ctr] != NULL)
freeNode(_storage[ctr]);
delete[] _arr;
delete[] _storage;
#ifdef DEBUG_HASH_COLLISIONS
extern void updateHashCollisionStats(int, int, int, int);
updateHashCollisionStats(_collisions, _lookups, _mask+1, _size);
#endif
}
/**
@ -351,95 +341,102 @@ HashMap<Key, Val, HashFunc, EqualFunc>::~HashMap() {
*/
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::assign(const HM_t &map) {
_arrsize = map._arrsize;
_arr = new Node *[_arrsize];
assert(_arr != NULL);
memset(_arr, 0, _arrsize * sizeof(Node *));
_mask = map._mask;
_storage = new Node *[_mask+1];
assert(_storage != NULL);
memset(_storage, 0, (_mask+1) * sizeof(Node *));
// Simply clone the map given to us, one by one.
_nele = 0;
for (uint ctr = 0; ctr < _arrsize; ++ctr) {
if (map._arr[ctr] != NULL) {
_arr[ctr] = allocNode(map._arr[ctr]->_key);
_arr[ctr]->_value = map._arr[ctr]->_value;
_nele++;
_size = 0;
for (uint ctr = 0; ctr <= _mask; ++ctr) {
if (map._storage[ctr] != NULL) {
_storage[ctr] = allocNode(map._storage[ctr]->_key);
_storage[ctr]->_value = map._storage[ctr]->_value;
_size++;
}
}
// Perform a sanity check (to help track down hashmap corruption)
assert(_nele == map._nele);
assert(_size == map._size);
}
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::clear(bool shrinkArray) {
for (uint ctr = 0; ctr < _arrsize; ++ctr) {
if (_arr[ctr] != NULL) {
freeNode(_arr[ctr]);
_arr[ctr] = NULL;
for (uint ctr = 0; ctr <= _mask; ++ctr) {
if (_storage[ctr] != NULL) {
freeNode(_storage[ctr]);
_storage[ctr] = NULL;
}
}
if (shrinkArray && _arrsize > nextTableSize(0)) {
delete[] _arr;
#ifdef USE_HASHMAP_MEMORY_POOL
_nodePool.freeUnusedPages();
#endif
_arrsize = nextTableSize(0);
_arr = new Node *[_arrsize];
assert(_arr != NULL);
memset(_arr, 0, _arrsize * sizeof(Node *));
if (shrinkArray && _mask >= HASHMAP_MIN_CAPACITY) {
delete[] _storage;
_mask = HASHMAP_MIN_CAPACITY;
_storage = new Node *[HASHMAP_MIN_CAPACITY];
assert(_storage != NULL);
memset(_storage, 0, HASHMAP_MIN_CAPACITY * sizeof(Node *));
}
_nele = 0;
_size = 0;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::expand_array(uint newsize) {
assert(newsize > _arrsize);
uint ctr, dex;
void HashMap<Key, Val, HashFunc, EqualFunc>::expandStorage(uint newCapacity) {
assert(newCapacity > _mask+1);
const uint old_nele = _nele;
const uint old_arrsize = _arrsize;
Node **old_arr = _arr;
const uint old_size = _size;
const uint old_mask = _mask;
Node **old_storage = _storage;
// allocate a new array
_nele = 0;
_arrsize = newsize;
_arr = new Node *[_arrsize];
assert(_arr != NULL);
memset(_arr, 0, _arrsize * sizeof(Node *));
_size = 0;
_mask = newCapacity - 1;
_storage = new Node *[newCapacity];
assert(_storage != NULL);
memset(_storage, 0, newCapacity * sizeof(Node *));
// rehash all the old elements
for (ctr = 0; ctr < old_arrsize; ++ctr) {
if (old_arr[ctr] == NULL)
for (uint ctr = 0; ctr <= old_mask; ++ctr) {
if (old_storage[ctr] == NULL)
continue;
// Insert the element from the old table into the new table.
// Since we know that no key exists twice in the old table, we
// can do this slightly better than by calling lookup, since we
// don't have to call _equal().
dex = _hash(old_arr[ctr]->_key) % _arrsize;
while (_arr[dex] != NULL) {
dex = (dex + 1) % _arrsize;
const uint hash = _hash(old_storage[ctr]->_key);
uint idx = hash & _mask;
for (uint perturb = hash; _storage[idx] != NULL; perturb >>= HASHMAP_PERTURB_SHIFT) {
idx = (5 * idx + perturb + 1) & _mask;
}
_arr[dex] = old_arr[ctr];
_nele++;
_storage[idx] = old_storage[ctr];
_size++;
}
// Perform a sanity check: Old number of elements should match the new one!
// This check will fail if some previous operation corrupted this hashmap.
assert(_nele == old_nele);
assert(_size == old_size);
delete[] old_arr;
delete[] old_storage;
return;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
int HashMap<Key, Val, HashFunc, EqualFunc>::lookup(const Key &key) const {
uint ctr = _hash(key) % _arrsize;
const uint hash = _hash(key);
uint ctr = hash & _mask;
for (uint perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) {
if (_storage[ctr] == NULL || _equal(_storage[ctr]->_key, key))
break;
while (_arr[ctr] != NULL && !_equal(_arr[ctr]->_key, key)) {
ctr = (ctr + 1) % _arrsize;
ctr = (5 * ctr + perturb + 1) & _mask;
#ifdef DEBUG_HASH_COLLISIONS
_collisions++;
@ -450,7 +447,7 @@ int HashMap<Key, Val, HashFunc, EqualFunc>::lookup(const Key &key) const {
_lookups++;
fprintf(stderr, "collisions %d, lookups %d, ratio %f in HashMap %p; size %d num elements %d\n",
_collisions, _lookups, ((double) _collisions / (double)_lookups),
(const void *)this, _arrsize, _nele);
(const void *)this, _mask+1, _size);
#endif
return ctr;
@ -460,13 +457,15 @@ template<class Key, class Val, class HashFunc, class EqualFunc>
int HashMap<Key, Val, HashFunc, EqualFunc>::lookupAndCreateIfMissing(const Key &key) {
uint ctr = lookup(key);
if (_arr[ctr] == NULL) {
_arr[ctr] = allocNode(key);
_nele++;
if (_storage[ctr] == NULL) {
_storage[ctr] = allocNode(key);
_size++;
// Keep the load factor below 75%.
if (_nele > _arrsize * 75 / 100) {
expand_array(nextTableSize(_arrsize));
// Keep the load factor below a certain threshold.
uint capacity = _mask + 1;
if (_size * HASHMAP_LOADFACTOR_DENOMINATOR > capacity * HASHMAP_LOADFACTOR_NUMERATOR) {
capacity = capacity < 500 ? (capacity * 4) : (capacity * 2);
expandStorage(capacity);
ctr = lookup(key);
}
}
@ -478,7 +477,7 @@ int HashMap<Key, Val, HashFunc, EqualFunc>::lookupAndCreateIfMissing(const Key &
template<class Key, class Val, class HashFunc, class EqualFunc>
bool HashMap<Key, Val, HashFunc, EqualFunc>::contains(const Key &key) const {
uint ctr = lookup(key);
return (_arr[ctr] != NULL);
return (_storage[ctr] != NULL);
}
template<class Key, class Val, class HashFunc, class EqualFunc>
@ -494,15 +493,15 @@ const Val &HashMap<Key, Val, HashFunc, EqualFunc>::operator[](const Key &key) co
template<class Key, class Val, class HashFunc, class EqualFunc>
Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) {
uint ctr = lookupAndCreateIfMissing(key);
assert(_arr[ctr] != NULL);
return _arr[ctr]->_value;
assert(_storage[ctr] != NULL);
return _storage[ctr]->_value;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) const {
uint ctr = lookup(key);
if (_arr[ctr] != NULL)
return _arr[ctr]->_value;
if (_storage[ctr] != NULL)
return _storage[ctr]->_value;
else
return _defaultVal;
}
@ -510,38 +509,50 @@ const Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) const
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::setVal(const Key &key, const Val &val) {
uint ctr = lookupAndCreateIfMissing(key);
assert(_arr[ctr] != NULL);
_arr[ctr]->_value = val;
assert(_storage[ctr] != NULL);
_storage[ctr]->_value = val;
}
template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::erase(const Key &key) {
// This is based on code in the Wikipedia article on Hash tables.
uint i = lookup(key);
if (_arr[i] == NULL)
const uint hash = _hash(key);
uint i = hash & _mask;
uint perturb;
for (perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) {
if (_storage[i] == NULL || _equal(_storage[i]->_key, key))
break;
i = (5 * i + perturb + 1) & _mask;
}
if (_storage[i] == NULL)
return; // key wasn't present, so no work has to be done
// If we remove a key, we must check all subsequent keys and possibly
// reinsert them.
uint j = i;
freeNode(_arr[i]);
_arr[i] = NULL;
while (true) {
freeNode(_storage[i]);
_storage[i] = NULL;
for (perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) {
// Look at the next table slot
j = (j + 1) % _arrsize;
j = (5 * j + perturb + 1) & _mask;
// If the next slot is empty, we are done
if (_arr[j] == NULL)
if (_storage[j] == NULL)
break;
// Compute the slot where the content of the next slot should normally be,
// assuming an empty table, and check whether we have to move it.
uint k = _hash(_arr[j]->_key) % _arrsize;
uint k = _hash(_storage[j]->_key) & _mask;
if ((j > i && (k <= i || k > j)) ||
(j < i && (k <= i && k > j)) ) {
_arr[i] = _arr[j];
_storage[i] = _storage[j];
i = j;
}
}
_arr[i] = NULL;
_nele--;
_storage[i] = NULL;
_size--;
return;
}

View File

@ -182,7 +182,7 @@ enum KeyCode {
};
/**
* List of certan special and some fake 'ascii' values used in keyboard events.
* List of certain special and some fake 'ascii' values used in keyboard events.
* The values for the function keys listed here are based on what certain SCUMM
* games expect in their scripts.
* @todo Get rid of the function key values, and instead enforce that engines use
@ -259,6 +259,10 @@ struct KeyState {
keycode = KEYCODE_INVALID;
ascii = flags = 0;
}
bool operator ==(const KeyState &x) const {
return keycode == x.keycode && ascii == x.ascii && flags == x.flags;
}
};
} // End of namespace Common

View File

@ -210,6 +210,11 @@ public:
++i;
}
void pop_front() {
iterator i = begin();
i = erase(i);
}
List<t_T> &operator=(const List<t_T> &list) {
if (this != &list) {

View File

@ -28,21 +28,10 @@
namespace Common {
static const size_t CHUNK_PAGE_SIZE = 32;
enum {
INITIAL_CHUNKS_PER_PAGE = 8
};
void* MemoryPool::allocPage() {
void* result = ::malloc(CHUNK_PAGE_SIZE * _chunkSize);
_pages.push_back(result);
void* current = result;
for (size_t i = 1; i < CHUNK_PAGE_SIZE; ++i) {
void* next = ((char*)current + _chunkSize);
*(void**)current = next;
current = next;
}
*(void**)current = NULL;
return result;
}
MemoryPool::MemoryPool(size_t chunkSize) {
// You must at least fit the pointer in the node (technically unneeded considering the next rounding statement)
@ -52,38 +41,71 @@ MemoryPool::MemoryPool(size_t chunkSize) {
_chunkSize = (_chunkSize + sizeof(void*) - 1) & (~(sizeof(void*) - 1));
_next = NULL;
_chunksPerPage = INITIAL_CHUNKS_PER_PAGE;
}
MemoryPool::~MemoryPool() {
for (size_t i = 0; i<_pages.size(); ++i)
::free(_pages[i]);
for (size_t i = 0; i < _pages.size(); ++i)
::free(_pages[i].start);
}
void* MemoryPool::malloc() {
#if 1
if (!_next)
_next = allocPage();
void MemoryPool::allocPage() {
Page page;
void* result = _next;
// Allocate a new page
page.numChunks = _chunksPerPage;
assert(page.numChunks * _chunkSize < 16*1024*1024); // Refuse to allocate pages bigger than 16 MB
page.start = ::malloc(page.numChunks * _chunkSize);
assert(page.start);
_pages.push_back(page);
// Next time, we'll alocate a page twice as big as this one.
_chunksPerPage *= 2;
// Add the page to the pool of free chunk
addPageToPool(page);
}
void MemoryPool::addPageToPool(const Page &page) {
// Add all chunks of the new page to the linked list (pool) of free chunks
void *current = page.start;
for (size_t i = 1; i < page.numChunks; ++i) {
void *next = ((char*)current + _chunkSize);
*(void **)current = next;
current = next;
}
// Last chunk points to the old _next
*(void**)current = _next;
// From now on, the first free chunk is the first chunk of the new page
_next = page.start;
}
void *MemoryPool::allocChunk() {
if (!_next) // No free chunks left? Allocate a new page
allocPage();
assert(_next);
void *result = _next;
_next = *(void**)result;
return result;
#else
return ::malloc(_chunkSize);
#endif
}
void MemoryPool::free(void* ptr) {
#if 1
void MemoryPool::freeChunk(void *ptr) {
// Add the chunk back to (the start of) the list of free chunks
*(void**)ptr = _next;
_next = ptr;
#else
::free(ptr);
#endif
}
// Technically not compliant C++ to compare unrelated pointers. In practice...
bool MemoryPool::isPointerInPage(void* ptr, void* page) {
return (ptr >= page) && (ptr < (char*)page + CHUNK_PAGE_SIZE * _chunkSize);
bool MemoryPool::isPointerInPage(void *ptr, const Page &page) {
return (ptr >= page.start) && (ptr < (char*)page.start + page.numChunks * _chunkSize);
}
void MemoryPool::freeUnusedPages() {
@ -94,9 +116,10 @@ void MemoryPool::freeUnusedPages() {
numberOfFreeChunksPerPage[i] = 0;
}
void* iterator = _next;
// Compute for each page how many chunks in it are still in use.
void *iterator = _next;
while (iterator) {
// This should be a binary search
// TODO: This should be a binary search (requiring us to keep _pages sorted)
for (size_t i = 0; i < _pages.size(); ++i) {
if (isPointerInPage(iterator, _pages[i])) {
++numberOfFreeChunksPerPage[i];
@ -106,16 +129,41 @@ void MemoryPool::freeUnusedPages() {
iterator = *(void**)iterator;
}
// Free all pages which are not in use.
size_t freedPagesCount = 0;
for (size_t i = 0; i < _pages.size(); ++i) {
if (numberOfFreeChunksPerPage[i] == CHUNK_PAGE_SIZE) {
::free(_pages[i]);
_pages[i] = NULL; // TODO : Remove NULL values
for (size_t i = 0; i < _pages.size(); ++i) {
if (numberOfFreeChunksPerPage[i] == _pages[i].numChunks) {
// Remove all chunks of this page from the list of free chunks
void **iter2 = &_next;
while (*iter2) {
if (isPointerInPage(*iter2, _pages[i]))
*iter2 = **(void***)iter2;
else
iter2 = *(void***)iter2;
}
::free(_pages[i].start);
++freedPagesCount;
_pages[i].start = NULL;
}
}
//printf("%d freed pages\n", freedPagesCount);
// printf("freed %d pages out of %d\n", (int)freedPagesCount, (int)_pages.size());
for (size_t i = 0; i < _pages.size(); ) {
if (_pages[i].start == NULL) {
_pages.remove_at(i);
// We just removed an entry, so we do not advance "i"
} else {
++i;
}
}
// Reset _chunksPerPage
_chunksPerPage = INITIAL_CHUNKS_PER_PAGE;
for (size_t i = 0; i < _pages.size(); ++i) {
if (_chunksPerPage < _pages[i].numChunks)
_chunksPerPage = _pages[i].numChunks;
}
}
} // End of namespace Common

View File

@ -32,26 +32,86 @@
namespace Common {
class MemoryPool {
private:
protected:
MemoryPool(const MemoryPool&);
MemoryPool& operator=(const MemoryPool&);
size_t _chunkSize;
Array<void*> _pages;
void* _next;
struct Page {
void *start;
size_t numChunks;
};
size_t _chunkSize;
Array<Page> _pages;
void *_next;
size_t _chunksPerPage;
void allocPage();
void addPageToPool(const Page &page);
bool isPointerInPage(void *ptr, const Page &page);
void* allocPage();
bool isPointerInPage(void* ptr, void* page);
public:
MemoryPool(size_t chunkSize);
~MemoryPool();
void* malloc();
void free(void* ptr);
void *allocChunk();
void freeChunk(void *ptr);
void freeUnusedPages();
size_t getChunkSize() const { return _chunkSize; }
};
template<size_t CHUNK_SIZE, size_t NUM_INTERNAL_CHUNKS = 32>
class FixedSizeMemoryPool : public MemoryPool {
private:
enum {
REAL_CHUNK_SIZE = (CHUNK_SIZE + sizeof(void*) - 1) & (~(sizeof(void*) - 1))
};
byte _storage[NUM_INTERNAL_CHUNKS * REAL_CHUNK_SIZE];
public:
FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {
assert(REAL_CHUNK_SIZE == _chunkSize);
// Insert some static storage
Page internalPage = { _storage, NUM_INTERNAL_CHUNKS };
addPageToPool(internalPage);
}
};
template<size_t CHUNK_SIZE>
class FixedSizeMemoryPool<CHUNK_SIZE,0> : public MemoryPool {
public:
FixedSizeMemoryPool() : MemoryPool(CHUNK_SIZE) {}
};
template<class T, size_t NUM_INTERNAL_CHUNKS = 32>
class ObjectPool : public FixedSizeMemoryPool<sizeof(T), NUM_INTERNAL_CHUNKS> {
public:
void deleteChunk(T *ptr) {
ptr->~T();
freeChunk(ptr);
}
};
} // End of namespace Common
// Provide a custom placement new operator, using an arbitrary
// MemoryPool.
//
// This *should* work with all C++ implementations, but may not.
//
// For details on using placement new for custom allocators, see e.g.
// <http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.14>
inline void* operator new(size_t nbytes, Common::MemoryPool& pool) {
assert(nbytes <= pool.getChunkSize());
return pool.allocChunk();
}
inline void operator delete(void* p, Common::MemoryPool& pool) {
pool.freeChunk(p);
}
#endif

View File

@ -70,7 +70,7 @@ private:
* To achieve that the object implements an internal reference counting.
* Thus you should try to avoid using the plain pointer after assigning
* it to a SharedPtr object for the first time. If you still use the
* plain pointer be sure you do not delete it on your own. You may also
* plain pointer be sure you do not delete it on your own. You may also
* not use the plain pointer to create a new SharedPtr object, since that
* would result in a double deletion of the pointer sooner or later.
*
@ -95,7 +95,7 @@ private:
*
* The class has implicit upcast support, so if you got a class B derived
* from class A, you can assign a pointer to B without any problems to a
* SharedPtr object with template parameter A. The very same applies to
* SharedPtr object with template parameter A. The very same applies to
* assignment of a SharedPtr<B> object to a SharedPtr<A> object.
*
* There are also operators != and == to compare two SharedPtr objects
@ -121,7 +121,7 @@ public:
~SharedPtr() { decRef(); }
SharedPtr &operator =(const SharedPtr &r) {
SharedPtr &operator=(const SharedPtr &r) {
if (r._refCount)
++(*r._refCount);
decRef();
@ -134,7 +134,7 @@ public:
}
template<class T2>
SharedPtr &operator =(const SharedPtr<T2> &r) {
SharedPtr &operator=(const SharedPtr<T2> &r) {
if (r._refCount)
++(*r._refCount);
decRef();
@ -146,8 +146,8 @@ public:
return *this;
}
ValueType &operator *() const { assert(_pointer); return *_pointer; }
Pointer operator ->() const { assert(_pointer); return _pointer; }
ValueType &operator*() const { assert(_pointer); return *_pointer; }
Pointer operator->() const { assert(_pointer); return _pointer; }
/**
* Returns the plain pointer value. Be sure you know what you
@ -170,6 +170,16 @@ public:
*/
bool unique() const { return refCount() == 1; }
/**
* Resets the SharedPtr object to a NULL pointer.
*/
void reset() {
decRef();
_deletion = 0;
_refCount = 0;
_pointer = 0;
}
/**
* Returns the number of references to the assigned pointer.
* This should just be used for debugging purposes.
@ -199,17 +209,13 @@ private:
} // end of namespace Common
template<class T1, class T2>
bool operator ==(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
bool operator==(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
return l.get() == r.get();
}
template<class T1, class T2>
bool operator !=(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
bool operator!=(const Common::SharedPtr<T1> &l, const Common::SharedPtr<T2> &r) {
return l.get() != r.get();
}
#endif

View File

@ -31,10 +31,9 @@
namespace Common {
/*! @brief simple class for handling both 2D position and size
This small class is an helper for position and size values.
*/
/**
* Simple class for handling both 2D position and size.
*/
struct Point {
int16 x; //!< The horizontal part of the point
int16 y; //!< The vertical part of the point
@ -65,23 +64,23 @@ struct Point {
}
};
/*! @brief simple class for handling a rectangular zone.
This small class is an helper for rectangles.
Note: This implementation is built around the assumption that (top,left) is
part of the rectangle, but (bottom,right) is not! This is reflected in
various methods, including contains(), intersects() and others.
Another very wide spread approach to rectangle classes treats (bottom,right)
also as a part of the rectangle.
Coneptually, both are sound, but the approach we use saves many intermediate
computations (like computing the height in our case is done by doing this:
height = bottom - top;
while in the alternate system, it would be
height = bottom - top + 1;
When writing code using our Rect class, always keep this principle in mind!
/**
* Simple class for handling a rectangular zone.
*
* Note: This implementation is built around the assumption that (top,left) is
* part of the rectangle, but (bottom,right) is not. This is reflected in
* various methods, including contains(), intersects() and others.
*
* Another very wide spread approach to rectangle classes treats (bottom,right)
* also as a part of the rectangle.
*
* Conceptually, both are sound, but the approach we use saves many intermediate
* computations (like computing the height in our case is done by doing this:
* height = bottom - top;
* while in the alternate system, it would be
* height = bottom - top + 1;
*
* When writing code using our Rect class, always keep this principle in mind!
*/
struct Rect {
int16 top, left; //!< The point at the top left of the rectangle (part of the rect).
@ -103,41 +102,56 @@ struct Rect {
bottom = top + aHeight;
}
/*! @brief check if given position is inside this rectangle
@param x the horizontal position to check
@param y the vertical position to check
@return true if the given position is inside this rectangle, false otherwise
*/
/**
* Check if given position is inside this rectangle.
*
* @param x the horizontal position to check
* @param y the vertical position to check
*
* @return true if the given position is inside this rectangle, false otherwise
*/
bool contains(int16 x, int16 y) const {
return (left <= x) && (x < right) && (top <= y) && (y < bottom);
}
/*! @brief check if given point is inside this rectangle
@param p the point to check
@return true if the given point is inside this rectangle, false otherwise
*/
/**
* Check if given point is inside this rectangle.
*
* @param p the point to check
*
* @return true if the given point is inside this rectangle, false otherwise
*/
bool contains(const Point &p) const {
return contains(p.x, p.y);
}
/*! @brief check if given rectangle intersects with this rectangle
/**
* Check if the given rect is _fully_ contained inside this rectangle.
*
* @param r The rectangle to check
*
* @return true if the given rect is inside, false otherwise
*/
bool contains(const Rect &r) const {
return (left < r.left) && (right > r.right) && (top < r.top) && (bottom > r.bottom);
}
@param r the rectangle to check
@return true if the given rectangle is inside the rectangle, false otherwise
*/
/**
* Check if given rectangle intersects with this rectangle
*
* @param r the rectangle to check
*
* @return true if the given rectangle is inside the rectangle, false otherwise
*/
bool intersects(const Rect &r) const {
return (left < r.right) && (r.left < right) && (top < r.bottom) && (r.top < bottom);
}
/*! @brief extend this rectangle so that it contains r
@param r the rectangle to extend by
*/
/**
* Extend this rectangle so that it contains r
*
* @param r the rectangle to extend by
*/
void extend(const Rect &r) {
left = MIN(left, r.left);
right = MAX(right, r.right);
@ -145,10 +159,11 @@ struct Rect {
bottom = MAX(bottom, r.bottom);
}
/*! @brief extend this rectangle in all four directions by the given number of pixels
@param offset the size to grow by
*/
/**
* Extend this rectangle in all four directions by the given number of pixels
*
* @param offset the size to grow by
*/
void grow(int16 offset) {
top -= offset;
left -= offset;
@ -205,7 +220,9 @@ struct Rect {
debug(debuglevel, "%s %d, %d, %d, %d", caption, left, top, right, bottom);
}
/*! @brief create a rectangle around the given center */
/**
* Create a rectangle around the given center.
*/
static Rect center(int16 cx, int16 cy, int16 w, int16 h) {
w /= 2;
h /= 2;

View File

@ -34,28 +34,27 @@ const String String::emptyString;
const char *String::emptyString = "";
#endif
static int computeCapacity(int len) {
// By default, for the capacity we use the nearest multiple of 32
// that leaves at least 16 chars of extra space (in case the string
// grows a bit).
// Finally, we subtract 1 to compensate for the trailing zero byte.
len += 16;
return ((len + 32 - 1) & ~0x1F) - 1;
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) : _len(0), _str(_storage) {
String::String(const char *str) : _size(0), _str(_storage) {
if (str == 0) {
_storage[0] = 0;
_len = 0;
_size = 0;
} else
initWithCStr(str, strlen(str));
}
String::String(const char *str, uint32 len) : _len(0), _str(_storage) {
String::String(const char *str, uint32 len) : _size(0), _str(_storage) {
initWithCStr(str, len);
}
String::String(const char *beginP, const char *endP) : _len(0), _str(_storage) {
String::String(const char *beginP, const char *endP) : _size(0), _str(_storage) {
assert(endP >= beginP);
initWithCStr(beginP, endP - beginP);
}
@ -67,13 +66,13 @@ void String::initWithCStr(const char *str, uint32 len) {
// for GCC 2.95.x compatibility (see also tracker item #1602879).
_storage[0] = 0;
_len = len;
_size = len;
if (len >= _builtinCapacity) {
// Not enough internal storage, so allocate more
_extern._capacity = computeCapacity(len);
_extern._capacity = computeCapacity(len+1);
_extern._refCount = 0;
_str = (char *)malloc(_extern._capacity+1);
_str = (char *)malloc(_extern._capacity);
assert(_str != 0);
}
@ -83,28 +82,30 @@ void String::initWithCStr(const char *str, uint32 len) {
}
String::String(const String &str)
: _len(str._len), _str(str.isStorageIntern() ? _storage : str._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)
: _len(0), _str(_storage) {
: _size(0), _str(_storage) {
_storage[0] = c;
_storage[1] = 0;
// TODO/FIXME: There is no reason for the following check -- we *do*
// allow strings to contain 0 bytes!
_len = (c == 0) ? 0 : 1;
_size = (c == 0) ? 0 : 1;
}
String::~String() {
@ -112,16 +113,16 @@ String::~String() {
}
void String::makeUnique() {
ensureCapacity(_len, true);
ensureCapacity(_size, true);
}
/**
* Ensure that enough storage is available to store at least new_len
* 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_len, bool keep_old) {
void String::ensureCapacity(uint32 new_size, bool keep_old) {
bool isShared;
uint32 curCapacity, newCapacity;
char *newStorage;
@ -129,7 +130,7 @@ void String::ensureCapacity(uint32 new_len, bool keep_old) {
if (isStorageIntern()) {
isShared = false;
curCapacity = _builtinCapacity - 1;
curCapacity = _builtinCapacity;
} else {
isShared = (oldRefCount && *oldRefCount > 1);
curCapacity = _extern._capacity;
@ -137,30 +138,30 @@ void String::ensureCapacity(uint32 new_len, bool keep_old) {
// Special case: If there is enough space, and we do not share
// the storage, then there is nothing to do.
if (!isShared && new_len <= curCapacity)
if (!isShared && new_size < curCapacity)
return;
if (isShared && new_len <= _builtinCapacity - 1) {
if (isShared && new_size < _builtinCapacity) {
// We share the storage, but there is enough internal storage: Use that.
newStorage = _storage;
newCapacity = _builtinCapacity - 1;
newCapacity = _builtinCapacity;
} else {
// We need to allocate storage on the heap!
// Compute a suitable new capacity limit
newCapacity = computeCapacity(new_len);
newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
// Allocate new storage
newStorage = (char *)malloc(newCapacity+1);
newStorage = (char *)malloc(newCapacity);
assert(newStorage);
}
// Copy old data if needed, elsewise reset the new storage.
if (keep_old) {
assert(_len <= newCapacity);
memcpy(newStorage, _str, _len + 1);
assert(_size < newCapacity);
memcpy(newStorage, _str, _size + 1);
} else {
_len = 0;
_size = 0;
newStorage[0] = 0;
}
@ -182,7 +183,11 @@ void String::ensureCapacity(uint32 new_len, bool keep_old) {
void String::incRefCount() const {
assert(!isStorageIntern());
if (_extern._refCount == 0) {
_extern._refCount = new int(2);
if (g_refCountPool == 0)
g_refCountPool = new MemoryPool(sizeof(int));
_extern._refCount = (int *)g_refCountPool->allocChunk();
*_extern._refCount = 2;
} else {
++(*_extern._refCount);
}
@ -198,7 +203,10 @@ void String::decRefCount(int *oldRefCount) {
if (!oldRefCount || *oldRefCount <= 0) {
// The ref count reached zero, so we free the string storage
// and the ref count storage.
delete oldRefCount;
if (oldRefCount) {
assert(g_refCountPool);
g_refCountPool->freeChunk(oldRefCount);
}
free(_str);
// Even though _str points to a freed memory block now,
@ -210,7 +218,7 @@ void String::decRefCount(int *oldRefCount) {
String& String::operator =(const char *str) {
uint32 len = strlen(str);
ensureCapacity(len, false);
_len = len;
_size = len;
memmove(_str, str, len + 1);
return *this;
}
@ -221,16 +229,16 @@ String &String::operator =(const String &str) {
if (str.isStorageIntern()) {
decRefCount(_extern._refCount);
_len = str._len;
_size = str._size;
_str = _storage;
memcpy(_str, str._str, _len + 1);
memcpy(_str, str._str, _size + 1);
} else {
str.incRefCount();
decRefCount(_extern._refCount);
_extern._refCount = str._extern._refCount;
_extern._capacity = str._extern._capacity;
_len = str._len;
_size = str._size;
_str = str._str;
}
@ -240,7 +248,7 @@ String &String::operator =(const String &str) {
String& String::operator =(char c) {
decRefCount(_extern._refCount);
_str = _storage;
_len = 1;
_size = 1;
_str[0] = c;
_str[1] = 0;
return *this;
@ -249,30 +257,30 @@ String& String::operator =(char c) {
String &String::operator +=(const char *str) {
int len = strlen(str);
if (len > 0) {
ensureCapacity(_len + len, true);
ensureCapacity(_size + len, true);
memcpy(_str + _len, str, len + 1);
_len += len;
memcpy(_str + _size, str, len + 1);
_size += len;
}
return *this;
}
String &String::operator +=(const String &str) {
int len = str._len;
int len = str._size;
if (len > 0) {
ensureCapacity(_len + len, true);
ensureCapacity(_size + len, true);
memcpy(_str + _len, str._str, len + 1);
_len += len;
memcpy(_str + _size, str._str, len + 1);
_size += len;
}
return *this;
}
String &String::operator +=(char c) {
ensureCapacity(_len + 1, true);
ensureCapacity(_size + 1, true);
_str[_len++] = c;
_str[_len] = 0;
_str[_size++] = c;
_str[_size] = 0;
return *this;
}
@ -293,10 +301,10 @@ bool String::hasPrefix(const char *x) const {
bool String::hasSuffix(const char *x) const {
assert(x != 0);
// Compare x with the end of _str.
const uint32 x_len = strlen(x);
if (x_len > _len)
const uint32 x_size = strlen(x);
if (x_size > _size)
return false;
const char *y = c_str() + _len - x_len;
const char *y = c_str() + _size - x_size;
while (*x && *x == *y) {
++x;
++y;
@ -315,66 +323,75 @@ bool String::contains(char x) const {
return strchr(c_str(), x) != NULL;
}
bool String::matchString(const char *pat) const {
return Common::matchString(c_str(), pat);
}
bool String::matchString(const String &pat) const {
return Common::matchString(c_str(), pat.c_str());
}
void String::deleteLastChar() {
deleteChar(_len - 1);
if (_size > 0)
deleteChar(_size - 1);
}
void String::deleteChar(uint32 p) {
assert(p < _len);
assert(p < _size);
makeUnique();
while (p++ < _len)
while (p++ < _size)
_str[p-1] = _str[p];
_len--;
_size--;
}
void String::clear() {
decRefCount(_extern._refCount);
_len = 0;
_size = 0;
_str = _storage;
_storage[0] = 0;
}
void String::setChar(char c, uint32 p) {
assert(p <= _len);
assert(p <= _size);
makeUnique();
_str[p] = c;
}
void String::insertChar(char c, uint32 p) {
assert(p <= _len);
assert(p <= _size);
ensureCapacity(_len + 1, true);
_len++;
for (uint32 i = _len; i > p; --i)
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 < _len; ++i)
for (uint32 i = 0; i < _size; ++i)
_str[i] = tolower(_str[i]);
}
void String::toUppercase() {
makeUnique();
for (uint32 i = 0; i < _len; ++i)
for (uint32 i = 0; i < _size; ++i)
_str[i] = toupper(_str[i]);
}
void String::trim() {
if (_len == 0)
if (_size == 0)
return;
makeUnique();
// Trim trailing whitespace
while (_len >= 1 && isspace(_str[_len-1]))
_len--;
_str[_len] = 0;
while (_size >= 1 && isspace(_str[_size-1]))
_size--;
_str[_size] = 0;
// Trim leading whitespace
char *t = _str;
@ -382,8 +399,8 @@ void String::trim() {
t++;
if (t != _str) {
_len -= t - _str;
memmove(_str, t, _len + 1);
_size -= t - _str;
memmove(_str, t, _size + 1);
}
}
@ -524,4 +541,112 @@ char *trim(char *t) {
return rtrim(ltrim(t));
}
Common::String lastPathComponent(const Common::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 Common::String();
// Now scan the whole component
const char *first = last - 1;
while (first >= str && *first != sep)
--first;
if (*first == sep)
first++;
return Common::String(first, last);
}
Common::String normalizePath(const Common::String &path, const char sep) {
if (path.empty())
return path;
const char *cur = path.c_str();
Common::String result;
// If there is a leading slash, preserve that:
if (*cur == sep) {
result += sep;
while (*cur == sep)
++cur;
}
// Scan till the end of the String
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 Common::String component(start, cur);
// Skip empty components and dot components, add all others
if (!component.empty() && component != ".") {
// Add a separator before the component, unless the result
// string already ends with one (which happens only if the
// path *starts* with a separator).
if (!result.empty() && result.lastChar() != sep)
result += sep;
// Add the component
result += component;
}
// Skip over separator chars
while (*cur == sep)
cur++;
}
return result;
}
bool matchString(const char *str, const char *pat) {
assert(str);
assert(pat);
const char *p = 0;
const char *q = 0;
for (;;) {
switch (*pat) {
case '*':
// Record pattern / string possition for backtracking
p = ++pat;
q = str;
// If pattern ended with * -> match
if (!*pat)
return true;
break;
default:
if (*pat != *str) {
if (p) {
// No match, oops -> try to backtrack
pat = p;
str = ++q;
if (!*str)
return !*pat;
break;
}
else
return false;
}
// fallthrough
case '?':
if (!*str)
return !*pat;
pat++;
str++;
}
}
}
} // End of namespace Common

View File

@ -54,14 +54,14 @@ protected:
* 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;
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 _len;
uint32 _size;
/**
* Pointer to the actual string storage. Either points to _storage,
@ -97,22 +97,22 @@ public:
#endif
/** Construct a new empty string. */
String() : _len(0), _str(_storage) { _storage[0] = 0; }
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. */
String(char c);
explicit String(char c);
~String();
@ -149,20 +149,44 @@ public:
bool contains(const char *x) const;
bool contains(char x) const;
inline const char *c_str() const { return _str; }
inline uint size() const { return _len; }
/**
* 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.
*
* 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
*
* @param str Text to be matched against the given pattern.
* @param pat Glob pattern.
*
* @return true if str matches the pattern, false otherwise.
*/
bool matchString(const char *pat) const;
bool matchString(const String &pat) const;
inline bool empty() const { return (_len == 0); }
char lastChar() const { return (_len > 0) ? _str[_len-1] : 0; }
inline const char *c_str() const { return _str; }
inline uint size() const { return _size; }
inline bool empty() const { return (_size == 0); }
char lastChar() const { return (_size > 0) ? _str[_size-1] : 0; }
char operator [](int idx) const {
assert(_str && idx >= 0 && idx < (int)_len);
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);
@ -172,11 +196,19 @@ public:
/** Set character c at 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;
@ -203,7 +235,7 @@ public:
protected:
void makeUnique();
void ensureCapacity(uint32 new_len, bool keep_old);
void ensureCapacity(uint32 new_size, bool keep_old);
void incRefCount() const;
void decRefCount(int *oldRefCount);
void initWithCStr(const char *str, uint32 len);
@ -218,7 +250,7 @@ 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 comparision operators for Strings
// Some useful additional comparison operators for Strings
bool operator == (const char *x, const String &y);
bool operator != (const char *x, const String &y);
@ -227,16 +259,67 @@ 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.
*/
Common::String lastPathComponent(const Common::String &path, const char sep);
/**
* Normalize a gien 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
*/
Common::String normalizePath(const Common::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.
*
* 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
*
* @param str Text to be matched against the given pattern.
* @param pat Glob pattern.
*
* @return true if str matches the pattern, false otherwise.
*/
bool matchString(const char *str, const char *pat);
class StringList : public Array<String> {
public:
void push_back(const char *str) {
ensureCapacity(_size + 1);
_data[_size++] = str;
Array<String>::push_back(str);
}
void push_back(const String &str) {
ensureCapacity(_size + 1);
_data[_size++] = str;
Array<String>::push_back(str);
}
};

View File

@ -284,10 +284,6 @@ extern "C" int residual_main(int argc, char *argv[]);
#define MAXPATHLEN 256
#endif
#ifndef CDECL
#define CDECL
#endif
#ifndef NORETURN
#define NORETURN
#endif

View File

@ -91,20 +91,20 @@ public:
/**
* Generates a random unsigned integer in the interval [0, max].
* @param max the upper bound
* @return a random number in the interval [0, max].
* @return a random number in the interval [0, max]
*/
uint getRandomNumber(uint max);
/**
* Generates a random unsigned integer in the interval [0, 1].
* Generates a random bit, i.e. either 0 or 1.
* Identical to getRandomNumber(1), but faster, hopefully.
* @return a random number in the interval [0, max].
* @return a random bit, either 0 or 1
*/
uint getRandomBit(void);
/**
* Generates a random unsigned integer in the interval [min, max].
* @param min the lower bound
* @param max the upper bound
* @return a random number in the interval [min, max].
* @return a random number in the interval [min, max]
*/
uint getRandomNumberRng(uint min, uint max);
};