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); fflush(stdout);
} }
void CDECL debug(const char *s, ...) { void debug(const char *s, ...) {
char buf[STRINGBUFLEN]; char buf[STRINGBUFLEN];
va_list va; va_list va;
@ -93,7 +93,7 @@ void CDECL debug(const char *s, ...) {
debugHelper(buf); debugHelper(buf);
} }
void CDECL debug(int level, const char *s, ...) { void debug(int level, const char *s, ...) {
char buf[STRINGBUFLEN]; char buf[STRINGBUFLEN];
va_list va; va_list va;
@ -107,7 +107,7 @@ void CDECL debug(int level, const char *s, ...) {
debugHelper(buf); debugHelper(buf);
} }
void NORETURN CDECL error(const char *s, ...) { void NORETURN error(const char *s, ...) {
char buf_input[STRINGBUFLEN]; char buf_input[STRINGBUFLEN];
char buf_output[STRINGBUFLEN]; char buf_output[STRINGBUFLEN];
va_list va; va_list va;
@ -153,7 +153,7 @@ void NORETURN CDECL error(const char *s, ...) {
exit(1); exit(1);
} }
void CDECL warning(const char *fmt, ...) { void warning(const char *fmt, ...) {
char buf[STRINGBUFLEN]; char buf[STRINGBUFLEN];
va_list va; va_list va;

View file

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

View file

@ -29,12 +29,18 @@
namespace Common { namespace Common {
/**
* Generic unary function.
*/
template<class Arg, class Result> template<class Arg, class Result>
struct UnaryFunction { struct UnaryFunction {
typedef Arg ArgumenType; typedef Arg ArgumenType;
typedef Result ResultType; typedef Result ResultType;
}; };
/**
* Generic binary function.
*/
template<class Arg1, class Arg2, class Result> template<class Arg1, class Arg2, class Result>
struct BinaryFunction { struct BinaryFunction {
typedef Arg1 FirstArgumentType; typedef Arg1 FirstArgumentType;
@ -42,16 +48,25 @@ struct BinaryFunction {
typedef Result ResultType; typedef Result ResultType;
}; };
/**
* Predicate to check for equallity of two data elements.
*/
template<class T> template<class T>
struct EqualTo : public BinaryFunction<T, T, bool> { struct EqualTo : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x == y; } bool operator()(const T &x, const T &y) const { return x == y; }
}; };
/**
* Predicate to check for x being less than y.
*/
template<class T> template<class T>
struct Less : public BinaryFunction<T, T, bool> { struct Less : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x < y; } bool operator()(const T &x, const T &y) const { return x < y; }
}; };
/**
* Predicate to check for x being greater than y.
*/
template<class T> template<class T>
struct Greater : public BinaryFunction<T, T, bool> { struct Greater : public BinaryFunction<T, T, bool> {
bool operator()(const T &x, const T &y) const { return x > y; } bool operator()(const T &x, const T &y) const { return x > y; }
@ -63,15 +78,19 @@ private:
Op _op; Op _op;
typename Op::FirstArgumentType _arg1; typename Op::FirstArgumentType _arg1;
public: 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 { typename Op::ResultType operator()(typename Op::SecondArgumentType v) const {
return _op(_arg1, v); 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); return Binder1st<Op>(op, t);
} }
@ -81,15 +100,19 @@ private:
Op _op; Op _op;
typename Op::SecondArgumentType _arg2; typename Op::SecondArgumentType _arg2;
public: 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 { typename Op::ResultType operator()(typename Op::FirstArgumentType v) const {
return _op(v, _arg2); 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); return Binder2nd<Op>(op, t);
} }
@ -119,18 +142,24 @@ public:
} }
}; };
/**
* Creates an unary function object from a function pointer.
*/
template<class Arg, class Result> template<class Arg, class Result>
inline PointerToUnaryFunc<Arg, Result> ptr_fun(Result (*func)(Arg)) { inline PointerToUnaryFunc<Arg, Result> ptr_fun(Result (*func)(Arg)) {
return PointerToUnaryFunc<Arg, Result>(func); return PointerToUnaryFunc<Arg, Result>(func);
} }
/**
* Creates an binary function object from a function pointer.
*/
template<class Arg1, class Arg2, class Result> template<class Arg1, class Arg2, class Result>
inline PointerToBinaryFunc<Arg1, Arg2, Result> ptr_fun(Result (*func)(Arg1, Arg2)) { inline PointerToBinaryFunc<Arg1, Arg2, Result> ptr_fun(Result (*func)(Arg1, Arg2)) {
return PointerToBinaryFunc<Arg1, Arg2, Result>(func); return PointerToBinaryFunc<Arg1, Arg2, Result>(func);
} }
template<class Result, class T> template<class Result, class T>
class MemFunc0 : public UnaryFunction<T*, Result> { class MemFunc0 : public UnaryFunction<T *, Result> {
private: private:
Result (T::*_func)(); Result (T::*_func)();
public: public:
@ -143,20 +172,20 @@ public:
}; };
template<class Result, class T> template<class Result, class T>
class ConstMemFunc0 : public UnaryFunction<T*, Result> { class ConstMemFunc0 : public UnaryFunction<T *, Result> {
private: private:
Result (T::*_func)() const; Result (T::*_func)() const;
public: public:
typedef Result (T::*FuncType)() const; typedef Result (T::*FuncType)() const;
ConstMemFunc0(const FuncType &func) : _func(func) {} ConstMemFunc0(const FuncType &func) : _func(func) {}
Result operator()(T *v) const { Result operator()(const T *v) const {
return (v->*_func)(); return (v->*_func)();
} }
}; };
template<class Result, class Arg, class T> template<class Result, class Arg, class T>
class MemFunc1 : public BinaryFunction<T*, Arg, Result> { class MemFunc1 : public BinaryFunction<T *, Arg, Result> {
private: private:
Result (T::*_func)(Arg); Result (T::*_func)(Arg);
public: public:
@ -169,40 +198,166 @@ public:
}; };
template<class Result, class Arg, class T> template<class Result, class Arg, class T>
class ConstMemFunc1 : public BinaryFunction<T*, Arg, Result> { class ConstMemFunc1 : public BinaryFunction<T *, Arg, Result> {
private: private:
Result (T::*_func)(Arg) const; Result (T::*_func)(Arg) const;
public: public:
typedef Result (T::*FuncType)(Arg) const; typedef Result (T::*FuncType)(Arg) const;
ConstMemFunc1(const FuncType &func) : _func(func) {} 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); 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> template<class Result, class T>
inline MemFunc0<Result, T> mem_fun(Result (T::*f)()) { inline MemFunc0<Result, T> mem_fun(Result (T::*f)()) {
return MemFunc0<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> template<class Result, class T>
inline ConstMemFunc0<Result, T> mem_fun(Result (T::*f)() const) { inline ConstMemFunc0<Result, T> mem_fun(Result (T::*f)() const) {
return ConstMemFunc0<Result, T>(f); 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> template<class Result, class Arg, class T>
inline MemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg)) { inline MemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg)) {
return MemFunc1<Result, Arg, T>(f); 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> template<class Result, class Arg, class T>
inline ConstMemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg) const) { inline ConstMemFunc1<Result, Arg, T> mem_fun(Result (T::*f)(Arg) const) {
return ConstMemFunc1<Result, Arg, T>(f); 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 // functor code
/**
* Generic functor object for function objects without parameters.
*
* @see Functor1
*/
template<class Res> template<class Res>
struct Functor0 { struct Functor0 {
virtual ~Functor0() {} virtual ~Functor0() {}
@ -211,6 +366,18 @@ struct Functor0 {
virtual Res operator()() const = 0; 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> template<class Res, class T>
class Functor0Mem : public Functor0<Res> { class Functor0Mem : public Functor0<Res> {
public: public:
@ -218,7 +385,7 @@ public:
Functor0Mem(T *t, const FuncType &func) : _t(t), _func(func) {} 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 { Res operator()() const {
return (_t->*_func)(); return (_t->*_func)();
} }
@ -227,6 +394,38 @@ private:
const FuncType _func; 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> template<class Arg, class Res>
struct Functor1 : public Common::UnaryFunction<Arg, Res> { struct Functor1 : public Common::UnaryFunction<Arg, Res> {
virtual ~Functor1() {} virtual ~Functor1() {}
@ -235,6 +434,13 @@ struct Functor1 : public Common::UnaryFunction<Arg, Res> {
virtual Res operator()(Arg) const = 0; 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> template<class Arg, class Res, class T>
class Functor1Mem : public Functor1<Arg, Res> { class Functor1Mem : public Functor1<Arg, Res> {
public: public:
@ -242,7 +448,7 @@ public:
Functor1Mem(T *t, const FuncType &func) : _t(t), _func(func) {} 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 { Res operator()(Arg v1) const {
return (_t->*_func)(v1); return (_t->*_func)(v1);
} }
@ -251,6 +457,11 @@ private:
const FuncType _func; const FuncType _func;
}; };
/**
* Generic functor object for binary function objects.
*
* @see Functor1
*/
template<class Arg1, class Arg2, class Res> template<class Arg1, class Arg2, class Res>
struct Functor2 : public Common::BinaryFunction<Arg1, Arg2, Res> { struct Functor2 : public Common::BinaryFunction<Arg1, Arg2, Res> {
virtual ~Functor2() {} virtual ~Functor2() {}
@ -259,6 +470,13 @@ struct Functor2 : public Common::BinaryFunction<Arg1, Arg2, Res> {
virtual Res operator()(Arg1, Arg2) const = 0; 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> template<class Arg1, class Arg2, class Res, class T>
class Functor2Mem : public Functor2<Arg1, Arg2, Res> { class Functor2Mem : public Functor2<Arg1, Arg2, Res> {
public: public:
@ -266,7 +484,7 @@ public:
Functor2Mem(T *t, const FuncType &func) : _t(t), _func(func) {} 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 { Res operator()(Arg1 v1, Arg2 v2) const {
return (_t->*_func)(v1, v2); 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 // FIXME: The following functors obviously are not consistently named
struct CaseSensitiveString_EqualTo { 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 { struct CaseSensitiveString_Hash {
@ -48,7 +48,7 @@ struct CaseSensitiveString_Hash {
struct IgnoreCase_EqualTo { 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 { struct IgnoreCase_Hash {
@ -77,7 +77,6 @@ struct Hash<const char *> {
} }
}; };
// String map -- by default case insensitive // String map -- by default case insensitive
typedef HashMap<String, String, IgnoreCase_Hash, IgnoreCase_EqualTo> StringMap; 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 // The hash map (associative array) implementation in this file is
// based on code by Andrew Y. Ng, 1996: // based on the PyDict implementation of CPython. The erase() method
// is based on example code in the Wikipedia article on Hash tables.
/*
* 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.
*/
#include "common/hashmap.h" #include "common/hashmap.h"
namespace Common { namespace Common {
// const char *: // Hash function for strings, taken from CPython.
uint hashit(const char *p) { uint hashit(const char *p) {
uint hash = 0; uint hash = *p << 7;
byte c; byte c;
while ((c = *p++)) int size = 0;
hash = (hash * 31 + c); while ((c = *p++)) {
return hash; 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 hashit_lower(const char *p) {
uint hash = 0; uint hash = tolower(*p) << 7;
byte c; byte c;
while ((c = *p++)) int size = 0;
hash = (hash * 31 + tolower(c)); while ((c = *p++)) {
return hash; hash = (1000003 * hash) ^ tolower(c);
size++;
}
return hash ^ size;
} }
// The following table is taken from the GNU ISO C++ Library's hashtable.h file. #ifdef DEBUG_HASH_COLLISIONS
static const uint primes[] = { static double
53ul, 97ul, 193ul, 389ul, 769ul, g_collisions = 0,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul, g_lookups = 0,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul, g_collPerLook = 0,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, g_capacity = 0,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, g_size = 0;
1610612741ul, 3221225473ul, 4294967291ul 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) { void updateHashCollisionStats(int collisions, int lookups, int arrsize, int nele) {
int i = 0; g_collisions += collisions;
while (x >= primes[i]) g_lookups += lookups;
i++; if (lookups)
return primes[i]; 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 } // End of namespace Common

View file

@ -51,6 +51,10 @@
* OTHER DEALINGS IN THE SOFTWARE. * 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 #ifndef COMMON_HASHMAP_H
#define COMMON_HASHMAP_H #define COMMON_HASHMAP_H
@ -58,29 +62,13 @@
#include "common/str.h" #include "common/str.h"
#include "common/util.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 #define USE_HASHMAP_MEMORY_POOL
#endif
#ifdef USE_HASHMAP_MEMORY_POOL #ifdef USE_HASHMAP_MEMORY_POOL
#include "common/memorypool.h" #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 #endif
namespace Common { 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 // 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 // code produces (many collisions indicate either a bad hash function, or a
// hash table that is too small). // hash table that is too small).
@ -115,31 +103,35 @@ public:
Node(const Key &key) : _key(key), _value() {} Node(const Key &key) : _key(key), _value() {}
}; };
enum {
HASHMAP_PERTURB_SHIFT = 5,
HASHMAP_MIN_CAPACITY = 16,
#ifdef USE_HASHMAP_MEMORY_POOL // The quotient of the next two constants controls how much the
MemoryPool _nodePool; // 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) { Node *allocNode(const Key &key) {
void* mem = _nodePool.malloc(); return new (_nodePool) Node(key);
return new (mem) Node(key); }
}
void freeNode(Node *node) { void freeNode(Node *node) {
node->~Node(); _nodePool.deleteChunk(node);
_nodePool.free(node);
} }
#else
Node* allocNode(const Key &key) {
return new Node(key);
}
void freeNode(Node *node) { Node **_storage; // hashtable of size arrsize.
delete node; uint _mask; /**< Capacity of the HashMap minus one; must be a power of two of minus one */
} uint _size;
#endif
Node **_arr; // hashtable of size arrsize.
uint _arrsize, _nele;
HashFunc _hash; HashFunc _hash;
EqualFunc _equal; EqualFunc _equal;
@ -154,7 +146,7 @@ public:
void assign(const HM_t &map); void assign(const HM_t &map);
int lookup(const Key &key) const; int lookup(const Key &key) const;
int lookupAndCreateIfMissing(const Key &key); int lookupAndCreateIfMissing(const Key &key);
void expand_array(uint newsize); void expandStorage(uint newCapacity);
template<class T> friend class IteratorImpl; template<class T> friend class IteratorImpl;
@ -176,8 +168,8 @@ public:
NodeType *deref() const { NodeType *deref() const {
assert(_hashmap != 0); assert(_hashmap != 0);
assert(_idx < _hashmap->_arrsize); assert(_idx <= _hashmap->_mask);
Node *node = _hashmap->_arr[_idx]; Node *node = _hashmap->_storage[_idx];
assert(node != 0); assert(node != 0);
return node; return node;
} }
@ -197,8 +189,8 @@ public:
assert(_hashmap); assert(_hashmap);
do { do {
_idx++; _idx++;
} while (_idx < _hashmap->_arrsize && _hashmap->_arr[_idx] == 0); } while (_idx <= _hashmap->_mask && _hashmap->_storage[_idx] == 0);
if (_idx >= _hashmap->_arrsize) if (_idx > _hashmap->_mask)
_idx = (uint)-1; _idx = (uint)-1;
return *this; return *this;
@ -225,7 +217,7 @@ public:
// Remove the previous content and ... // Remove the previous content and ...
clear(); clear();
delete[] _arr; delete[] _storage;
// ... copy the new stuff. // ... copy the new stuff.
assign(map); assign(map);
return *this; return *this;
@ -244,12 +236,12 @@ public:
void erase(const Key &key); void erase(const Key &key);
uint size() const { return _nele; } uint size() const { return _size; }
iterator begin() { iterator begin() {
// Find and return the _key non-empty entry // Find and return the _key non-empty entry
for (uint ctr = 0; ctr < _arrsize; ++ctr) { for (uint ctr = 0; ctr <= _mask; ++ctr) {
if (_arr[ctr]) if (_storage[ctr])
return iterator(ctr, this); return iterator(ctr, this);
} }
return end(); return end();
@ -260,8 +252,8 @@ public:
const_iterator begin() const { const_iterator begin() const {
// Find and return the first non-empty entry // Find and return the first non-empty entry
for (uint ctr = 0; ctr < _arrsize; ++ctr) { for (uint ctr = 0; ctr <= _mask; ++ctr) {
if (_arr[ctr]) if (_storage[ctr])
return const_iterator(ctr, this); return const_iterator(ctr, this);
} }
return end(); return end();
@ -272,14 +264,14 @@ public:
iterator find(const Key &key) { iterator find(const Key &key) {
uint ctr = lookup(key); uint ctr = lookup(key);
if (_arr[ctr]) if (_storage[ctr])
return iterator(ctr, this); return iterator(ctr, this);
return end(); return end();
} }
const_iterator find(const Key &key) const { const_iterator find(const Key &key) const {
uint ctr = lookup(key); uint ctr = lookup(key);
if (_arr[ctr]) if (_storage[ctr])
return const_iterator(ctr, this); return const_iterator(ctr, this);
return end(); return end();
} }
@ -287,7 +279,7 @@ public:
// TODO: insert() method? // TODO: insert() method?
bool empty() const { bool empty() const {
return (_nele == 0); return (_size == 0);
} }
}; };
@ -299,16 +291,13 @@ public:
*/ */
template<class Key, class Val, class HashFunc, class EqualFunc> template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap() : HashMap<Key, Val, HashFunc, EqualFunc>::HashMap() :
#ifdef USE_HASHMAP_MEMORY_POOL
_nodePool(sizeof(Node)),
#endif
_defaultVal() { _defaultVal() {
_arrsize = nextTableSize(0); _mask = HASHMAP_MIN_CAPACITY - 1;
_arr = new Node *[_arrsize]; _storage = new Node *[HASHMAP_MIN_CAPACITY];
assert(_arr != NULL); assert(_storage != NULL);
memset(_arr, 0, _arrsize * sizeof(Node *)); memset(_storage, 0, HASHMAP_MIN_CAPACITY * sizeof(Node *));
_nele = 0; _size = 0;
#ifdef DEBUG_HASH_COLLISIONS #ifdef DEBUG_HASH_COLLISIONS
_collisions = 0; _collisions = 0;
@ -322,10 +311,7 @@ HashMap<Key, Val, HashFunc, EqualFunc>::HashMap() :
* to heap buffers for the internal storage. * to heap buffers for the internal storage.
*/ */
template<class Key, class Val, class HashFunc, class EqualFunc> template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::HashMap(const HM_t &map) : HashMap<Key, Val, HashFunc, EqualFunc>::HashMap(const HM_t &map) :
#ifdef USE_HASHMAP_MEMORY_POOL
_nodePool(sizeof(Node)),
#endif
_defaultVal() { _defaultVal() {
assign(map); 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> template<class Key, class Val, class HashFunc, class EqualFunc>
HashMap<Key, Val, HashFunc, EqualFunc>::~HashMap() { HashMap<Key, Val, HashFunc, EqualFunc>::~HashMap() {
for (uint ctr = 0; ctr < _arrsize; ++ctr) for (uint ctr = 0; ctr <= _mask; ++ctr)
if (_arr[ctr] != NULL) if (_storage[ctr] != NULL)
freeNode(_arr[ctr]); 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> template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::assign(const HM_t &map) { void HashMap<Key, Val, HashFunc, EqualFunc>::assign(const HM_t &map) {
_arrsize = map._arrsize; _mask = map._mask;
_arr = new Node *[_arrsize]; _storage = new Node *[_mask+1];
assert(_arr != NULL); assert(_storage != NULL);
memset(_arr, 0, _arrsize * sizeof(Node *)); memset(_storage, 0, (_mask+1) * sizeof(Node *));
// Simply clone the map given to us, one by one. // Simply clone the map given to us, one by one.
_nele = 0; _size = 0;
for (uint ctr = 0; ctr < _arrsize; ++ctr) { for (uint ctr = 0; ctr <= _mask; ++ctr) {
if (map._arr[ctr] != NULL) { if (map._storage[ctr] != NULL) {
_arr[ctr] = allocNode(map._arr[ctr]->_key); _storage[ctr] = allocNode(map._storage[ctr]->_key);
_arr[ctr]->_value = map._arr[ctr]->_value; _storage[ctr]->_value = map._storage[ctr]->_value;
_nele++; _size++;
} }
} }
// Perform a sanity check (to help track down hashmap corruption) // 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> template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::clear(bool shrinkArray) { void HashMap<Key, Val, HashFunc, EqualFunc>::clear(bool shrinkArray) {
for (uint ctr = 0; ctr < _arrsize; ++ctr) { for (uint ctr = 0; ctr <= _mask; ++ctr) {
if (_arr[ctr] != NULL) { if (_storage[ctr] != NULL) {
freeNode(_arr[ctr]); freeNode(_storage[ctr]);
_arr[ctr] = NULL; _storage[ctr] = NULL;
} }
} }
if (shrinkArray && _arrsize > nextTableSize(0)) { #ifdef USE_HASHMAP_MEMORY_POOL
delete[] _arr; _nodePool.freeUnusedPages();
#endif
_arrsize = nextTableSize(0); if (shrinkArray && _mask >= HASHMAP_MIN_CAPACITY) {
_arr = new Node *[_arrsize]; delete[] _storage;
assert(_arr != NULL);
memset(_arr, 0, _arrsize * sizeof(Node *)); _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> template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::expand_array(uint newsize) { void HashMap<Key, Val, HashFunc, EqualFunc>::expandStorage(uint newCapacity) {
assert(newsize > _arrsize); assert(newCapacity > _mask+1);
uint ctr, dex;
const uint old_nele = _nele; const uint old_size = _size;
const uint old_arrsize = _arrsize; const uint old_mask = _mask;
Node **old_arr = _arr; Node **old_storage = _storage;
// allocate a new array // allocate a new array
_nele = 0; _size = 0;
_arrsize = newsize; _mask = newCapacity - 1;
_arr = new Node *[_arrsize]; _storage = new Node *[newCapacity];
assert(_arr != NULL); assert(_storage != NULL);
memset(_arr, 0, _arrsize * sizeof(Node *)); memset(_storage, 0, newCapacity * sizeof(Node *));
// rehash all the old elements // rehash all the old elements
for (ctr = 0; ctr < old_arrsize; ++ctr) { for (uint ctr = 0; ctr <= old_mask; ++ctr) {
if (old_arr[ctr] == NULL) if (old_storage[ctr] == NULL)
continue; continue;
// Insert the element from the old table into the new table. // Insert the element from the old table into the new table.
// Since we know that no key exists twice in the old table, we // Since we know that no key exists twice in the old table, we
// can do this slightly better than by calling lookup, since we // can do this slightly better than by calling lookup, since we
// don't have to call _equal(). // don't have to call _equal().
dex = _hash(old_arr[ctr]->_key) % _arrsize; const uint hash = _hash(old_storage[ctr]->_key);
while (_arr[dex] != NULL) { uint idx = hash & _mask;
dex = (dex + 1) % _arrsize; for (uint perturb = hash; _storage[idx] != NULL; perturb >>= HASHMAP_PERTURB_SHIFT) {
idx = (5 * idx + perturb + 1) & _mask;
} }
_arr[dex] = old_arr[ctr]; _storage[idx] = old_storage[ctr];
_nele++; _size++;
} }
// Perform a sanity check: Old number of elements should match the new one! // Perform a sanity check: Old number of elements should match the new one!
// This check will fail if some previous operation corrupted this hashmap. // 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; return;
} }
template<class Key, class Val, class HashFunc, class EqualFunc> template<class Key, class Val, class HashFunc, class EqualFunc>
int HashMap<Key, Val, HashFunc, EqualFunc>::lookup(const Key &key) const { 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 = (5 * ctr + perturb + 1) & _mask;
ctr = (ctr + 1) % _arrsize;
#ifdef DEBUG_HASH_COLLISIONS #ifdef DEBUG_HASH_COLLISIONS
_collisions++; _collisions++;
@ -450,7 +447,7 @@ int HashMap<Key, Val, HashFunc, EqualFunc>::lookup(const Key &key) const {
_lookups++; _lookups++;
fprintf(stderr, "collisions %d, lookups %d, ratio %f in HashMap %p; size %d num elements %d\n", fprintf(stderr, "collisions %d, lookups %d, ratio %f in HashMap %p; size %d num elements %d\n",
_collisions, _lookups, ((double) _collisions / (double)_lookups), _collisions, _lookups, ((double) _collisions / (double)_lookups),
(const void *)this, _arrsize, _nele); (const void *)this, _mask+1, _size);
#endif #endif
return ctr; 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) { int HashMap<Key, Val, HashFunc, EqualFunc>::lookupAndCreateIfMissing(const Key &key) {
uint ctr = lookup(key); uint ctr = lookup(key);
if (_arr[ctr] == NULL) { if (_storage[ctr] == NULL) {
_arr[ctr] = allocNode(key); _storage[ctr] = allocNode(key);
_nele++; _size++;
// Keep the load factor below 75%. // Keep the load factor below a certain threshold.
if (_nele > _arrsize * 75 / 100) { uint capacity = _mask + 1;
expand_array(nextTableSize(_arrsize)); if (_size * HASHMAP_LOADFACTOR_DENOMINATOR > capacity * HASHMAP_LOADFACTOR_NUMERATOR) {
capacity = capacity < 500 ? (capacity * 4) : (capacity * 2);
expandStorage(capacity);
ctr = lookup(key); 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> template<class Key, class Val, class HashFunc, class EqualFunc>
bool HashMap<Key, Val, HashFunc, EqualFunc>::contains(const Key &key) const { bool HashMap<Key, Val, HashFunc, EqualFunc>::contains(const Key &key) const {
uint ctr = lookup(key); uint ctr = lookup(key);
return (_arr[ctr] != NULL); return (_storage[ctr] != NULL);
} }
template<class Key, class Val, class HashFunc, class EqualFunc> 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> template<class Key, class Val, class HashFunc, class EqualFunc>
Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) { Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) {
uint ctr = lookupAndCreateIfMissing(key); uint ctr = lookupAndCreateIfMissing(key);
assert(_arr[ctr] != NULL); assert(_storage[ctr] != NULL);
return _arr[ctr]->_value; return _storage[ctr]->_value;
} }
template<class Key, class Val, class HashFunc, class EqualFunc> template<class Key, class Val, class HashFunc, class EqualFunc>
const Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) const { const Val &HashMap<Key, Val, HashFunc, EqualFunc>::getVal(const Key &key) const {
uint ctr = lookup(key); uint ctr = lookup(key);
if (_arr[ctr] != NULL) if (_storage[ctr] != NULL)
return _arr[ctr]->_value; return _storage[ctr]->_value;
else else
return _defaultVal; 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> template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::setVal(const Key &key, const Val &val) { void HashMap<Key, Val, HashFunc, EqualFunc>::setVal(const Key &key, const Val &val) {
uint ctr = lookupAndCreateIfMissing(key); uint ctr = lookupAndCreateIfMissing(key);
assert(_arr[ctr] != NULL); assert(_storage[ctr] != NULL);
_arr[ctr]->_value = val; _storage[ctr]->_value = val;
} }
template<class Key, class Val, class HashFunc, class EqualFunc> template<class Key, class Val, class HashFunc, class EqualFunc>
void HashMap<Key, Val, HashFunc, EqualFunc>::erase(const Key &key) { void HashMap<Key, Val, HashFunc, EqualFunc>::erase(const Key &key) {
// This is based on code in the Wikipedia article on Hash tables. // 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 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 // If we remove a key, we must check all subsequent keys and possibly
// reinsert them. // reinsert them.
uint j = i; uint j = i;
freeNode(_arr[i]); freeNode(_storage[i]);
_arr[i] = NULL; _storage[i] = NULL;
while (true) { for (perturb = hash; ; perturb >>= HASHMAP_PERTURB_SHIFT) {
// Look at the next table slot // 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 the next slot is empty, we are done
if (_arr[j] == NULL) if (_storage[j] == NULL)
break; break;
// Compute the slot where the content of the next slot should normally be, // 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. // 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)) || if ((j > i && (k <= i || k > j)) ||
(j < i && (k <= i && k > j)) ) { (j < i && (k <= i && k > j)) ) {
_arr[i] = _arr[j]; _storage[i] = _storage[j];
i = j; i = j;
} }
} }
_arr[i] = NULL; _storage[i] = NULL;
_nele--; _size--;
return; 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 * The values for the function keys listed here are based on what certain SCUMM
* games expect in their scripts. * games expect in their scripts.
* @todo Get rid of the function key values, and instead enforce that engines use * @todo Get rid of the function key values, and instead enforce that engines use
@ -259,6 +259,10 @@ struct KeyState {
keycode = KEYCODE_INVALID; keycode = KEYCODE_INVALID;
ascii = flags = 0; ascii = flags = 0;
} }
bool operator ==(const KeyState &x) const {
return keycode == x.keycode && ascii == x.ascii && flags == x.flags;
}
}; };
} // End of namespace Common } // End of namespace Common

View file

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

View file

@ -28,21 +28,10 @@
namespace Common { 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) { MemoryPool::MemoryPool(size_t chunkSize) {
// You must at least fit the pointer in the node (technically unneeded considering the next rounding statement) // 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)); _chunkSize = (_chunkSize + sizeof(void*) - 1) & (~(sizeof(void*) - 1));
_next = NULL; _next = NULL;
_chunksPerPage = INITIAL_CHUNKS_PER_PAGE;
} }
MemoryPool::~MemoryPool() { MemoryPool::~MemoryPool() {
for (size_t i = 0; i<_pages.size(); ++i) for (size_t i = 0; i < _pages.size(); ++i)
::free(_pages[i]); ::free(_pages[i].start);
} }
void* MemoryPool::malloc() { void MemoryPool::allocPage() {
#if 1 Page page;
if (!_next)
_next = allocPage();
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; _next = *(void**)result;
return result; return result;
#else
return ::malloc(_chunkSize);
#endif
} }
void MemoryPool::free(void* ptr) { void MemoryPool::freeChunk(void *ptr) {
#if 1 // Add the chunk back to (the start of) the list of free chunks
*(void**)ptr = _next; *(void**)ptr = _next;
_next = ptr; _next = ptr;
#else
::free(ptr);
#endif
} }
// Technically not compliant C++ to compare unrelated pointers. In practice... // Technically not compliant C++ to compare unrelated pointers. In practice...
bool MemoryPool::isPointerInPage(void* ptr, void* page) { bool MemoryPool::isPointerInPage(void *ptr, const Page &page) {
return (ptr >= page) && (ptr < (char*)page + CHUNK_PAGE_SIZE * _chunkSize); return (ptr >= page.start) && (ptr < (char*)page.start + page.numChunks * _chunkSize);
} }
void MemoryPool::freeUnusedPages() { void MemoryPool::freeUnusedPages() {
@ -94,9 +116,10 @@ void MemoryPool::freeUnusedPages() {
numberOfFreeChunksPerPage[i] = 0; numberOfFreeChunksPerPage[i] = 0;
} }
void* iterator = _next; // Compute for each page how many chunks in it are still in use.
void *iterator = _next;
while (iterator) { 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) { for (size_t i = 0; i < _pages.size(); ++i) {
if (isPointerInPage(iterator, _pages[i])) { if (isPointerInPage(iterator, _pages[i])) {
++numberOfFreeChunksPerPage[i]; ++numberOfFreeChunksPerPage[i];
@ -106,16 +129,41 @@ void MemoryPool::freeUnusedPages() {
iterator = *(void**)iterator; iterator = *(void**)iterator;
} }
// Free all pages which are not in use.
size_t freedPagesCount = 0; size_t freedPagesCount = 0;
for (size_t i = 0; i < _pages.size(); ++i) { for (size_t i = 0; i < _pages.size(); ++i) {
if (numberOfFreeChunksPerPage[i] == CHUNK_PAGE_SIZE) { if (numberOfFreeChunksPerPage[i] == _pages[i].numChunks) {
::free(_pages[i]); // Remove all chunks of this page from the list of free chunks
_pages[i] = NULL; // TODO : Remove NULL values void **iter2 = &_next;
while (*iter2) {
if (isPointerInPage(*iter2, _pages[i]))
*iter2 = **(void***)iter2;
else
iter2 = *(void***)iter2;
}
::free(_pages[i].start);
++freedPagesCount; ++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 } // End of namespace Common

View file

@ -32,26 +32,86 @@
namespace Common { namespace Common {
class MemoryPool { class MemoryPool {
private: protected:
MemoryPool(const MemoryPool&); MemoryPool(const MemoryPool&);
MemoryPool& operator=(const MemoryPool&); MemoryPool& operator=(const MemoryPool&);
size_t _chunkSize; struct Page {
Array<void*> _pages; void *start;
void* _next; 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: public:
MemoryPool(size_t chunkSize); MemoryPool(size_t chunkSize);
~MemoryPool(); ~MemoryPool();
void* malloc(); void *allocChunk();
void free(void* ptr); void freeChunk(void *ptr);
void freeUnusedPages(); 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 } // 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 #endif

View file

@ -70,7 +70,7 @@ private:
* To achieve that the object implements an internal reference counting. * To achieve that the object implements an internal reference counting.
* Thus you should try to avoid using the plain pointer after assigning * 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 * 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 * 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. * 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 * 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 * 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. * assignment of a SharedPtr<B> object to a SharedPtr<A> object.
* *
* There are also operators != and == to compare two SharedPtr objects * There are also operators != and == to compare two SharedPtr objects
@ -121,7 +121,7 @@ public:
~SharedPtr() { decRef(); } ~SharedPtr() { decRef(); }
SharedPtr &operator =(const SharedPtr &r) { SharedPtr &operator=(const SharedPtr &r) {
if (r._refCount) if (r._refCount)
++(*r._refCount); ++(*r._refCount);
decRef(); decRef();
@ -134,7 +134,7 @@ public:
} }
template<class T2> template<class T2>
SharedPtr &operator =(const SharedPtr<T2> &r) { SharedPtr &operator=(const SharedPtr<T2> &r) {
if (r._refCount) if (r._refCount)
++(*r._refCount); ++(*r._refCount);
decRef(); decRef();
@ -146,8 +146,8 @@ public:
return *this; return *this;
} }
ValueType &operator *() const { assert(_pointer); return *_pointer; } ValueType &operator*() const { assert(_pointer); return *_pointer; }
Pointer operator ->() const { assert(_pointer); return _pointer; } Pointer operator->() const { assert(_pointer); return _pointer; }
/** /**
* Returns the plain pointer value. Be sure you know what you * Returns the plain pointer value. Be sure you know what you
@ -170,6 +170,16 @@ public:
*/ */
bool unique() const { return refCount() == 1; } 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. * Returns the number of references to the assigned pointer.
* This should just be used for debugging purposes. * This should just be used for debugging purposes.
@ -199,17 +209,13 @@ private:
} // end of namespace Common } // end of namespace Common
template<class T1, class T2> 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(); return l.get() == r.get();
} }
template<class T1, class T2> 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(); return l.get() != r.get();
} }
#endif #endif

View file

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

View file

@ -34,28 +34,27 @@ const String String::emptyString;
const char *String::emptyString = ""; const char *String::emptyString = "";
#endif #endif
static int computeCapacity(int len) {
// By default, for the capacity we use the nearest multiple of 32 MemoryPool *g_refCountPool = 0; // FIXME: This is never freed right now
// that leaves at least 16 chars of extra space (in case the string
// grows a bit). static uint32 computeCapacity(uint32 len) {
// Finally, we subtract 1 to compensate for the trailing zero byte. // By default, for the capacity we use the next multiple of 32
len += 16; return ((len + 32 - 1) & ~0x1F);
return ((len + 32 - 1) & ~0x1F) - 1;
} }
String::String(const char *str) : _len(0), _str(_storage) { String::String(const char *str) : _size(0), _str(_storage) {
if (str == 0) { if (str == 0) {
_storage[0] = 0; _storage[0] = 0;
_len = 0; _size = 0;
} else } else
initWithCStr(str, strlen(str)); 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); 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); assert(endP >= beginP);
initWithCStr(beginP, 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). // for GCC 2.95.x compatibility (see also tracker item #1602879).
_storage[0] = 0; _storage[0] = 0;
_len = len; _size = len;
if (len >= _builtinCapacity) { if (len >= _builtinCapacity) {
// Not enough internal storage, so allocate more // Not enough internal storage, so allocate more
_extern._capacity = computeCapacity(len); _extern._capacity = computeCapacity(len+1);
_extern._refCount = 0; _extern._refCount = 0;
_str = (char *)malloc(_extern._capacity+1); _str = (char *)malloc(_extern._capacity);
assert(_str != 0); assert(_str != 0);
} }
@ -83,28 +82,30 @@ void String::initWithCStr(const char *str, uint32 len) {
} }
String::String(const String &str) String::String(const String &str)
: _len(str._len), _str(str.isStorageIntern() ? _storage : str._str) { : _size(str._size) {
if (str.isStorageIntern()) { if (str.isStorageIntern()) {
// String in internal storage: just copy it // String in internal storage: just copy it
memcpy(_storage, str._storage, _builtinCapacity); memcpy(_storage, str._storage, _builtinCapacity);
_str = _storage;
} else { } else {
// String in external storage: use refcount mechanism // String in external storage: use refcount mechanism
str.incRefCount(); str.incRefCount();
_extern._refCount = str._extern._refCount; _extern._refCount = str._extern._refCount;
_extern._capacity = str._extern._capacity; _extern._capacity = str._extern._capacity;
_str = str._str;
} }
assert(_str != 0); assert(_str != 0);
} }
String::String(char c) String::String(char c)
: _len(0), _str(_storage) { : _size(0), _str(_storage) {
_storage[0] = c; _storage[0] = c;
_storage[1] = 0; _storage[1] = 0;
// TODO/FIXME: There is no reason for the following check -- we *do* // TODO/FIXME: There is no reason for the following check -- we *do*
// allow strings to contain 0 bytes! // allow strings to contain 0 bytes!
_len = (c == 0) ? 0 : 1; _size = (c == 0) ? 0 : 1;
} }
String::~String() { String::~String() {
@ -112,16 +113,16 @@ String::~String() {
} }
void String::makeUnique() { 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 * characters plus a null byte. In addition, if we currently share
* the storage with another string, unshare it, so that we can safely * the storage with another string, unshare it, so that we can safely
* write to the storage. * write to the storage.
*/ */
void String::ensureCapacity(uint32 new_len, bool keep_old) { void String::ensureCapacity(uint32 new_size, bool keep_old) {
bool isShared; bool isShared;
uint32 curCapacity, newCapacity; uint32 curCapacity, newCapacity;
char *newStorage; char *newStorage;
@ -129,7 +130,7 @@ void String::ensureCapacity(uint32 new_len, bool keep_old) {
if (isStorageIntern()) { if (isStorageIntern()) {
isShared = false; isShared = false;
curCapacity = _builtinCapacity - 1; curCapacity = _builtinCapacity;
} else { } else {
isShared = (oldRefCount && *oldRefCount > 1); isShared = (oldRefCount && *oldRefCount > 1);
curCapacity = _extern._capacity; 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 // Special case: If there is enough space, and we do not share
// the storage, then there is nothing to do. // the storage, then there is nothing to do.
if (!isShared && new_len <= curCapacity) if (!isShared && new_size < curCapacity)
return; return;
if (isShared && new_len <= _builtinCapacity - 1) { if (isShared && new_size < _builtinCapacity) {
// We share the storage, but there is enough internal storage: Use that. // We share the storage, but there is enough internal storage: Use that.
newStorage = _storage; newStorage = _storage;
newCapacity = _builtinCapacity - 1; newCapacity = _builtinCapacity;
} else { } else {
// We need to allocate storage on the heap! // We need to allocate storage on the heap!
// Compute a suitable new capacity limit // Compute a suitable new capacity limit
newCapacity = computeCapacity(new_len); newCapacity = MAX(curCapacity * 2, computeCapacity(new_size+1));
// Allocate new storage // Allocate new storage
newStorage = (char *)malloc(newCapacity+1); newStorage = (char *)malloc(newCapacity);
assert(newStorage); assert(newStorage);
} }
// Copy old data if needed, elsewise reset the new storage. // Copy old data if needed, elsewise reset the new storage.
if (keep_old) { if (keep_old) {
assert(_len <= newCapacity); assert(_size < newCapacity);
memcpy(newStorage, _str, _len + 1); memcpy(newStorage, _str, _size + 1);
} else { } else {
_len = 0; _size = 0;
newStorage[0] = 0; newStorage[0] = 0;
} }
@ -182,7 +183,11 @@ void String::ensureCapacity(uint32 new_len, bool keep_old) {
void String::incRefCount() const { void String::incRefCount() const {
assert(!isStorageIntern()); assert(!isStorageIntern());
if (_extern._refCount == 0) { 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 { } else {
++(*_extern._refCount); ++(*_extern._refCount);
} }
@ -198,7 +203,10 @@ void String::decRefCount(int *oldRefCount) {
if (!oldRefCount || *oldRefCount <= 0) { if (!oldRefCount || *oldRefCount <= 0) {
// The ref count reached zero, so we free the string storage // The ref count reached zero, so we free the string storage
// and the ref count storage. // and the ref count storage.
delete oldRefCount; if (oldRefCount) {
assert(g_refCountPool);
g_refCountPool->freeChunk(oldRefCount);
}
free(_str); free(_str);
// Even though _str points to a freed memory block now, // 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) { String& String::operator =(const char *str) {
uint32 len = strlen(str); uint32 len = strlen(str);
ensureCapacity(len, false); ensureCapacity(len, false);
_len = len; _size = len;
memmove(_str, str, len + 1); memmove(_str, str, len + 1);
return *this; return *this;
} }
@ -221,16 +229,16 @@ String &String::operator =(const String &str) {
if (str.isStorageIntern()) { if (str.isStorageIntern()) {
decRefCount(_extern._refCount); decRefCount(_extern._refCount);
_len = str._len; _size = str._size;
_str = _storage; _str = _storage;
memcpy(_str, str._str, _len + 1); memcpy(_str, str._str, _size + 1);
} else { } else {
str.incRefCount(); str.incRefCount();
decRefCount(_extern._refCount); decRefCount(_extern._refCount);
_extern._refCount = str._extern._refCount; _extern._refCount = str._extern._refCount;
_extern._capacity = str._extern._capacity; _extern._capacity = str._extern._capacity;
_len = str._len; _size = str._size;
_str = str._str; _str = str._str;
} }
@ -240,7 +248,7 @@ String &String::operator =(const String &str) {
String& String::operator =(char c) { String& String::operator =(char c) {
decRefCount(_extern._refCount); decRefCount(_extern._refCount);
_str = _storage; _str = _storage;
_len = 1; _size = 1;
_str[0] = c; _str[0] = c;
_str[1] = 0; _str[1] = 0;
return *this; return *this;
@ -249,30 +257,30 @@ String& String::operator =(char c) {
String &String::operator +=(const char *str) { String &String::operator +=(const char *str) {
int len = strlen(str); int len = strlen(str);
if (len > 0) { if (len > 0) {
ensureCapacity(_len + len, true); ensureCapacity(_size + len, true);
memcpy(_str + _len, str, len + 1); memcpy(_str + _size, str, len + 1);
_len += len; _size += len;
} }
return *this; return *this;
} }
String &String::operator +=(const String &str) { String &String::operator +=(const String &str) {
int len = str._len; int len = str._size;
if (len > 0) { if (len > 0) {
ensureCapacity(_len + len, true); ensureCapacity(_size + len, true);
memcpy(_str + _len, str._str, len + 1); memcpy(_str + _size, str._str, len + 1);
_len += len; _size += len;
} }
return *this; return *this;
} }
String &String::operator +=(char c) { String &String::operator +=(char c) {
ensureCapacity(_len + 1, true); ensureCapacity(_size + 1, true);
_str[_len++] = c; _str[_size++] = c;
_str[_len] = 0; _str[_size] = 0;
return *this; return *this;
} }
@ -293,10 +301,10 @@ bool String::hasPrefix(const char *x) const {
bool String::hasSuffix(const char *x) const { bool String::hasSuffix(const char *x) const {
assert(x != 0); assert(x != 0);
// Compare x with the end of _str. // Compare x with the end of _str.
const uint32 x_len = strlen(x); const uint32 x_size = strlen(x);
if (x_len > _len) if (x_size > _size)
return false; return false;
const char *y = c_str() + _len - x_len; const char *y = c_str() + _size - x_size;
while (*x && *x == *y) { while (*x && *x == *y) {
++x; ++x;
++y; ++y;
@ -315,66 +323,75 @@ bool String::contains(char x) const {
return strchr(c_str(), x) != NULL; 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() { void String::deleteLastChar() {
deleteChar(_len - 1); if (_size > 0)
deleteChar(_size - 1);
} }
void String::deleteChar(uint32 p) { void String::deleteChar(uint32 p) {
assert(p < _len); assert(p < _size);
makeUnique(); makeUnique();
while (p++ < _len) while (p++ < _size)
_str[p-1] = _str[p]; _str[p-1] = _str[p];
_len--; _size--;
} }
void String::clear() { void String::clear() {
decRefCount(_extern._refCount); decRefCount(_extern._refCount);
_len = 0; _size = 0;
_str = _storage; _str = _storage;
_storage[0] = 0; _storage[0] = 0;
} }
void String::setChar(char c, uint32 p) { void String::setChar(char c, uint32 p) {
assert(p <= _len); assert(p <= _size);
makeUnique(); makeUnique();
_str[p] = c; _str[p] = c;
} }
void String::insertChar(char c, uint32 p) { void String::insertChar(char c, uint32 p) {
assert(p <= _len); assert(p <= _size);
ensureCapacity(_len + 1, true); ensureCapacity(_size + 1, true);
_len++; _size++;
for (uint32 i = _len; i > p; --i) for (uint32 i = _size; i > p; --i)
_str[i] = _str[i-1]; _str[i] = _str[i-1];
_str[p] = c; _str[p] = c;
} }
void String::toLowercase() { void String::toLowercase() {
makeUnique(); makeUnique();
for (uint32 i = 0; i < _len; ++i) for (uint32 i = 0; i < _size; ++i)
_str[i] = tolower(_str[i]); _str[i] = tolower(_str[i]);
} }
void String::toUppercase() { void String::toUppercase() {
makeUnique(); makeUnique();
for (uint32 i = 0; i < _len; ++i) for (uint32 i = 0; i < _size; ++i)
_str[i] = toupper(_str[i]); _str[i] = toupper(_str[i]);
} }
void String::trim() { void String::trim() {
if (_len == 0) if (_size == 0)
return; return;
makeUnique(); makeUnique();
// Trim trailing whitespace // Trim trailing whitespace
while (_len >= 1 && isspace(_str[_len-1])) while (_size >= 1 && isspace(_str[_size-1]))
_len--; _size--;
_str[_len] = 0; _str[_size] = 0;
// Trim leading whitespace // Trim leading whitespace
char *t = _str; char *t = _str;
@ -382,8 +399,8 @@ void String::trim() {
t++; t++;
if (t != _str) { if (t != _str) {
_len -= t - _str; _size -= t - _str;
memmove(_str, t, _len + 1); memmove(_str, t, _size + 1);
} }
} }
@ -524,4 +541,112 @@ char *trim(char *t) {
return rtrim(ltrim(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 } // End of namespace Common

View file

@ -54,14 +54,14 @@ protected:
* than 8 makes no sense, since that's the size of member _extern * than 8 makes no sense, since that's the size of member _extern
* (on 32 bit machines; 12 bytes on systems with 64bit pointers). * (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 * Length of the string. Stored to avoid having to call strlen
* a lot. Yes, we limit ourselves to strings shorter than 4GB -- * a lot. Yes, we limit ourselves to strings shorter than 4GB --
* on purpose :-). * on purpose :-).
*/ */
uint32 _len; uint32 _size;
/** /**
* Pointer to the actual string storage. Either points to _storage, * Pointer to the actual string storage. Either points to _storage,
@ -97,22 +97,22 @@ public:
#endif #endif
/** Construct a new empty string. */ /** 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. */ /** Construct a new string from the given NULL-terminated C string. */
String(const char *str); String(const char *str);
/** Construct a new string containing exactly len characters read from address str. */ /** Construct a new string containing exactly len characters read from address str. */
String(const char *str, uint32 len); String(const char *str, uint32 len);
/** Construct a new string containing the characters between beginP (including) and endP (excluding). */ /** Construct a new string containing the characters between beginP (including) and endP (excluding). */
String(const char *beginP, const char *endP); String(const char *beginP, const char *endP);
/** Construct a copy of the given string. */ /** Construct a copy of the given string. */
String(const String &str); String(const String &str);
/** Construct a string consisting of the given character. */ /** Construct a string consisting of the given character. */
String(char c); explicit String(char c);
~String(); ~String();
@ -149,20 +149,44 @@ public:
bool contains(const char *x) const; bool contains(const char *x) const;
bool contains(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 { char operator [](int idx) const {
assert(_str && idx >= 0 && idx < (int)_len); assert(_str && idx >= 0 && idx < (int)_size);
return _str[idx]; return _str[idx];
} }
/** Remove the last character from the string. */ /** Remove the last character from the string. */
void deleteLastChar(); void deleteLastChar();
/** Remove the character at position p from the string. */ /** Remove the character at position p from the string. */
void deleteChar(uint32 p); void deleteChar(uint32 p);
@ -172,11 +196,19 @@ public:
/** Set character c at position p. */ /** Set character c at position p. */
void insertChar(char c, uint32 p); void insertChar(char c, uint32 p);
/** Clears the string, making it empty. */
void clear(); void clear();
/** Convert all characters in the string to lowercase. */
void toLowercase(); void toLowercase();
/** Convert all characters in the string to uppercase. */
void toUppercase(); void toUppercase();
/**
* Removes trailing and leading whitespaces. Uses isspace() to decide
* what is whitespace and what not.
*/
void trim(); void trim();
uint hash() const; uint hash() const;
@ -203,7 +235,7 @@ public:
protected: protected:
void makeUnique(); void makeUnique();
void ensureCapacity(uint32 new_len, bool keep_old); void ensureCapacity(uint32 new_size, bool keep_old);
void incRefCount() const; void incRefCount() const;
void decRefCount(int *oldRefCount); void decRefCount(int *oldRefCount);
void initWithCStr(const char *str, uint32 len); 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 +(const String &x, char y);
String operator +(char x, const String &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);
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 *rtrim(char *t);
extern char *trim(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> { class StringList : public Array<String> {
public: public:
void push_back(const char *str) { void push_back(const char *str) {
ensureCapacity(_size + 1); Array<String>::push_back(str);
_data[_size++] = str;
} }
void push_back(const String &str) { void push_back(const String &str) {
ensureCapacity(_size + 1); Array<String>::push_back(str);
_data[_size++] = str;
} }
}; };

View file

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

View file

@ -91,20 +91,20 @@ public:
/** /**
* Generates a random unsigned integer in the interval [0, max]. * Generates a random unsigned integer in the interval [0, max].
* @param max the upper bound * @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); 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. * 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); uint getRandomBit(void);
/** /**
* Generates a random unsigned integer in the interval [min, max]. * Generates a random unsigned integer in the interval [min, max].
* @param min the lower bound * @param min the lower bound
* @param max the upper 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); uint getRandomNumberRng(uint min, uint max);
}; };