for making a hash table imitate a vector. Since there's now more than just one type of savable/restorable container, hash.cc and hash.h have been renamed to store.cc and store.h ("store" for storage).
Destroying/unlinking/deleting an item now clears out the item's props field.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2389 c06c8d41-db1a-0410-9941-cceddc491573
K2GMFKXUWN5R3KCW6OYVXHN47MIQZKEEIOSAU6LFFKBNKF6JBVWAC EFWEYIB2R3DPD3JWIPU6LS6SFLPMYN7J7X4GBZR7DJWKHJ3UELSAC EGV2HM7SD7UQSWJGLR65NQJTUBAJ7WHLM67FMH4UFP7JRSFKREPAC UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC 7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC 5BJPWUPLJFS34FUTFJVKA4A52YMIGV6EWDXLNSDCWBJWBGVSQFGQC JPYDWBRN75GC6UZ26MXJTCXGORTJOWGRDEU4JFPU52LYHGK6UI2QC HZ6SBPPUHJUYFVXA6KOELY455RTBXICRJGVHTUYLHWAPV2DEOMSQC /** File: hash.h* Summary: Saveable hash-table capable of storing multiple types* of data.* Written by: Matthew Cline** Modified for Crawl Reference by $Author$ on $Date$** Change History (most recent first):** <1> 10/5/07 MPC Created*/#ifndef HASH_H#define HASH_H#include <string>#include <map>struct tagHeader;class CrawlHashTable;class item_def;class coord_def;// NOTE: Changing the ordering of these enums will break savefile// compatibility.enum hash_val_type{HV_NONE = 0,HV_BOOL,HV_BYTE,HV_SHORT,HV_LONG,HV_FLOAT,HV_STR,HV_COORD,HV_HASH,HV_ITEM,NUM_HASH_VAL_TYPES};enum hash_flag_type{HFLAG_UNSET = (1 << 0),HFLAG_CONST_VAL = (1 << 1),HFLAG_CONST_TYPE = (1 << 2),HFLAG_NO_ERASE = (1 << 3)};// Can't just cast everything into a void pointer, since a float might// not fit into a pointer on all systems.typedef union HashUnion HashUnion;union HashUnion{bool boolean;char byte;short _short;long _long;float _float;void* ptr;};class CrawlHashValue{public:CrawlHashValue();CrawlHashValue(const CrawlHashValue &other);~CrawlHashValue();// Only needed for doing some assertion checking.CrawlHashValue &operator = (const CrawlHashValue &other);protected:hash_val_type type;unsigned char flags;HashUnion val;public:unsigned char get_flags() const;unsigned char set_flags(unsigned char flags);unsigned char unset_flags(unsigned char flags);hash_val_type get_type() const;CrawlHashTable &new_table(unsigned char flags);CrawlHashTable &new_table(hash_val_type type, unsigned char flags = 0);bool &get_bool();char &get_byte();short &get_short();long &get_long();float &get_float();std::string &get_string();coord_def &get_coord();CrawlHashTable &get_table();item_def &get_item();bool get_bool() const;char get_byte() const;short get_short() const;long get_long() const;float get_float() const;std::string get_string() const;coord_def get_coord() const;const CrawlHashTable& get_table() const;const item_def& get_item() const;void set_bool(const bool val);void set_byte(const char val);void set_short(const short val);void set_long(const long val);void set_float(const float val);void set_string(const std::string &val);void set_coord(const coord_def &val);void set_table(const CrawlHashTable &val);void set_item(const item_def &val);public:// NOTE: All operators will assert if the hash value is of the// wrong type for the operation. If the value has no type yet,// the operation will set it to the appropriate type. If the// value has no type yet and the operation modifies the existing// value rather than replacing it (i.e., ++) the value will be set// to a default before the operation is done.// If the hash value is a hash table, the table's values can be// accessed with the [] operator.CrawlHashValue &operator [] (const std::string &key);CrawlHashValue &operator [] (const long &key);const CrawlHashValue &operator [] (const std::string &key) const;const CrawlHashValue &operator [] (const long &key) const;// Typecast operators&operator bool();&operator char();&operator short();&operator long();&operator float();&operator std::string();&operator coord_def();&operator CrawlHashTable();&operator item_def();operator bool() const;operator char() const;operator short() const;operator long() const;operator float() const;operator std::string() const;operator coord_def() const;// Assignment operatorsbool &operator = (const bool &val);char &operator = (const char &val);short &operator = (const short &val);long &operator = (const long &val);float &operator = (const float &val);std::string &operator = (const std::string &val);const char* operator = (const char* val);coord_def &operator = (const coord_def &val);CrawlHashTable &operator = (const CrawlHashTable &val);item_def &operator = (const item_def &val);// Misc operatorsstd::string &operator += (const std::string &val);// Prefixlong operator ++ ();long operator -- ();// Postfixlong operator ++ (int);long operator -- (int);protected:CrawlHashValue(const unsigned char flags,const hash_val_type type = HV_NONE);void write(tagHeader &th) const;void read(tagHeader &th);void unset(bool force = false);friend class CrawlHashTable;};// A hash table can have a maximum of 255 key/value pairs. If you// want more than that you can use nested hash tables.//// By default a hash table's value data types are heterogeneous. To// make it homogeneous (which causes dynamic type checking) you have// to give a type to the hash table constructor; once it's been// created it's type (or lack of type) is immutable.//// An empty hash table will take up only 1 byte in the savefile. A// non-empty hash table will have an overhead of 3 bytes for the hash// table overall and 2 bytes per key/value pair, over and above the// number of bytes needed to store the keys and values themselves.class CrawlHashTable{public:CrawlHashTable();CrawlHashTable(unsigned char flags);CrawlHashTable(hash_val_type type, unsigned char flags = 0);~CrawlHashTable();typedef std::map<std::string, CrawlHashValue> hash_map_type;protected:hash_val_type type;unsigned char default_flags;hash_map_type hash_map;friend class CrawlHashValue;public:void write(tagHeader &th) const;void read(tagHeader &th);unsigned char get_default_flags() const;unsigned char set_default_flags(unsigned char flags);unsigned char unset_default_flags(unsigned char flags);hash_val_type get_type() const;bool exists(const std::string key) const;bool exists(const long key) const;void assert_validity() const;int compact_indicies(long min_index, long max_index,bool compact_down = true);bool fixup_indexed_array(std::string name = "");// NOTE: If get_value() or [] is given a key which doesn't exist// in the table, an unset/empty CrawlHashValue will be created// with that key and returned. If it is not then given a value// then the next call to assert_validity() will fail. If the// hash table has a type (rather than being heterogeneous)// then trying to assign a different type to the CrawlHashValue// will assert.CrawlHashValue& get_value(const std::string &key);CrawlHashValue& get_value(const long &index);CrawlHashValue& operator[] (const std::string &key);CrawlHashValue& operator[] (const long &index);// NOTE: If the const versions of get_value() or [] are given a// key which doesn't exist, they will assert.const CrawlHashValue& get_value(const std::string &key) const;const CrawlHashValue& get_value(const long &index) const;const CrawlHashValue& operator[] (const std::string &key) const;const CrawlHashValue& operator[] (const long &index) const;// std::map style interfacesize_t size() const;bool empty() const;void erase(const std::string key);void erase(const long index);void clear();hash_map_type::iterator begin();hash_map_type::iterator end();hash_map_type::const_iterator begin() const;hash_map_type::const_iterator end() const;};// A wrapper for non-heterogeneous hash tables, so that the values can// be accessed without using get_foo(). T needs to have both normal// and const type-cast operators defined by CrawlHashValue for this// template to work.template <typename T, hash_val_type TYPE>class CrawlTableWrapper{public:CrawlTableWrapper();CrawlTableWrapper(CrawlHashTable& table);CrawlTableWrapper(CrawlHashTable* table);protected:CrawlHashTable* table;public:void wrap(CrawlHashTable& table);void wrap(CrawlHashTable* table);CrawlHashTable* get_table();T& operator[] (const std::string &key);T& operator[] (const long &index);const CrawlHashTable* get_table() const;const T operator[] (const std::string &key) const;const T operator[] (const long &index) const;};typedef CrawlTableWrapper<bool, HV_BOOL> CrawlBoolTable;typedef CrawlTableWrapper<char, HV_BYTE> CrawlByteTable;typedef CrawlTableWrapper<short, HV_SHORT> CrawlShortTable;typedef CrawlTableWrapper<long, HV_LONG> CrawlLongTable;typedef CrawlTableWrapper<float, HV_FLOAT> CrawlFloatTable;typedef CrawlTableWrapper<std::string, HV_STR> CrawlStringTable;typedef CrawlTableWrapper<coord_def, HV_COORD> CrawlCoordTable;#endif
/** File: hash.cc* Summary: Saveable hash-table capable of storing multiple types* of data.* Written by: Matthew Cline** Modified for Crawl Reference by $Author$ on $Date$** Change History (most recent first):* <1> 10/5/07 MPC Created*/#include "AppHdr.h"#include "hash.h"#include "externs.h"#include "tags.h"CrawlHashValue::CrawlHashValue(): type(HV_NONE), flags(HFLAG_UNSET){val.ptr = NULL;}CrawlHashValue::CrawlHashValue(const CrawlHashValue &other){ASSERT(other.type >= HV_NONE && other.type < NUM_HASH_VAL_TYPES);type = other.type;flags = other.flags;if (flags & HFLAG_UNSET){val = other.val;return;}switch (type){case HV_NONE:case HV_BOOL:case HV_BYTE:case HV_SHORT:case HV_LONG:case HV_FLOAT:val = other.val;break;case HV_STR:{std::string* str;str = new std::string(*static_cast<std::string*>(other.val.ptr));val.ptr = static_cast<void*>(str);break;}case HV_COORD:{coord_def* coord;coord = new coord_def(*static_cast<coord_def*>(other.val.ptr));val.ptr = static_cast<void*>(coord);break;}case HV_HASH:{CrawlHashTable* hash;CrawlHashTable* tmp = static_cast<CrawlHashTable*>(other.val.ptr);hash = new CrawlHashTable(*tmp);val.ptr = static_cast<void*>(hash);break;}case HV_ITEM:{item_def* item;item = new item_def(*static_cast<item_def*>(other.val.ptr));val.ptr = static_cast<void*>(item);break;}case NUM_HASH_VAL_TYPES:ASSERT(false);}}CrawlHashValue::CrawlHashValue(const unsigned char _flags,const hash_val_type _type): type(_type), flags(_flags){ASSERT(type >= HV_NONE && type < NUM_HASH_VAL_TYPES);ASSERT(!(flags & HFLAG_UNSET));flags |= HFLAG_UNSET;val.ptr = NULL;}CrawlHashValue::~CrawlHashValue(){unset(true);}void CrawlHashValue::unset(bool force){if (flags & HFLAG_UNSET)return;if (force)flags &= ~HFLAG_NO_ERASE;ASSERT(!(flags & HFLAG_NO_ERASE));switch (type){case HV_BOOL:val.boolean = false;break;case HV_BYTE:val.byte = 0;break;case HV_SHORT:val._short = 0;break;case HV_LONG:val._long = 0;break;case HV_FLOAT:val._float = 0.0;break;case HV_STR:{std::string* str = static_cast<std::string*>(val.ptr);delete str;val.ptr = NULL;break;}case HV_COORD:{coord_def* coord = static_cast<coord_def*>(val.ptr);delete coord;val.ptr = NULL;break;}case HV_HASH:{CrawlHashTable* hash = static_cast<CrawlHashTable*>(val.ptr);delete hash;val.ptr = NULL;break;}case HV_ITEM:{item_def* item = static_cast<item_def*>(val.ptr);delete item;val.ptr = NULL;break;}case HV_NONE:ASSERT(false);case NUM_HASH_VAL_TYPES:ASSERT(false);}flags |= HFLAG_UNSET;}// Only needed to do some assertion checking.CrawlHashValue &CrawlHashValue::operator = (const CrawlHashValue &other){ASSERT(other.type >= HV_NONE && other.type < NUM_HASH_VAL_TYPES);ASSERT(other.type != HV_NONE || type == HV_NONE);// NOTE: We don't bother checking HFLAG_CONST_VAL, since the// asignment operator is used when swapping two elements.if (!(flags & HFLAG_UNSET)){if (flags & HFLAG_CONST_TYPE)ASSERT(type == HV_NONE || type == other.type);}type = other.type;flags = other.flags;val = other.val;return (*this);}///////////////////////////////////// Meta-data accessors and changersunsigned char CrawlHashValue::get_flags() const{return flags;}unsigned char CrawlHashValue::set_flags(unsigned char _flags){flags |= _flags;return flags;}unsigned char CrawlHashValue::unset_flags(unsigned char _flags){flags &= ~_flags;return flags;}hash_val_type CrawlHashValue::get_type() const{return type;}//////////////////////////////// Read/write from/to savefilevoid CrawlHashValue::write(tagHeader &th) const{ASSERT(!(flags & HFLAG_UNSET));marshallByte(th, (char) type);marshallByte(th, (char) flags);switch (type){case HV_BOOL:marshallBoolean(th, val.boolean);break;case HV_BYTE:marshallByte(th, val.byte);break;case HV_SHORT:marshallShort(th, val._short);break;case HV_LONG:marshallLong(th, val._long);break;case HV_FLOAT:marshallFloat(th, val._float);break;case HV_STR:{std::string* str = static_cast<std::string*>(val.ptr);marshallString(th, *str);break;}case HV_COORD:{coord_def* coord = static_cast<coord_def*>(val.ptr);marshallCoord(th, *coord);break;}case HV_HASH:{CrawlHashTable* hash = static_cast<CrawlHashTable*>(val.ptr);hash->write(th);break;}case HV_ITEM:{item_def* item = static_cast<item_def*>(val.ptr);marshallItem(th, *item);break;}case HV_NONE:ASSERT(false);case NUM_HASH_VAL_TYPES:ASSERT(false);}}void CrawlHashValue::read(tagHeader &th){type = static_cast<hash_val_type>(unmarshallByte(th));flags = (unsigned char) unmarshallByte(th);ASSERT(!(flags & HFLAG_UNSET));switch (type){case HV_BOOL:val.boolean = unmarshallBoolean(th);break;case HV_BYTE:val.byte = unmarshallByte(th);break;case HV_SHORT:val._short = unmarshallShort(th);break;case HV_LONG:val._long = unmarshallLong(th);break;case HV_FLOAT:val._float = unmarshallFloat(th);break;case HV_STR:{std::string str = unmarshallString(th);val.ptr = (void*) new std::string(str);break;}case HV_COORD:{coord_def coord;unmarshallCoord(th, coord);val.ptr = (void*) new coord_def(coord);break;}case HV_HASH:{CrawlHashTable* hash = new CrawlHashTable();hash->read(th);val.ptr = (void*) hash;break;}case HV_ITEM:{item_def item;unmarshallItem(th, item);val.ptr = (void*) new item_def(item);break;}case HV_NONE:ASSERT(false);case NUM_HASH_VAL_TYPES:ASSERT(false);}}////////////////////////////////////////////////////////////////// Setup a new table with the given flags and/or type; assert if// a table already exists.CrawlHashTable &CrawlHashValue::new_table(unsigned char _flags){return new_table(HV_NONE, flags);}CrawlHashTable &CrawlHashValue::new_table(hash_val_type _type,unsigned char _flags){CrawlHashTable* old_table = static_cast<CrawlHashTable*>(val.ptr);ASSERT(flags & HFLAG_UNSET);ASSERT(type == HV_NONE|| (type == HV_HASH&& old_table->size() == 0&& old_table->get_type() == HV_NONE&& old_table->get_default_flags() == 0));CrawlHashTable &table = get_table();table.default_flags = _flags;table.type = _type;type = HV_HASH;flags &= ~HFLAG_UNSET;return table;}///////////////////////////////////////////// Dynamic type-checking accessor functions#define GET_VAL(x, _type, field, value) \ASSERT((flags & HFLAG_UNSET) || !(flags & HFLAG_CONST_VAL)); \if (type != (x)) \{ \if (type == HV_NONE) \{ \type = (x); \field = (value); \} \else \{ \ASSERT(!(flags & HFLAG_CONST_TYPE)); \switch(type) \{ \case HV_BOOL: \field = (_type) val.boolean; \break; \case HV_BYTE: \field = (_type) val.byte; \break; \case HV_SHORT: \field = (_type) val._short; \break; \case HV_LONG: \field = (_type) val._long; \break; \case HV_FLOAT: \field = (_type) val._float; \break; \default: \ASSERT(false); \} \type = (x); \} \} \flags &= ~HFLAG_UNSET; \return field;#define GET_VAL_PTR(x, _type, value) \ASSERT((flags & HFLAG_UNSET) || !(flags & HFLAG_CONST_VAL)); \if (type != (x)) \{ \if (type == HV_NONE) \{ \type = (x); \val.ptr = (value); \} \else \{ \unset(); \val.ptr = (value); \type = (x); \} \} \flags &= ~HFLAG_UNSET; \return *((_type) val.ptr);bool &CrawlHashValue::get_bool(){GET_VAL(HV_BOOL, bool, val.boolean, false);}char &CrawlHashValue::get_byte(){GET_VAL(HV_BYTE, char, val.byte, 0);}short &CrawlHashValue::get_short(){GET_VAL(HV_SHORT, short, val._short, 0);}long &CrawlHashValue::get_long(){GET_VAL(HV_LONG, long, val._long, 0);}float &CrawlHashValue::get_float(){GET_VAL(HV_FLOAT, float, val._float, 0.0);}std::string &CrawlHashValue::get_string(){GET_VAL_PTR(HV_STR, std::string*, new std::string(""));}coord_def &CrawlHashValue::get_coord(){GET_VAL_PTR(HV_COORD, coord_def*, new coord_def());}CrawlHashTable &CrawlHashValue::get_table(){GET_VAL_PTR(HV_HASH, CrawlHashTable*, new CrawlHashTable());}item_def &CrawlHashValue::get_item(){GET_VAL_PTR(HV_ITEM, item_def*, new item_def());}///////////////////////////// Const accessor functions#define GET_CONST_SETUP(x) \ASSERT(!(flags & HFLAG_UNSET)); \ASSERT(type == (x));bool CrawlHashValue::get_bool() const{GET_CONST_SETUP(HV_BOOL);return val.boolean;}char CrawlHashValue::get_byte() const{GET_CONST_SETUP(HV_BYTE);return val.byte;}short CrawlHashValue::get_short() const{GET_CONST_SETUP(HV_SHORT);return val._short;}long CrawlHashValue::get_long() const{GET_CONST_SETUP(HV_LONG);return val._long;}float CrawlHashValue::get_float() const{GET_CONST_SETUP(HV_FLOAT);return val._float;}std::string CrawlHashValue::get_string() const{GET_CONST_SETUP(HV_STR);return *((std::string*)val.ptr);}coord_def CrawlHashValue::get_coord() const{GET_CONST_SETUP(HV_COORD);return *((coord_def*)val.ptr);}const CrawlHashTable& CrawlHashValue::get_table() const{GET_CONST_SETUP(HV_HASH);return *((CrawlHashTable*)val.ptr);}const item_def& CrawlHashValue::get_item() const{GET_CONST_SETUP(HV_ITEM);return *((item_def*)val.ptr);}/////////////////////// Typecast operators&CrawlHashValue::operator bool(){return get_bool();}&CrawlHashValue::operator char(){return get_byte();}&CrawlHashValue::operator short(){return get_short();}&CrawlHashValue::operator float(){return get_float();}&CrawlHashValue::operator long(){return get_long();}&CrawlHashValue::operator std::string(){return get_string();}&CrawlHashValue::operator coord_def(){return get_coord();}&CrawlHashValue::operator CrawlHashTable(){return get_table();}&CrawlHashValue::operator item_def(){return get_item();}///////////////////////////// Const typecast operatorsCrawlHashValue::operator bool() const{return get_bool();}#define CONST_INT_CAST() \switch(type) \{ \case HV_BYTE: \return get_byte(); \case HV_SHORT: \return get_short(); \case HV_LONG: \return get_long(); \default: \ASSERT(false); \return 0; \}CrawlHashValue::operator char() const{CONST_INT_CAST();}CrawlHashValue::operator short() const{CONST_INT_CAST();}CrawlHashValue::operator long() const{CONST_INT_CAST();}CrawlHashValue::operator float() const{return get_float();}CrawlHashValue::operator std::string() const{return get_string();}CrawlHashValue::operator coord_def() const{return get_coord();}///////////////////////// Assignment operatorsbool &CrawlHashValue::operator = (const bool &_val){return (get_bool() = _val);}char &CrawlHashValue::operator = (const char &_val){return (get_byte() = _val);}short &CrawlHashValue::operator = (const short &_val){return (get_short() = _val);}long &CrawlHashValue::operator = (const long &_val){return (get_long() = _val);}float &CrawlHashValue::operator = (const float &_val){return (get_float() = _val);}std::string &CrawlHashValue::operator = (const std::string &_val){return (get_string() = _val);}const char* CrawlHashValue::operator = (const char* _val){get_string() = _val;return get_string().c_str();}coord_def &CrawlHashValue::operator = (const coord_def &_val){return (get_coord() = _val);}CrawlHashTable &CrawlHashValue::operator = (const CrawlHashTable &_val){return (get_table() = _val);}item_def &CrawlHashValue::operator = (const item_def &_val){return (get_item() = _val);}///////////////////////////////////////////////////// Non-assignment operators which affect the lvalue#define INT_OPERATOR_UNARY(op) \switch(type) \{ \case HV_BYTE: \{ \char &temp = get_byte(); \temp op; \return temp; \} \\case HV_SHORT: \{ \short &temp = get_short(); \temp op; \return temp; \} \case HV_LONG: \{ \long &temp = get_long(); \temp op; \return temp; \} \\default: \ASSERT(false); \return 0; \}// Prefixlong CrawlHashValue::operator ++ (){INT_OPERATOR_UNARY(++);}long CrawlHashValue::operator -- (){INT_OPERATOR_UNARY(--);}// Postfixlong CrawlHashValue::operator ++ (int){INT_OPERATOR_UNARY(++);}long CrawlHashValue::operator -- (int){INT_OPERATOR_UNARY(--);}std::string &CrawlHashValue::operator += (const std::string &_val){return (get_string() += _val);}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////CrawlHashTable::CrawlHashTable(): type(HV_NONE), default_flags(0){}CrawlHashTable::CrawlHashTable(unsigned char flags): type(HV_NONE), default_flags(flags){ASSERT(!(default_flags & HFLAG_UNSET));}CrawlHashTable::CrawlHashTable(hash_val_type _type, unsigned char flags): type(_type), default_flags(flags){ASSERT(type >= HV_NONE && type < NUM_HASH_VAL_TYPES);ASSERT(!(default_flags & HFLAG_UNSET));}CrawlHashTable::~CrawlHashTable(){assert_validity();}//////////////////////////////// Read/write from/to savefilevoid CrawlHashTable::write(tagHeader &th) const{assert_validity();if (empty()){marshallByte(th, 0);return;}marshallByte(th, size());marshallByte(th, static_cast<char>(type));marshallByte(th, (char) default_flags);CrawlHashTable::hash_map_type::const_iterator i = hash_map.begin();for (; i != hash_map.end(); i++){marshallString(th, i->first);i->second.write(th);}assert_validity();}void CrawlHashTable::read(tagHeader &th){assert_validity();ASSERT(empty());ASSERT(type == HV_NONE);ASSERT(default_flags == 0);unsigned char _size = (unsigned char) unmarshallByte(th);if (_size == 0)return;type = static_cast<hash_val_type>(unmarshallByte(th));default_flags = (unsigned char) unmarshallByte(th);for (unsigned char i = 0; i < _size; i++){std::string key = unmarshallString(th);CrawlHashValue &val = (*this)[key];val.read(th);}assert_validity();}//////////////////// Misc functionsunsigned char CrawlHashTable::get_default_flags() const{assert_validity();return default_flags;}unsigned char CrawlHashTable::set_default_flags(unsigned char flags){assert_validity();ASSERT(!(flags & HFLAG_UNSET));default_flags |= flags;return default_flags;}unsigned char CrawlHashTable::unset_default_flags(unsigned char flags){assert_validity();ASSERT(!(flags & HFLAG_UNSET));default_flags &= ~flags;return default_flags;}hash_val_type CrawlHashTable::get_type() const{assert_validity();return type;}bool CrawlHashTable::exists(const std::string key) const{assert_validity();hash_map_type::const_iterator i = hash_map.find(key);return (i != hash_map.end());}bool CrawlHashTable::exists(const long index) const{char buf[12];sprintf(buf, "%ld", index);return exists(buf);}void CrawlHashTable::assert_validity() const{#if DEBUGASSERT(!(default_flags & HFLAG_UNSET));hash_map_type::const_iterator i = hash_map.begin();unsigned long actual_size = 0;for (; i != hash_map.end(); i++){actual_size++;const std::string &key = i->first;const CrawlHashValue &val = i->second;ASSERT(key != "");std::string trimmed = trimmed_string(key);ASSERT(key == trimmed);ASSERT(val.type != HV_NONE);ASSERT(!(val.flags & HFLAG_UNSET));switch(val.type){case HV_STR:case HV_COORD:case HV_ITEM:ASSERT(val.val.ptr != NULL);break;case HV_HASH:{ASSERT(val.val.ptr != NULL);CrawlHashTable* nested;nested = static_cast<CrawlHashTable*>(val.val.ptr);nested->assert_validity();break;}default:break;}}ASSERT(size() == actual_size);ASSERT(size() <= 255);#endif}int CrawlHashTable::compact_indicies(long min_index, long max_index,bool compact_down){ASSERT(min_index <= max_index);if (min_index == max_index)return 0;long min_exists, max_exists;for (min_exists = min_index; min_exists <= max_index; min_exists++)if (exists(min_exists))break;if (min_exists > max_index)return 0;for (max_exists = max_index; max_exists >= min_exists; max_exists--)if (exists(max_exists))break;if (max_exists <= min_exists)return 0;bool hole_found = false;for (long i = min_exists; i < max_exists; i++)if (!exists(i)){hole_found = true;break;}if (!hole_found)return 0;long start, stop, dir;if (compact_down){start = min_exists;stop = max_exists;dir = +1;}else{start = max_exists;stop = min_exists;dir = -1;}stop += dir;long move = 0;for (long i = start; i != stop; i += dir){if (!exists(i)){move++;continue;}if (move == 0)continue;char buf1[12], buf2[12];sprintf(buf1, "%ld", i - (move * dir));sprintf(buf2, "%ld", i);CrawlHashValue &val = hash_map[buf2];hash_map[buf1] = val;// Ensure a new()'d object isn't freed, since the other// CrawlHashValue now owns it.val.type = HV_BOOL;erase(buf2);}return move;}bool CrawlHashTable::fixup_indexed_array(std::string name){bool problems = false;long max_index = 0;std::vector<std::string> bad_keys;for (hash_map_type::iterator i = begin(); i != end(); i++){int index = strtol(i->first.c_str(), NULL, 10);if (index < 0){if (name != "")mprf("Negative index %ld found in %s.",index, name.c_str());problems = true;bad_keys.push_back(i->first);}else if (index == 0 && i->first != "0"){if (name != "")mprf("Non-numerical index '%s' found in %s.",i->first.c_str(), name.c_str());problems = true;bad_keys.push_back(i->first);}else if (index > max_index)max_index = index;}for (unsigned long i = 0, _size = bad_keys.size(); i < _size; i++)erase(bad_keys[i]);int holes = compact_indicies(0, max_index);if (holes > 0 && name != "")mprf("%d holes found in %s.", holes, name.c_str());return problems;}////////////// AccessorsCrawlHashValue& CrawlHashTable::get_value(const std::string &key){assert_validity();hash_map_type::iterator i = hash_map.find(key);if (i == hash_map.end()){hash_map[key] = CrawlHashValue(default_flags);CrawlHashValue &val = hash_map[key];if (type != HV_NONE){val.type = type;val.flags |= HFLAG_CONST_TYPE;}return (val);}return (i->second);}CrawlHashValue& CrawlHashTable::get_value(const long &index){char buf[12];sprintf(buf, "%ld", index);return get_value(buf);}const CrawlHashValue& CrawlHashTable::get_value(const std::string &key) const{assert_validity();hash_map_type::const_iterator i = hash_map.find(key);ASSERT(i != hash_map.end());ASSERT(i->second.type != HV_NONE);ASSERT(!(i->second.flags & HFLAG_UNSET));return (i->second);}const CrawlHashValue& CrawlHashTable::get_value(const long &index) const{char buf[12];sprintf(buf, "%ld", index);return get_value(buf);}CrawlHashValue& CrawlHashTable::operator[] (const std::string &key){return get_value(key);}CrawlHashValue& CrawlHashTable::operator[] (const long &index){return get_value(index);}const CrawlHashValue& CrawlHashTable::operator[] (const std::string &key) const{return get_value(key);}const CrawlHashValue& CrawlHashTable::operator[] (const long &index) const{return get_value(index);}///////////////////////////// std::map style interfacesize_t CrawlHashTable::size() const{return hash_map.size();}bool CrawlHashTable::empty() const{return hash_map.empty();}void CrawlHashTable::erase(const std::string key){assert_validity();hash_map_type::iterator i = hash_map.find(key);if (i != hash_map.end()){CrawlHashValue &val = i->second;ASSERT(!(val.flags & HFLAG_NO_ERASE));hash_map.erase(i);}}void CrawlHashTable::erase(const long index){char buf[12];sprintf(buf, "%ld", index);erase(buf);}void CrawlHashTable::clear(){assert_validity();ASSERT(!(default_flags & HFLAG_NO_ERASE));hash_map_type::iterator i = hash_map.begin();for (; i != hash_map.end(); i++)ASSERT(!(i->second.flags & HFLAG_NO_ERASE));hash_map.clear();}CrawlHashTable::hash_map_type::iterator CrawlHashTable::begin(){assert_validity();return hash_map.begin();}CrawlHashTable::hash_map_type::iterator CrawlHashTable::end(){assert_validity();return hash_map.end();}CrawlHashTable::hash_map_type::const_iterator CrawlHashTable::begin() const{assert_validity();return hash_map.begin();}CrawlHashTable::hash_map_type::const_iterator CrawlHashTable::end() const{assert_validity();return hash_map.end();}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////template <typename T, hash_val_type TYPE>CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(){table = NULL;}template <typename T, hash_val_type TYPE>CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(CrawlHashTable& _table){wrap(_table);}template <typename T, hash_val_type TYPE>CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(CrawlHashTable* _table){wrap(_table);}template <typename T, hash_val_type TYPE>void CrawlTableWrapper<T, TYPE>::wrap(CrawlHashTable& _table){wrap(&_table);}template <typename T, hash_val_type TYPE>void CrawlTableWrapper<T, TYPE>::wrap(CrawlHashTable* _table){ASSERT(_table != NULL);ASSERT(_table->get_type() == TYPE);table = _table;}template <typename T, hash_val_type TYPE>T& CrawlTableWrapper<T, TYPE>::operator[] (const std::string &key){return (T&) (*table)[key];}template <typename T, hash_val_type TYPE>T& CrawlTableWrapper<T, TYPE>::operator[] (const long &index){return (T&) (*table)[index];}template <typename T, hash_val_type TYPE>const T CrawlTableWrapper<T, TYPE>::operator[] (const std::string &key) const{return (T) (*table)[key];}template <typename T, hash_val_type TYPE>const T CrawlTableWrapper<T, TYPE>::operator[] (const long &index) const{return (T) (*table)[index];}
/** File: store.cc* Summary: Saveable hash-table and vector capable of storing* multiple types of data.* Written by: Matthew Cline** Modified for Crawl Reference by $Author$ on $Date$** Change History (most recent first):* <1> 10/5/07 MPC Created*/#ifndef STORE_H#define STORE_H#include <limits.h>#include <map>#include <string>#include <vector>struct tagHeader;class CrawlHashTable;class CrawlVector;class item_def;class coord_def;typedef unsigned char hash_size;typedef unsigned char vec_size;typedef unsigned char store_flags;#define VEC_MAX_SIZE 255#define HASH_MAX_SIZE 255// NOTE: Changing the ordering of these enums will break savefile// compatibility.enum store_val_type{SV_NONE = 0,SV_BOOL,SV_BYTE,SV_SHORT,SV_LONG,SV_FLOAT,SV_STR,SV_COORD,SV_ITEM,SV_HASH,SV_VEC,NUM_STORE_VAL_TYPES};enum store_flag_type{SFLAG_UNSET = (1 << 0),SFLAG_CONST_VAL = (1 << 1),SFLAG_CONST_TYPE = (1 << 2),SFLAG_NO_ERASE = (1 << 3)};// Can't just cast everything into a void pointer, since a float might// not fit into a pointer on all systems.typedef union StoreUnion StoreUnion;union StoreUnion{bool boolean;char byte;short _short;long _long;float _float;void* ptr;};class CrawlStoreValue{public:CrawlStoreValue();CrawlStoreValue(const CrawlStoreValue &other);~CrawlStoreValue();// Conversion constructorsCrawlStoreValue(const bool val);CrawlStoreValue(const char &val);CrawlStoreValue(const short &val);CrawlStoreValue(const long &val);CrawlStoreValue(const float &val);CrawlStoreValue(const std::string &val);CrawlStoreValue(const char* val);CrawlStoreValue(const coord_def &val);CrawlStoreValue(const item_def &val);CrawlStoreValue(const CrawlHashTable &val);CrawlStoreValue(const CrawlVector &val);// Only needed for doing some assertion checking.CrawlStoreValue &operator = (const CrawlStoreValue &other);protected:store_val_type type;store_flags flags;StoreUnion val;public:store_flags get_flags() const;store_flags set_flags(store_flags flags);store_flags unset_flags(store_flags flags);store_val_type get_type() const;CrawlHashTable &new_table(store_flags flags);CrawlHashTable &new_table(store_val_type type, store_flags flags = 0);CrawlVector &new_vector(store_flags flags,vec_size max_size = VEC_MAX_SIZE);CrawlVector &new_vector(store_val_type type, store_flags flags = 0,vec_size max_size = VEC_MAX_SIZE);bool &get_bool();char &get_byte();short &get_short();long &get_long();float &get_float();std::string &get_string();coord_def &get_coord();CrawlHashTable &get_table();CrawlVector &get_vector();item_def &get_item();bool get_bool() const;char get_byte() const;short get_short() const;long get_long() const;float get_float() const;std::string get_string() const;coord_def get_coord() const;const CrawlHashTable& get_table() const;const CrawlVector& get_vector() const;const item_def& get_item() const;void set_bool(const bool val);void set_byte(const char val);void set_short(const short val);void set_long(const long val);void set_float(const float val);void set_string(const std::string &val);void set_coord(const coord_def &val);void set_table(const CrawlHashTable &val);void set_vector(const CrawlVector &val);void set_item(const item_def &val);public:// NOTE: All operators will assert if the alue is of the wrong// type for the operation. If the value has no type yet, the// operation will set it to the appropriate type. If the value// has no type yet and the operation modifies the existing value// rather than replacing it (i.e., ++) the value will be set to a// default before the operation is done.// If the value is a hash table or vector, the container's values// can be accessed with the [] operator with the approriate key// type (strings for hashes, longs for vectors).CrawlStoreValue &operator [] (const std::string &key);CrawlStoreValue &operator [] (const vec_size &index);const CrawlStoreValue &operator [] (const std::string &key) const;const CrawlStoreValue &operator [] (const vec_size &index) const;// Typecast operators&operator bool();&operator char();&operator short();&operator long();&operator float();&operator std::string();&operator coord_def();&operator CrawlHashTable();&operator CrawlVector();&operator item_def();operator bool() const;operator char() const;operator short() const;operator long() const;operator float() const;operator std::string() const;operator coord_def() const;// Assignment operatorsCrawlStoreValue &operator = (const bool &val);CrawlStoreValue &operator = (const char &val);CrawlStoreValue &operator = (const short &val);CrawlStoreValue &operator = (const long &val);CrawlStoreValue &operator = (const float &val);CrawlStoreValue &operator = (const std::string &val);CrawlStoreValue &operator = (const char* val);CrawlStoreValue &operator = (const coord_def &val);CrawlStoreValue &operator = (const CrawlHashTable &val);CrawlStoreValue &operator = (const CrawlVector &val);CrawlStoreValue &operator = (const item_def &val);// Misc operatorsstd::string &operator += (const std::string &val);// Prefixlong operator ++ ();long operator -- ();// Postfixlong operator ++ (int);long operator -- (int);protected:CrawlStoreValue(const store_flags flags,const store_val_type type = SV_NONE);void write(tagHeader &th) const;void read(tagHeader &th);void unset(bool force = false);friend class CrawlHashTable;friend class CrawlVector;};// A hash table can have a maximum of 255 key/value pairs. If you// want more than that you can use nested hash tables.//// By default a hash table's value data types are heterogeneous. To// make it homogeneous (which causes dynamic type checking) you have// to give a type to the hash table constructor; once it's been// created it's type (or lack of type) is immutable.//// An empty hash table will take up only 1 byte in the savefile. A// non-empty hash table will have an overhead of 3 bytes for the hash// table overall and 2 bytes per key/value pair, over and above the// number of bytes needed to store the keys and values themselves.class CrawlHashTable{public:CrawlHashTable();CrawlHashTable(store_flags flags);CrawlHashTable(store_val_type type, store_flags flags = 0);~CrawlHashTable();typedef std::map<std::string, CrawlStoreValue> hash_map_type;typedef hash_map_type::iterator iterator;typedef hash_map_type::const_iterator const_iterator;protected:store_val_type type;store_flags default_flags;hash_map_type hash_map;friend class CrawlStoreValue;public:void write(tagHeader &th) const;void read(tagHeader &th);store_flags get_default_flags() const;store_flags set_default_flags(store_flags flags);store_flags unset_default_flags(store_flags flags);store_val_type get_type() const;bool exists(const std::string key) const;void assert_validity() const;// NOTE: If get_value() or [] is given a key which doesn't exist// in the table, an unset/empty CrawlStoreValue will be created// with that key and returned. If it is not then given a value// then the next call to assert_validity() will fail. If the// hash table has a type (rather than being heterogeneous)// then trying to assign a different type to the CrawlStoreValue// will assert.CrawlStoreValue& get_value(const std::string &key);CrawlStoreValue& operator[] (const std::string &key);// NOTE: If the const versions of get_value() or [] are given a// key which doesn't exist, they will assert.const CrawlStoreValue& get_value(const std::string &key) const;const CrawlStoreValue& operator[] (const std::string &key) const;// std::map style interfacehash_size size() const;bool empty() const;void erase(const std::string key);void clear();iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;};// A CrawlVector is the vector version of CrawlHashTable, except that// a non-empty CrawlVector has one more byte of savefile overhead that// a hash table, and that can specify a maximum size to make it act// similarly to a FixedVec.class CrawlVector{public:CrawlVector();CrawlVector(store_flags flags, vec_size max_size = VEC_MAX_SIZE);CrawlVector(store_val_type type, store_flags flags = 0,vec_size max_size = VEC_MAX_SIZE);~CrawlVector();typedef std::vector<CrawlStoreValue> vector_type;typedef vector_type::iterator iterator;typedef vector_type::const_iterator const_iterator;protected:store_val_type type;store_flags default_flags;vec_size max_size;vector_type vector;friend class CrawlStoreValue;public:void write(tagHeader &th) const;void read(tagHeader &th);store_flags get_default_flags() const;store_flags set_default_flags(store_flags flags);store_flags unset_default_flags(store_flags flags);store_val_type get_type() const;void assert_validity() const;void set_max_size(vec_size size);vec_size get_max_size() const;CrawlStoreValue& get_value(const vec_size &index);CrawlStoreValue& operator[] (const vec_size &index);// NOTE: If the const versions of get_value() or [] are given a// index which doesn't exist, they will assert.const CrawlStoreValue& get_value(const vec_size &index) const;const CrawlStoreValue& operator[] (const vec_size &index) const;// std::vector style interfacevec_size size() const;bool empty() const;// NOTE: push_back() and insert() have val passed by value rather// than by reference so that coversion constructors will work.CrawlStoreValue& pop_back();void push_back(CrawlStoreValue val);void insert(const vec_size index, CrawlStoreValue val);// resize() will assert if the maximum size has been set.void resize(const vec_size size);void erase(const vec_size index);void clear();iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;};// A wrapper for non-heterogeneous hash tables, so that the values can// be accessed without using get_foo(). T needs to have both normal// and const type-cast operators defined by CrawlStoreValue for this// template to work.template <typename T, store_val_type TYPE>class CrawlTableWrapper{public:CrawlTableWrapper();CrawlTableWrapper(CrawlHashTable& table);CrawlTableWrapper(CrawlHashTable* table);protected:CrawlHashTable* table;public:void wrap(CrawlHashTable& table);void wrap(CrawlHashTable* table);CrawlHashTable* get_table();T& operator[] (const std::string &key);const CrawlHashTable* get_table() const;const T operator[] (const std::string &key) const;};typedef CrawlTableWrapper<bool, SV_BOOL> CrawlBoolTable;typedef CrawlTableWrapper<char, SV_BYTE> CrawlByteTable;typedef CrawlTableWrapper<short, SV_SHORT> CrawlShortTable;typedef CrawlTableWrapper<long, SV_LONG> CrawlLongTable;typedef CrawlTableWrapper<float, SV_FLOAT> CrawlFloatTable;typedef CrawlTableWrapper<std::string, SV_STR> CrawlStringTable;typedef CrawlTableWrapper<coord_def, SV_COORD> CrawlCoordTable;#endif
/** File: store.cc* Summary: Saveable hash-table and vector capable of storing* multiple types of data.* Written by: Matthew Cline** Modified for Crawl Reference by $Author$ on $Date$** Change History (most recent first):* <1> 10/5/07 MPC Created*/#include "AppHdr.h"#include "store.h"#include "externs.h"#include "tags.h"CrawlStoreValue::CrawlStoreValue(): type(SV_NONE), flags(SFLAG_UNSET){val.ptr = NULL;}CrawlStoreValue::CrawlStoreValue(const CrawlStoreValue &other){ASSERT(other.type >= SV_NONE && other.type < NUM_STORE_VAL_TYPES);type = other.type;flags = other.flags;if (flags & SFLAG_UNSET){val = other.val;return;}switch (type){case SV_NONE:case SV_BOOL:case SV_BYTE:case SV_SHORT:case SV_LONG:case SV_FLOAT:val = other.val;break;case SV_STR:{std::string* str;str = new std::string(*static_cast<std::string*>(other.val.ptr));val.ptr = static_cast<void*>(str);break;}case SV_COORD:{coord_def* coord;coord = new coord_def(*static_cast<coord_def*>(other.val.ptr));val.ptr = static_cast<void*>(coord);break;}case SV_ITEM:{item_def* item;item = new item_def(*static_cast<item_def*>(other.val.ptr));val.ptr = static_cast<void*>(item);break;}case SV_HASH:{CrawlHashTable* hash;CrawlHashTable* tmp = static_cast<CrawlHashTable*>(other.val.ptr);hash = new CrawlHashTable(*tmp);val.ptr = static_cast<void*>(hash);break;}case SV_VEC:{CrawlVector* vec;CrawlVector* tmp = static_cast<CrawlVector*>(other.val.ptr);vec = new CrawlVector(*tmp);val.ptr = static_cast<void*>(vec);break;}case NUM_STORE_VAL_TYPES:ASSERT(false);}}CrawlStoreValue::CrawlStoreValue(const store_flags _flags,const store_val_type _type): type(_type), flags(_flags){ASSERT(type >= SV_NONE && type < NUM_STORE_VAL_TYPES);ASSERT(!(flags & SFLAG_UNSET));flags |= SFLAG_UNSET;val.ptr = NULL;}// Conversion constructorsCrawlStoreValue::CrawlStoreValue(const bool _val): type(SV_BOOL), flags(0){get_bool() = _val;}CrawlStoreValue::CrawlStoreValue(const char &_val): type(SV_BYTE), flags(0){get_byte() = _val;}CrawlStoreValue::CrawlStoreValue(const short &_val): type(SV_SHORT), flags(0){get_short() = _val;}CrawlStoreValue::CrawlStoreValue(const long &_val): type(SV_LONG), flags(0){get_long() = _val;}CrawlStoreValue::CrawlStoreValue(const float &_val): type(SV_FLOAT), flags(0){get_float() = _val;}CrawlStoreValue::CrawlStoreValue(const std::string &_val): type(SV_STR), flags(0){get_string() = _val;}CrawlStoreValue::CrawlStoreValue(const char* _val): type(SV_STR), flags(0){get_string() = _val;}CrawlStoreValue::CrawlStoreValue(const coord_def &_val): type(SV_COORD), flags(0){get_coord() = _val;}CrawlStoreValue::CrawlStoreValue(const item_def &_val): type(SV_ITEM), flags(0){get_item() = _val;}CrawlStoreValue::CrawlStoreValue(const CrawlHashTable &_val): type(SV_HASH), flags(0){get_table() = _val;}CrawlStoreValue::CrawlStoreValue(const CrawlVector &_val): type(SV_VEC), flags(0){get_vector() = _val;}CrawlStoreValue::~CrawlStoreValue(){unset(true);}void CrawlStoreValue::unset(bool force){if (flags & SFLAG_UNSET)return;if (force)flags &= ~SFLAG_NO_ERASE;ASSERT(!(flags & SFLAG_NO_ERASE));switch (type){case SV_BOOL:val.boolean = false;break;case SV_BYTE:val.byte = 0;break;case SV_SHORT:val._short = 0;break;case SV_LONG:val._long = 0;break;case SV_FLOAT:val._float = 0.0;break;case SV_STR:{std::string* str = static_cast<std::string*>(val.ptr);delete str;val.ptr = NULL;break;}case SV_COORD:{coord_def* coord = static_cast<coord_def*>(val.ptr);delete coord;val.ptr = NULL;break;}case SV_ITEM:{item_def* item = static_cast<item_def*>(val.ptr);delete item;val.ptr = NULL;break;}case SV_HASH:{CrawlHashTable* hash = static_cast<CrawlHashTable*>(val.ptr);delete hash;val.ptr = NULL;break;}case SV_VEC:{CrawlVector* vec = static_cast<CrawlVector*>(val.ptr);delete vec;val.ptr = NULL;break;}case SV_NONE:ASSERT(false);case NUM_STORE_VAL_TYPES:ASSERT(false);}flags |= SFLAG_UNSET;}// Only needed to do some assertion checking.CrawlStoreValue &CrawlStoreValue::operator = (const CrawlStoreValue &other){ASSERT(other.type >= SV_NONE && other.type < NUM_STORE_VAL_TYPES);ASSERT(other.type != SV_NONE || type == SV_NONE);// NOTE: We don't bother checking SFLAG_CONST_VAL, since the// asignment operator is used when swapping two elements.if (!(flags & SFLAG_UNSET)){if (flags & SFLAG_CONST_TYPE)ASSERT(type == SV_NONE || type == other.type);}type = other.type;flags = other.flags;val = other.val;return (*this);}///////////////////////////////////// Meta-data accessors and changersstore_flags CrawlStoreValue::get_flags() const{return flags;}store_flags CrawlStoreValue::set_flags(store_flags _flags){flags |= _flags;return flags;}store_flags CrawlStoreValue::unset_flags(store_flags _flags){flags &= ~_flags;return flags;}store_val_type CrawlStoreValue::get_type() const{return type;}//////////////////////////////// Read/write from/to savefilevoid CrawlStoreValue::write(tagHeader &th) const{ASSERT(!(flags & SFLAG_UNSET));marshallByte(th, (char) type);marshallByte(th, (char) flags);switch (type){case SV_BOOL:marshallBoolean(th, val.boolean);break;case SV_BYTE:marshallByte(th, val.byte);break;case SV_SHORT:marshallShort(th, val._short);break;case SV_LONG:marshallLong(th, val._long);break;case SV_FLOAT:marshallFloat(th, val._float);break;case SV_STR:{std::string* str = static_cast<std::string*>(val.ptr);marshallString(th, *str);break;}case SV_COORD:{coord_def* coord = static_cast<coord_def*>(val.ptr);marshallCoord(th, *coord);break;}case SV_ITEM:{item_def* item = static_cast<item_def*>(val.ptr);marshallItem(th, *item);break;}case SV_HASH:{CrawlHashTable* hash = static_cast<CrawlHashTable*>(val.ptr);hash->write(th);break;}case SV_VEC:{CrawlVector* vec = static_cast<CrawlVector*>(val.ptr);vec->write(th);break;}case SV_NONE:ASSERT(false);case NUM_STORE_VAL_TYPES:ASSERT(false);}}void CrawlStoreValue::read(tagHeader &th){type = static_cast<store_val_type>(unmarshallByte(th));flags = (store_flags) unmarshallByte(th);ASSERT(!(flags & SFLAG_UNSET));switch (type){case SV_BOOL:val.boolean = unmarshallBoolean(th);break;case SV_BYTE:val.byte = unmarshallByte(th);break;case SV_SHORT:val._short = unmarshallShort(th);break;case SV_LONG:val._long = unmarshallLong(th);break;case SV_FLOAT:val._float = unmarshallFloat(th);break;case SV_STR:{std::string str = unmarshallString(th);val.ptr = (void*) new std::string(str);break;}case SV_COORD:{coord_def coord;unmarshallCoord(th, coord);val.ptr = (void*) new coord_def(coord);break;}case SV_ITEM:{item_def item;unmarshallItem(th, item);val.ptr = (void*) new item_def(item);break;}case SV_HASH:{CrawlHashTable* hash = new CrawlHashTable();hash->read(th);val.ptr = (void*) hash;break;}case SV_VEC:{CrawlVector* vec = new CrawlVector();vec->read(th);val.ptr = (void*) vec;break;}case SV_NONE:ASSERT(false);case NUM_STORE_VAL_TYPES:ASSERT(false);}}////////////////////////////////////////////////////////////////// Setup a new table with the given flags and/or type; assert if// a table already exists.CrawlHashTable &CrawlStoreValue::new_table(store_flags _flags){return new_table(SV_NONE, flags);}CrawlHashTable &CrawlStoreValue::new_table(store_val_type _type,store_flags _flags){CrawlHashTable* old_table = static_cast<CrawlHashTable*>(val.ptr);ASSERT(flags & SFLAG_UNSET);ASSERT(type == SV_NONE|| (type == SV_HASH&& old_table->size() == 0&& old_table->get_type() == SV_NONE&& old_table->get_default_flags() == 0));CrawlHashTable &table = get_table();table.default_flags = _flags;table.type = _type;type = SV_HASH;flags &= ~SFLAG_UNSET;return table;}////////////////////////////////////////////////////////////////// Setup a new vector with the given flags and/or type; assert if// a vector already exists.CrawlVector &CrawlStoreValue::new_vector(store_flags _flags,vec_size max_size){return new_vector(SV_NONE, flags, max_size);}CrawlVector &CrawlStoreValue::new_vector(store_val_type _type,store_flags _flags,vec_size _max_size){CrawlVector* old_vector = static_cast<CrawlVector*>(val.ptr);ASSERT(flags & SFLAG_UNSET);ASSERT(type == SV_NONE|| (type == SV_VEC&& old_vector->size() == 0&& old_vector->get_type() == SV_NONE&& old_vector->get_default_flags() == 0&& old_vector->get_max_size() == VEC_MAX_SIZE));CrawlVector &vector = get_vector();vector.default_flags = _flags;vector.type = _type;type = SV_VEC;flags &= ~SFLAG_UNSET;return vector;}///////////////////////////////////////////// Dynamic type-checking accessor functions#define GET_VAL(x, _type, field, value) \ASSERT((flags & SFLAG_UNSET) || !(flags & SFLAG_CONST_VAL)); \if (type != (x)) \{ \if (type == SV_NONE) \{ \type = (x); \field = (value); \} \else \{ \ASSERT(!(flags & SFLAG_CONST_TYPE)); \switch(type) \{ \case SV_BOOL: \field = (_type) val.boolean; \break; \case SV_BYTE: \field = (_type) val.byte; \break; \case SV_SHORT: \field = (_type) val._short; \break; \case SV_LONG: \field = (_type) val._long; \break; \case SV_FLOAT: \field = (_type) val._float; \break; \default: \ASSERT(false); \} \type = (x); \} \} \flags &= ~SFLAG_UNSET; \return field;#define GET_VAL_PTR(x, _type, value) \ASSERT((flags & SFLAG_UNSET) || !(flags & SFLAG_CONST_VAL)); \if (type != (x)) \{ \if (type == SV_NONE) \{ \type = (x); \val.ptr = (value); \} \else \{ \unset(); \val.ptr = (value); \type = (x); \} \} \flags &= ~SFLAG_UNSET; \return *((_type) val.ptr);bool &CrawlStoreValue::get_bool(){GET_VAL(SV_BOOL, bool, val.boolean, false);}char &CrawlStoreValue::get_byte(){GET_VAL(SV_BYTE, char, val.byte, 0);}short &CrawlStoreValue::get_short(){GET_VAL(SV_SHORT, short, val._short, 0);}long &CrawlStoreValue::get_long(){GET_VAL(SV_LONG, long, val._long, 0);}float &CrawlStoreValue::get_float(){GET_VAL(SV_FLOAT, float, val._float, 0.0);}std::string &CrawlStoreValue::get_string(){GET_VAL_PTR(SV_STR, std::string*, new std::string(""));}coord_def &CrawlStoreValue::get_coord(){GET_VAL_PTR(SV_COORD, coord_def*, new coord_def());}item_def &CrawlStoreValue::get_item(){GET_VAL_PTR(SV_ITEM, item_def*, new item_def());}CrawlHashTable &CrawlStoreValue::get_table(){GET_VAL_PTR(SV_HASH, CrawlHashTable*, new CrawlHashTable());}CrawlVector &CrawlStoreValue::get_vector(){GET_VAL_PTR(SV_VEC, CrawlVector*, new CrawlVector());}CrawlStoreValue &CrawlStoreValue::operator [] (const std::string &key){return get_table()[key];}CrawlStoreValue &CrawlStoreValue::operator [] (const vec_size &index){return get_vector()[index];}///////////////////////////// Const accessor functions#define GET_CONST_SETUP(x) \ASSERT(!(flags & SFLAG_UNSET)); \ASSERT(type == (x));bool CrawlStoreValue::get_bool() const{GET_CONST_SETUP(SV_BOOL);return val.boolean;}char CrawlStoreValue::get_byte() const{GET_CONST_SETUP(SV_BYTE);return val.byte;}short CrawlStoreValue::get_short() const{GET_CONST_SETUP(SV_SHORT);return val._short;}long CrawlStoreValue::get_long() const{GET_CONST_SETUP(SV_LONG);return val._long;}float CrawlStoreValue::get_float() const{GET_CONST_SETUP(SV_FLOAT);return val._float;}std::string CrawlStoreValue::get_string() const{GET_CONST_SETUP(SV_STR);return *((std::string*)val.ptr);}coord_def CrawlStoreValue::get_coord() const{GET_CONST_SETUP(SV_COORD);return *((coord_def*)val.ptr);}const item_def& CrawlStoreValue::get_item() const{GET_CONST_SETUP(SV_ITEM);return *((item_def*)val.ptr);}const CrawlHashTable& CrawlStoreValue::get_table() const{GET_CONST_SETUP(SV_HASH);return *((CrawlHashTable*)val.ptr);}const CrawlVector& CrawlStoreValue::get_vector() const{GET_CONST_SETUP(SV_VEC);return *((CrawlVector*)val.ptr);}const CrawlStoreValue &CrawlStoreValue::operator[] (const std::string &key) const{return get_table()[key];}const CrawlStoreValue &CrawlStoreValue::operator[](const vec_size &index) const{return get_vector()[index];}/////////////////////// Typecast operators&CrawlStoreValue::operator bool(){return get_bool();}&CrawlStoreValue::operator char(){return get_byte();}&CrawlStoreValue::operator short(){return get_short();}&CrawlStoreValue::operator float(){return get_float();}&CrawlStoreValue::operator long(){return get_long();}&CrawlStoreValue::operator std::string(){return get_string();}&CrawlStoreValue::operator coord_def(){return get_coord();}&CrawlStoreValue::operator CrawlHashTable(){return get_table();}&CrawlStoreValue::operator CrawlVector(){return get_vector();}&CrawlStoreValue::operator item_def(){return get_item();}///////////////////////////// Const typecast operatorsCrawlStoreValue::operator bool() const{return get_bool();}#define CONST_INT_CAST() \switch(type) \{ \case SV_BYTE: \return get_byte(); \case SV_SHORT: \return get_short(); \case SV_LONG: \return get_long(); \default: \ASSERT(false); \return 0; \}CrawlStoreValue::operator char() const{CONST_INT_CAST();}CrawlStoreValue::operator short() const{CONST_INT_CAST();}CrawlStoreValue::operator long() const{CONST_INT_CAST();}CrawlStoreValue::operator float() const{return get_float();}CrawlStoreValue::operator std::string() const{return get_string();}CrawlStoreValue::operator coord_def() const{return get_coord();}///////////////////////// Assignment operatorsCrawlStoreValue &CrawlStoreValue::operator = (const bool &_val){get_bool() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const char &_val){get_byte() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const short &_val){get_short() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const long &_val){get_long() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const float &_val){get_float() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const std::string &_val){get_string() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const char* _val){get_string() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const coord_def &_val){get_coord() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const CrawlHashTable &_val){get_table() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const CrawlVector &_val){get_vector() = _val;return (*this);}CrawlStoreValue &CrawlStoreValue::operator = (const item_def &_val){get_item() = _val;return (*this);}///////////////////////////////////////////////////// Non-assignment operators which affect the lvalue#define INT_OPERATOR_UNARY(op) \switch(type) \{ \case SV_BYTE: \{ \char &temp = get_byte(); \temp op; \return temp; \} \\case SV_SHORT: \{ \short &temp = get_short(); \temp op; \return temp; \} \case SV_LONG: \{ \long &temp = get_long(); \temp op; \return temp; \} \\default: \ASSERT(false); \return 0; \}// Prefixlong CrawlStoreValue::operator ++ (){INT_OPERATOR_UNARY(++);}long CrawlStoreValue::operator -- (){INT_OPERATOR_UNARY(--);}// Postfixlong CrawlStoreValue::operator ++ (int){INT_OPERATOR_UNARY(++);}long CrawlStoreValue::operator -- (int){INT_OPERATOR_UNARY(--);}std::string &CrawlStoreValue::operator += (const std::string &_val){return (get_string() += _val);}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////CrawlHashTable::CrawlHashTable(): type(SV_NONE), default_flags(0){}CrawlHashTable::CrawlHashTable(store_flags flags): type(SV_NONE), default_flags(flags){ASSERT(!(default_flags & SFLAG_UNSET));}CrawlHashTable::CrawlHashTable(store_val_type _type, store_flags flags): type(_type), default_flags(flags){ASSERT(type >= SV_NONE && type < NUM_STORE_VAL_TYPES);ASSERT(!(default_flags & SFLAG_UNSET));}CrawlHashTable::~CrawlHashTable(){assert_validity();}//////////////////////////////// Read/write from/to savefilevoid CrawlHashTable::write(tagHeader &th) const{assert_validity();if (empty()){marshallByte(th, 0);return;}marshallByte(th, size());marshallByte(th, static_cast<char>(type));marshallByte(th, (char) default_flags);CrawlHashTable::hash_map_type::const_iterator i = hash_map.begin();for (; i != hash_map.end(); i++){marshallString(th, i->first);i->second.write(th);}assert_validity();}void CrawlHashTable::read(tagHeader &th){assert_validity();ASSERT(empty());ASSERT(type == SV_NONE);ASSERT(default_flags == 0);hash_size _size = (hash_size) unmarshallByte(th);if (_size == 0)return;type = static_cast<store_val_type>(unmarshallByte(th));default_flags = (store_flags) unmarshallByte(th);for (hash_size i = 0; i < _size; i++){std::string key = unmarshallString(th);CrawlStoreValue &val = (*this)[key];val.read(th);}assert_validity();}//////////////////// Misc functionsstore_flags CrawlHashTable::get_default_flags() const{assert_validity();return default_flags;}store_flags CrawlHashTable::set_default_flags(store_flags flags){assert_validity();ASSERT(!(flags & SFLAG_UNSET));default_flags |= flags;return default_flags;}store_flags CrawlHashTable::unset_default_flags(store_flags flags){assert_validity();ASSERT(!(flags & SFLAG_UNSET));default_flags &= ~flags;return default_flags;}store_val_type CrawlHashTable::get_type() const{assert_validity();return type;}bool CrawlHashTable::exists(const std::string key) const{assert_validity();hash_map_type::const_iterator i = hash_map.find(key);return (i != hash_map.end());}void CrawlHashTable::assert_validity() const{#if DEBUGASSERT(!(default_flags & SFLAG_UNSET));hash_map_type::const_iterator i = hash_map.begin();unsigned long actual_size = 0;for (; i != hash_map.end(); i++){actual_size++;const std::string &key = i->first;const CrawlStoreValue &val = i->second;ASSERT(key != "");std::string trimmed = trimmed_string(key);ASSERT(key == trimmed);ASSERT(val.type != SV_NONE);ASSERT(!(val.flags & SFLAG_UNSET));switch(val.type){case SV_STR:case SV_COORD:case SV_ITEM:ASSERT(val.val.ptr != NULL);break;case SV_HASH:{ASSERT(val.val.ptr != NULL);CrawlHashTable* nested;nested = static_cast<CrawlHashTable*>(val.val.ptr);nested->assert_validity();break;}default:break;}}ASSERT(size() == actual_size);#endif}////////////////////////////////// Accessors to contained valuesCrawlStoreValue& CrawlHashTable::get_value(const std::string &key){assert_validity();iterator i = hash_map.find(key);if (i == hash_map.end()){hash_map[key] = CrawlStoreValue(default_flags);CrawlStoreValue &val = hash_map[key];if (type != SV_NONE){val.type = type;val.flags |= SFLAG_CONST_TYPE;}return (val);}return (i->second);}const CrawlStoreValue& CrawlHashTable::get_value(const std::string &key) const{assert_validity();hash_map_type::const_iterator i = hash_map.find(key);ASSERT(i != hash_map.end());ASSERT(i->second.type != SV_NONE);ASSERT(!(i->second.flags & SFLAG_UNSET));return (i->second);}CrawlStoreValue& CrawlHashTable::operator[] (const std::string &key){return get_value(key);}const CrawlStoreValue& CrawlHashTable::operator[] (const std::string &key)const{return get_value(key);}///////////////////////////// std::map style interfacehash_size CrawlHashTable::size() const{return hash_map.size();}bool CrawlHashTable::empty() const{return hash_map.empty();}void CrawlHashTable::erase(const std::string key){assert_validity();iterator i = hash_map.find(key);if (i != hash_map.end()){CrawlStoreValue &val = i->second;ASSERT(!(val.flags & SFLAG_NO_ERASE));hash_map.erase(i);}}void CrawlHashTable::clear(){assert_validity();ASSERT(!(default_flags & SFLAG_NO_ERASE));iterator i = hash_map.begin();for (; i != hash_map.end(); i++)ASSERT(!(i->second.flags & SFLAG_NO_ERASE));hash_map.clear();}CrawlHashTable::iterator CrawlHashTable::begin(){assert_validity();return hash_map.begin();}CrawlHashTable::iterator CrawlHashTable::end(){assert_validity();return hash_map.end();}CrawlHashTable::const_iterator CrawlHashTable::begin() const{assert_validity();return hash_map.begin();}CrawlHashTable::const_iterator CrawlHashTable::end() const{assert_validity();return hash_map.end();}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////CrawlVector::CrawlVector(): type(SV_NONE), default_flags(0), max_size(VEC_MAX_SIZE){}CrawlVector::CrawlVector(store_flags flags, vec_size _max_size): type(SV_NONE), default_flags(flags), max_size(_max_size){ASSERT(!(default_flags & SFLAG_UNSET));ASSERT(max_size > 0);}CrawlVector::CrawlVector(store_val_type _type, store_flags flags,vec_size _max_size): type(_type), default_flags(flags), max_size(_max_size){ASSERT(type >= SV_NONE && type < NUM_STORE_VAL_TYPES);ASSERT(!(default_flags & SFLAG_UNSET));ASSERT(max_size > 0);}CrawlVector::~CrawlVector(){assert_validity();}//////////////////////////////// Read/write from/to savefilevoid CrawlVector::write(tagHeader &th) const{assert_validity();if (empty()){marshallByte(th, 0);return;}marshallByte(th, (char) size());marshallByte(th, (char) max_size);marshallByte(th, static_cast<char>(type));marshallByte(th, (char) default_flags);for (vec_size i = 0; i < size(); i++){CrawlStoreValue val = vector[i];ASSERT(val.type != SV_NONE);ASSERT(!(val.flags & SFLAG_UNSET));val.write(th);}assert_validity();}void CrawlVector::read(tagHeader &th){assert_validity();ASSERT(empty());ASSERT(type == SV_NONE);ASSERT(default_flags == 0);ASSERT(max_size == VEC_MAX_SIZE);vec_size _size = (vec_size) unmarshallByte(th);if (_size == 0)return;max_size = static_cast<vec_size>(unmarshallByte(th));type = static_cast<store_val_type>(unmarshallByte(th));default_flags = static_cast<store_flags>(unmarshallByte(th));ASSERT(_size <= max_size);vector.resize(_size);for (vec_size i = 0; i < _size; i++)vector[i].read(th);assert_validity();}//////////////////// Misc functionsstore_flags CrawlVector::get_default_flags() const{assert_validity();return default_flags;}store_flags CrawlVector::set_default_flags(store_flags flags){assert_validity();ASSERT(!(flags & SFLAG_UNSET));default_flags |= flags;return default_flags;}store_flags CrawlVector::unset_default_flags(store_flags flags){assert_validity();ASSERT(!(flags & SFLAG_UNSET));default_flags &= ~flags;return default_flags;}store_val_type CrawlVector::get_type() const{assert_validity();return type;}void CrawlVector::assert_validity() const{#if DEBUGASSERT(!(default_flags & SFLAG_UNSET));ASSERT(max_size > 0);ASSERT(max_size >= size());for (vec_size i = 0, _size = size(); i < _size; i++){const CrawlStoreValue &val = vector[i];// A vector might be resize()'d and filled up with unset// values, which are then set one by one, so we can't// assert over that here.if (val.type == SV_NONE || (val.flags & SFLAG_UNSET))continue;switch(val.type){case SV_STR:case SV_COORD:case SV_ITEM:ASSERT(val.val.ptr != NULL);break;case SV_HASH:{ASSERT(val.val.ptr != NULL);CrawlVector* nested;nested = static_cast<CrawlVector*>(val.val.ptr);nested->assert_validity();break;}case SV_VEC:{ASSERT(val.val.ptr != NULL);CrawlVector* nested;nested = static_cast<CrawlVector*>(val.val.ptr);nested->assert_validity();break;}default:break;}}#endif}void CrawlVector::set_max_size(vec_size _size){ASSERT(_size > 0);ASSERT(max_size == VEC_MAX_SIZE);max_size = _size;vector.reserve(max_size);}vec_size CrawlVector::get_max_size() const{return max_size;}////////////////////////////////// Accessors to contained valuesCrawlStoreValue& CrawlVector::get_value(const vec_size &index){assert_validity();ASSERT(index <= max_size);ASSERT(index <= vector.size());return vector[index];}const CrawlStoreValue& CrawlVector::get_value(const vec_size &index) const{assert_validity();ASSERT(index <= max_size);ASSERT(index <= vector.size());return vector[index];}CrawlStoreValue& CrawlVector::operator[] (const vec_size &index){return get_value(index);}const CrawlStoreValue& CrawlVector::operator[] (const vec_size &index) const{return get_value(index);}///////////////////////////// std::vector style interfacevec_size CrawlVector::size() const{return vector.size();}bool CrawlVector::empty() const{return vector.empty();}CrawlStoreValue& CrawlVector::pop_back(){assert_validity();ASSERT(vector.size() > 0);CrawlStoreValue& val = vector[vector.size() - 1];vector.pop_back();return val;}void CrawlVector::push_back(CrawlStoreValue val){assert_validity();ASSERT(vector.size() < max_size);ASSERT(type == SV_NONE|| (val.type == SV_NONE && (val.flags & SFLAG_UNSET))|| (val.type == type));val.flags |= default_flags;if (type != SV_NONE){val.type = type;val.flags |= SFLAG_CONST_TYPE;}vector.push_back(val);}void CrawlVector::insert(const vec_size index, CrawlStoreValue val){assert_validity();ASSERT(vector.size() < max_size);ASSERT(type == SV_NONE|| (val.type == SV_NONE && (val.flags & SFLAG_UNSET))|| (val.type == type));val.flags |= default_flags;if (type != SV_NONE){val.type = type;val.flags |= SFLAG_CONST_TYPE;}vector.insert(vector.begin() + index, val);}void CrawlVector::resize(const vec_size _size){assert_validity();ASSERT(max_size == VEC_MAX_SIZE);ASSERT(_size < max_size);vec_size old_size = size();vector.resize(_size);for (vec_size i = old_size; i < _size; i++){vector[i].flags = SFLAG_UNSET | default_flags;vector[i].type = SV_NONE;}}void CrawlVector::erase(const vec_size index){assert_validity();ASSERT(index <= max_size);ASSERT(index <= vector.size());vector.erase(vector.begin() + index);}void CrawlVector::clear(){assert_validity();ASSERT(!(default_flags & SFLAG_NO_ERASE));for (vec_size i = 0, _size = size(); i < _size; i++)ASSERT(!(vector[i].flags & SFLAG_NO_ERASE));vector.clear();}CrawlVector::iterator CrawlVector::begin(){assert_validity();return vector.begin();}CrawlVector::iterator CrawlVector::end(){assert_validity();return vector.end();}CrawlVector::const_iterator CrawlVector::begin() const{assert_validity();return vector.begin();}CrawlVector::const_iterator CrawlVector::end() const{assert_validity();return vector.end();}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////template <typename T, store_val_type TYPE>CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(){table = NULL;}template <typename T, store_val_type TYPE>CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(CrawlHashTable& _table){wrap(_table);}template <typename T, store_val_type TYPE>CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(CrawlHashTable* _table){wrap(_table);}template <typename T, store_val_type TYPE>void CrawlTableWrapper<T, TYPE>::wrap(CrawlHashTable& _table){wrap(&_table);}template <typename T, store_val_type TYPE>void CrawlTableWrapper<T, TYPE>::wrap(CrawlHashTable* _table){ASSERT(_table != NULL);ASSERT(_table->get_type() == TYPE);table = _table;}template <typename T, store_val_type TYPE>T& CrawlTableWrapper<T, TYPE>::operator[] (const std::string &key){return (T&) (*table)[key];}template <typename T, store_val_type TYPE>const T CrawlTableWrapper<T, TYPE>::operator[] (const std::string &key) const{return (T) (*table)[key];}
msg += "Strange, this deck is already empty.";int cards_left = 0;if (deck.plus2 >= 0)cards_left = deck.plus - deck.plus2;elsecards_left = -deck.plus;
if (props["cards"].get_vector().get_type() != SV_BYTE)msg::stream << "'cards' vector doesn't contain bytes. ";
msg += " But there should be been ";msg += cards_left;msg += " cards left.";
msg::stream << "Strange, this deck is already empty.";int cards_left = 0;if (deck.plus2 >= 0)cards_left = deck.plus - deck.plus2;elsecards_left = -deck.plus;if (cards_left != 0){msg::stream << " But there should be been ";msg::stream << cards_left;msg::stream << " cards left.";}