/** * @file * @brief Saveable hash-table and vector capable of storing * multiple types of data. **/ #include "AppHdr.h" #include "store.h" #include "dlua.h" #include "externs.h" #include "monster.h" #include "strings.h" #include "tags.h" #include "travel.h" #include // These tend to be called from tight loops, and C++ method calls don't // get optimized away except for LTO -fwhole-program builds, so merely // disabling the function's body is not enough; let's not call them at all. #ifdef DEBUG # define ASSERT_VALIDITY(x) x assert_validity() #else # define ASSERT_VALIDITY(x) #endif CrawlStoreValue::CrawlStoreValue() : type(SV_NONE), flags(SFLAG_UNSET) { val.ptr = NULL; } CrawlStoreValue::CrawlStoreValue(const CrawlStoreValue &other) { ASSERT_RANGE(other.type, SV_NONE, NUM_STORE_VAL_TYPES); val.ptr = NULL; 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_INT: case SV_INT64: case SV_FLOAT: val = other.val; break; case SV_STR: { string* str; str = new string(*static_cast(other.val.ptr)); val.ptr = static_cast(str); break; } case SV_COORD: { coord_def* coord; coord = new coord_def(*static_cast(other.val.ptr)); val.ptr = static_cast(coord); break; } case SV_ITEM: { item_def* item; item = new item_def(*static_cast(other.val.ptr)); val.ptr = static_cast(item); break; } case SV_HASH: { CrawlHashTable* hash; CrawlHashTable* tmp = static_cast(other.val.ptr); hash = new CrawlHashTable(*tmp); val.ptr = static_cast(hash); break; } case SV_VEC: { CrawlVector* vec; CrawlVector* tmp = static_cast(other.val.ptr); vec = new CrawlVector(*tmp); val.ptr = static_cast(vec); break; } case SV_LEV_ID: { level_id* id; id = new level_id(*static_cast(other.val.ptr)); val.ptr = static_cast(id); break; } case SV_LEV_POS: { level_pos* pos; pos = new level_pos(*static_cast(other.val.ptr)); val.ptr = static_cast(pos); break; } case SV_MONST: { monster* mon; mon = new monster(*static_cast(other.val.ptr)); val.ptr = static_cast(mon); break; } case SV_LUA: { dlua_chunk* chunk; chunk = new dlua_chunk(*static_cast(other.val.ptr)); val.ptr = static_cast(chunk); break; } case NUM_STORE_VAL_TYPES: die("unknown stored value type"); } } CrawlStoreValue::CrawlStoreValue(const store_flags _flags, const store_val_type _type) : type(_type), flags(_flags) { ASSERT_RANGE(type, SV_NONE, NUM_STORE_VAL_TYPES); ASSERT(!(flags & SFLAG_UNSET)); flags |= SFLAG_UNSET; val.ptr = NULL; } // Conversion constructors CrawlStoreValue::CrawlStoreValue(const bool _val) : type(SV_BOOL), flags(SFLAG_UNSET) { get_bool() = _val; } CrawlStoreValue::CrawlStoreValue(const char &_val) : type(SV_BYTE), flags(SFLAG_UNSET) { get_byte() = _val; } CrawlStoreValue::CrawlStoreValue(const short &_val) : type(SV_SHORT), flags(SFLAG_UNSET) { get_short() = _val; } CrawlStoreValue::CrawlStoreValue(const int &_val) : type(SV_INT), flags(SFLAG_UNSET) { get_int() = _val; } CrawlStoreValue::CrawlStoreValue(const int64_t &_val) : type(SV_INT), flags(SFLAG_UNSET) { get_int64() = _val; } CrawlStoreValue::CrawlStoreValue(const float &_val) : type(SV_FLOAT), flags(SFLAG_UNSET) { get_float() = _val; } CrawlStoreValue::CrawlStoreValue(const string &_val) : type(SV_STR), flags(SFLAG_UNSET) { val.ptr = NULL; get_string() = _val; } CrawlStoreValue::CrawlStoreValue(const char* _val) : type(SV_STR), flags(SFLAG_UNSET) { val.ptr = NULL; get_string() = _val; } CrawlStoreValue::CrawlStoreValue(const coord_def &_val) : type(SV_COORD), flags(SFLAG_UNSET) { val.ptr = NULL; get_coord() = _val; } CrawlStoreValue::CrawlStoreValue(const item_def &_val) : type(SV_ITEM), flags(SFLAG_UNSET) { val.ptr = NULL; get_item() = _val; } CrawlStoreValue::CrawlStoreValue(const CrawlHashTable &_val) : type(SV_HASH), flags(SFLAG_UNSET) { val.ptr = NULL; get_table() = _val; } CrawlStoreValue::CrawlStoreValue(const CrawlVector &_val) : type(SV_VEC), flags(SFLAG_UNSET) { val.ptr = NULL; get_vector() = _val; } CrawlStoreValue::CrawlStoreValue(const level_id &_val) : type(SV_LEV_ID), flags(SFLAG_UNSET) { val.ptr = NULL; get_level_id() = _val; } CrawlStoreValue::CrawlStoreValue(const level_pos &_val) : type(SV_LEV_POS), flags(SFLAG_UNSET) { val.ptr = NULL; get_level_pos() = _val; } CrawlStoreValue::CrawlStoreValue(const monster& _val) : type(SV_MONST), flags(SFLAG_UNSET) { val.ptr = NULL; get_monster() = _val; } CrawlStoreValue::CrawlStoreValue(const dlua_chunk &_val) : type(SV_LUA), flags(SFLAG_UNSET) { val.ptr = NULL; get_lua() = _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_INT: val._int = 0; break; case SV_INT64: val._int64 = 0; break; case SV_FLOAT: val._float = 0.0; break; case SV_STR: { string* str = static_cast(val.ptr); delete str; val.ptr = NULL; break; } case SV_COORD: { coord_def* coord = static_cast(val.ptr); delete coord; val.ptr = NULL; break; } case SV_ITEM: { item_def* item = static_cast(val.ptr); delete item; val.ptr = NULL; break; } case SV_HASH: { CrawlHashTable* hash = static_cast(val.ptr); delete hash; val.ptr = NULL; break; } case SV_VEC: { CrawlVector* vec = static_cast(val.ptr); delete vec; val.ptr = NULL; break; } case SV_LEV_ID: { level_id* id = static_cast(val.ptr); delete id; val.ptr = NULL; break; } case SV_LEV_POS: { level_pos* pos = static_cast(val.ptr); delete pos; val.ptr = NULL; break; } case SV_MONST: { monster* mon = static_cast(val.ptr); delete mon; val.ptr = NULL; break; } case SV_LUA: { dlua_chunk* chunk = static_cast(val.ptr); delete chunk; val.ptr = NULL; break; } case SV_NONE: die("CrawlStoreValue::unset: unsetting nothing"); break; default: die("CrawlStoreValue::unset: unsetting invalid type"); break; } flags |= SFLAG_UNSET; } #define COPY_PTR(ptr_type) \ { \ ptr_type *ptr = static_cast(val.ptr); \ if (ptr != NULL) \ delete ptr; \ ptr = static_cast(other.val.ptr); \ val.ptr = (void*) new ptr_type (*ptr); \ } CrawlStoreValue &CrawlStoreValue::operator = (const CrawlStoreValue &other) { ASSERT_RANGE(other.type, SV_NONE, 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; switch (type) { case SV_NONE: case SV_BOOL: case SV_BYTE: case SV_SHORT: case SV_INT: case SV_INT64: case SV_FLOAT: val = other.val; break; case SV_STR: COPY_PTR(string); break; case SV_COORD: COPY_PTR(coord_def); break; case SV_ITEM: COPY_PTR(item_def); break; case SV_HASH: COPY_PTR(CrawlHashTable); break; case SV_VEC: COPY_PTR(CrawlVector); break; case SV_LEV_ID: COPY_PTR(level_id); break; case SV_LEV_POS: COPY_PTR(level_pos); break; default: die("CrawlStoreValue has invalid type"); break; } return *this; } /////////////////////////////////// // Meta-data accessors and changers store_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 savefile void CrawlStoreValue::write(writer &th) const { ASSERT(type != SV_NONE || (flags & SFLAG_UNSET)); ASSERT(!(flags & SFLAG_UNSET) || type == SV_NONE); 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_INT: marshallInt(th, val._int); break; case SV_INT64: marshallSigned(th, val._int64); break; case SV_FLOAT: marshallFloat(th, val._float); break; case SV_STR: { string* str = static_cast(val.ptr); marshallString(th, *str); break; } case SV_COORD: { coord_def* coord = static_cast(val.ptr); marshallCoord(th, *coord); break; } case SV_ITEM: { item_def* item = static_cast(val.ptr); marshallItem(th, *item); break; } case SV_HASH: { CrawlHashTable* hash = static_cast(val.ptr); hash->write(th); break; } case SV_VEC: { CrawlVector* vec = static_cast(val.ptr); vec->write(th); break; } case SV_LEV_ID: { level_id* id = static_cast(val.ptr); id->save(th); break; } case SV_LEV_POS: { level_pos* pos = static_cast(val.ptr); pos->save(th); break; } case SV_MONST: { monster* mon = static_cast(val.ptr); marshallMonster(th, *mon); break; } case SV_LUA: { dlua_chunk* chunk = static_cast(val.ptr); chunk->write(th); break; } case SV_NONE: break; case NUM_STORE_VAL_TYPES: die("unknown stored value type"); } } void CrawlStoreValue::read(reader &th) { type = static_cast(unmarshallByte(th)); flags = (store_flags) unmarshallByte(th); ASSERT(type != SV_NONE || (flags & SFLAG_UNSET)); ASSERT(!(flags & SFLAG_UNSET) || type == SV_NONE); 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_INT: val._int = unmarshallInt(th); break; case SV_INT64: val._int64 = unmarshallSigned(th); break; case SV_FLOAT: val._float = unmarshallFloat(th); break; case SV_STR: { string str = unmarshallString(th); val.ptr = (void*) new string(str); break; } case SV_COORD: { const coord_def coord = unmarshallCoord(th); 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_LEV_ID: { level_id* id = new level_id(); id->load(th); val.ptr = (void*) id; break; } case SV_LEV_POS: { level_pos* pos = new level_pos(); pos->load(th); val.ptr = (void*) pos; break; } case SV_MONST: { monster mon; unmarshallMonster(th, mon); val.ptr = (void*) new monster(mon); break; } case SV_LUA: { dlua_chunk chunk; chunk.read(th); val.ptr = (void*) new dlua_chunk(chunk); break; } case SV_NONE: break; case NUM_STORE_VAL_TYPES: die("unknown stored value type"); } } CrawlHashTable &CrawlStoreValue::new_table() { return get_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) { #ifdef DEBUG CrawlVector* old_vec = static_cast(val.ptr); ASSERT(flags & SFLAG_UNSET); ASSERT(type == SV_NONE || (type == SV_VEC && old_vec->empty() && old_vec->get_type() == SV_NONE && old_vec->get_default_flags() == 0 && old_vec->get_max_size() == VEC_MAX_SIZE)); #endif CrawlVector &vec = get_vector(); vec.default_flags = _flags; vec.type = _type; type = SV_VEC; flags &= ~SFLAG_UNSET; return vec; } /////////////////////////////////////////// // 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_INT: \ field = (_type) val._int; \ break; \ case SV_INT64: \ field = (_type) val._int64; \ break; \ case SV_FLOAT: \ field = (_type) val._float; \ break; \ default: \ die("unknown stored value type"); \ } \ 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) || (flags & SFLAG_UNSET)) \ { \ if (type == SV_NONE) \ { \ type = (x); \ val.ptr = (value); \ } \ else \ { \ unset(); \ val.ptr = (value); \ type = (x); \ } \ } \ else \ delete (value); \ 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); } int &CrawlStoreValue::get_int() { GET_VAL(SV_INT, int, val._int, 0); } int64_t &CrawlStoreValue::get_int64() { GET_VAL(SV_INT64, int64_t, val._int64, 0); } float &CrawlStoreValue::get_float() { GET_VAL(SV_FLOAT, float, val._float, 0.0); } string &CrawlStoreValue::get_string() { GET_VAL_PTR(SV_STR, string*, new 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()); } level_id &CrawlStoreValue::get_level_id() { GET_VAL_PTR(SV_LEV_ID, level_id*, new level_id()); } level_pos &CrawlStoreValue::get_level_pos() { GET_VAL_PTR(SV_LEV_POS, level_pos*, new level_pos()); } monster &CrawlStoreValue::get_monster() { GET_VAL_PTR(SV_MONST, monster* , new monster()); } dlua_chunk &CrawlStoreValue::get_lua() { GET_VAL_PTR(SV_LUA, dlua_chunk*, new dlua_chunk()); } /////////////////////////// // 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; } int CrawlStoreValue::get_int() const { GET_CONST_SETUP(SV_INT); return val._int; } int64_t CrawlStoreValue::get_int64() const { GET_CONST_SETUP(SV_INT64); return val._int64; } float CrawlStoreValue::get_float() const { GET_CONST_SETUP(SV_FLOAT); return val._float; } string CrawlStoreValue::get_string() const { GET_CONST_SETUP(SV_STR); return *((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 monster& CrawlStoreValue::get_monster() const { GET_CONST_SETUP(SV_MONST); return *((monster*)val.ptr); } level_id CrawlStoreValue::get_level_id() const { GET_CONST_SETUP(SV_LEV_ID); return *((level_id*)val.ptr); } level_pos CrawlStoreValue::get_level_pos() const { GET_CONST_SETUP(SV_LEV_POS); return *((level_pos*)val.ptr); } ///////////////////// // 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 int&() { return get_int(); } CrawlStoreValue::operator int64_t&() { return get_int64(); } CrawlStoreValue::operator 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(); } CrawlStoreValue::operator level_id&() { return get_level_id(); } CrawlStoreValue::operator level_pos&() { return get_level_pos(); } CrawlStoreValue::operator monster& () { return get_monster(); } CrawlStoreValue::operator dlua_chunk&() { return get_lua(); } /////////////////////////// // Const typecast operators CrawlStoreValue::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_INT: \ return get_int(); \ default: \ die("unknown stored value type"); \ } CrawlStoreValue::operator char() const { CONST_INT_CAST(); } CrawlStoreValue::operator short() const { CONST_INT_CAST(); } CrawlStoreValue::operator int() const { CONST_INT_CAST(); } CrawlStoreValue::operator int64_t() const { // Allow upgrading but not downgrading. switch (type) { case SV_BYTE: return get_byte(); case SV_SHORT: return get_short(); case SV_INT: return get_int(); case SV_INT64: return get_int64(); default: die("unknown stored value type"); } } CrawlStoreValue::operator float() const { return get_float(); } CrawlStoreValue::operator string() const { return get_string(); } CrawlStoreValue::operator coord_def() const { return get_coord(); } CrawlStoreValue::operator level_id() const { return get_level_id(); } CrawlStoreValue::operator level_pos() const { return get_level_pos(); } /////////////////////// // Assignment operators CrawlStoreValue &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 int &_val) { get_int() = _val; return *this; } CrawlStoreValue &CrawlStoreValue::operator = (const int64_t &_val) { get_int64() = _val; return *this; } CrawlStoreValue &CrawlStoreValue::operator = (const float &_val) { get_float() = _val; return *this; } CrawlStoreValue &CrawlStoreValue::operator = (const 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; } CrawlStoreValue &CrawlStoreValue::operator = (const level_id &_val) { get_level_id() = _val; return *this; } CrawlStoreValue &CrawlStoreValue::operator = (const level_pos &_val) { get_level_pos() = _val; return *this; } CrawlStoreValue &CrawlStoreValue::operator = (const monster& _val) { get_monster() = _val; return *this; } CrawlStoreValue &CrawlStoreValue::operator = (const dlua_chunk &_val) { get_lua() = _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_INT: \ { \ int &temp = get_int(); \ temp op; \ return temp; \ } \ case SV_INT64: \ { \ int64_t &temp = get_int64(); \ temp op; \ return temp; \ } \ \ default: \ die("unknown stored value type"); \ return 0; \ } // Prefix int CrawlStoreValue::operator ++ () { INT_OPERATOR_UNARY(++); } int CrawlStoreValue::operator -- () { INT_OPERATOR_UNARY(--); } // Postfix int CrawlStoreValue::operator ++ (int) { INT_OPERATOR_UNARY(++); } int CrawlStoreValue::operator -- (int) { INT_OPERATOR_UNARY(--); } string &CrawlStoreValue::operator += (const string &_val) { return get_string() += _val; } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// CrawlHashTable::CrawlHashTable() { hash_map = NULL; } CrawlHashTable::CrawlHashTable(const CrawlHashTable& other) { if (other.hash_map == NULL) { hash_map = NULL; return; } hash_map = new hash_map_type(*(other.hash_map)); } CrawlHashTable::~CrawlHashTable() { // NOTE: Not using unique_ptr because making hash_map an unique_ptr // causes compile weirdness in externs.h if (hash_map == NULL) return; delete hash_map; hash_map = NULL; } CrawlHashTable &CrawlHashTable::operator = (const CrawlHashTable &other) { if (hash_map != NULL) delete hash_map; if (other.hash_map == NULL) { hash_map = NULL; return *this; } hash_map = new hash_map_type(*(other.hash_map)); return *this; } ////////////////////////////// // Read/write from/to savefile void CrawlHashTable::write(writer &th) const { ASSERT_VALIDITY(); if (empty()) { marshallUnsigned(th, 0); return; } marshallUnsigned(th, size()); 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(reader &th) { ASSERT_VALIDITY(); ASSERT(empty()); #if TAG_MAJOR_VERSION == 34 unsigned int _size; if (th.getMinorVersion() < TAG_MINOR_16_BIT_TABLE) _size = unmarshallByte(th); else _size = unmarshallUnsigned(th); #else unsigned int _size = unmarshallUnsigned(th); #endif if (_size == 0) return; init_hash_map(); for (unsigned int i = 0; i < _size; i++) { string key = unmarshallString(th); CrawlStoreValue &val = (*this)[key]; val.read(th); } ASSERT_VALIDITY(); } #ifdef DEBUG_PROPS static map accesses; # define ACCESS(x) ++accesses[x] #else # define ACCESS(x) #endif ////////////////// // Misc functions bool CrawlHashTable::exists(const string &key) const { if (hash_map == NULL) return false; ACCESS(key); ASSERT_VALIDITY(); hash_map_type::const_iterator i = hash_map->find(key); return i != hash_map->end(); } void CrawlHashTable::assert_validity() const { #ifdef DEBUG if (hash_map == NULL) return; hash_map_type::const_iterator i = hash_map->begin(); size_t actual_size = 0; for (; i != hash_map->end(); ++i) { actual_size++; const string &key = i->first; const CrawlStoreValue &val = i->second; ASSERT(!key.empty()); 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: case SV_LEV_ID: case SV_LEV_POS: ASSERT(val.val.ptr != NULL); break; case SV_HASH: { ASSERT(val.val.ptr != NULL); CrawlHashTable* nested; nested = static_cast(val.val.ptr); nested->assert_validity(); break; } case SV_VEC: { ASSERT(val.val.ptr != NULL); CrawlVector* nested; nested = static_cast(val.val.ptr); nested->assert_validity(); break; } default: break; } } ASSERT(size() == actual_size); #endif } //////////////////////////////// // Accessors to contained values CrawlStoreValue& CrawlHashTable::get_value(const string &key) { ASSERT_VALIDITY(); init_hash_map(); ACCESS(key); iterator i = hash_map->find(key); if (i == hash_map->end()) { (*hash_map)[key] = CrawlStoreValue(); CrawlStoreValue &val = (*hash_map)[key]; return val; } return i->second; } const CrawlStoreValue& CrawlHashTable::get_value(const string &key) const { #ifdef ASSERTS if (!hash_map) die("trying to read non-existent property \"%s\"", key.c_str()); #endif ASSERT_VALIDITY(); ACCESS(key); hash_map_type::const_iterator i = hash_map->find(key); #ifdef ASSERTS if (i == hash_map->end()) die("trying to read non-existent property \"%s\"", key.c_str()); #endif ASSERT(i->second.type != SV_NONE); ASSERT(!(i->second.flags & SFLAG_UNSET)); return i->second; } /////////////////////////// // std::map style interface unsigned int CrawlHashTable::size() const { if (hash_map == NULL) return 0; return hash_map->size(); } bool CrawlHashTable::empty() const { if (hash_map == NULL) return true; return hash_map->empty(); } void CrawlHashTable::erase(const string key) { ASSERT_VALIDITY(); init_hash_map(); ACCESS(key); iterator i = hash_map->find(key); if (i != hash_map->end()) { #ifdef ASSERTS CrawlStoreValue &val = i->second; ASSERT(!(val.flags & SFLAG_NO_ERASE)); #endif hash_map->erase(i); } } void CrawlHashTable::clear() { ASSERT_VALIDITY(); if (hash_map == NULL) return; delete hash_map; hash_map = NULL; } CrawlHashTable::iterator CrawlHashTable::begin() { ASSERT_VALIDITY(); init_hash_map(); return hash_map->begin(); } CrawlHashTable::iterator CrawlHashTable::end() { ASSERT_VALIDITY(); init_hash_map(); return hash_map->end(); } CrawlHashTable::const_iterator CrawlHashTable::begin() const { ASSERT(hash_map != NULL); ASSERT_VALIDITY(); return hash_map->begin(); } CrawlHashTable::const_iterator CrawlHashTable::end() const { ASSERT(hash_map != NULL); ASSERT_VALIDITY(); return hash_map->end(); } void CrawlHashTable::init_hash_map() { if (hash_map != NULL) return; hash_map = new hash_map_type(); } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// 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_RANGE(type, SV_NONE, NUM_STORE_VAL_TYPES); ASSERT(!(default_flags & SFLAG_UNSET)); ASSERT(max_size > 0); } CrawlVector::~CrawlVector() { ASSERT_VALIDITY(); } ////////////////////////////// // Read/write from/to savefile void CrawlVector::write(writer &th) const { ASSERT_VALIDITY(); if (empty()) { marshallByte(th, 0); return; } marshallUnsigned(th, (char) size()); marshallUnsigned(th, (char) max_size); marshallByte(th, static_cast(type)); marshallByte(th, (char) default_flags); for (vec_size i = 0; i < size(); i++) { CrawlStoreValue val = vec[i]; val.write(th); } ASSERT_VALIDITY(); } void CrawlVector::read(reader &th) { ASSERT_VALIDITY(); ASSERT(empty()); ASSERT(type == SV_NONE); ASSERT(default_flags == 0); ASSERT(max_size == VEC_MAX_SIZE); #if TAG_MAJOR_VERSION == 34 vec_size _size; if (th.getMinorVersion() < TAG_MINOR_16_BIT_TABLE) _size = (vec_size) unmarshallByte(th); else _size = (vec_size) unmarshallUnsigned(th); #else vec_size _size = (vec_size) unmarshallUnsigned(th); #endif if (_size == 0) return; #if TAG_MAJOR_VERSION == 34 if (th.getMinorVersion() < TAG_MINOR_16_BIT_TABLE) max_size = static_cast(unmarshallByte(th)); else #endif max_size = static_cast(unmarshallUnsigned(th)); type = static_cast(unmarshallByte(th)); default_flags = static_cast(unmarshallByte(th)); ASSERT(_size <= max_size); vec.resize(_size); for (vec_size i = 0; i < _size; i++) vec[i].read(th); ASSERT_VALIDITY(); } ////////////////// // Misc functions store_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 { #ifdef DEBUG ASSERT(!(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 = vec[i]; if (type != SV_NONE) ASSERT(val.type == SV_NONE || val.type == type); // 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: case SV_LEV_ID: case SV_LEV_POS: ASSERT(val.val.ptr != NULL); break; case SV_HASH: { ASSERT(val.val.ptr != NULL); CrawlHashTable* nested; nested = static_cast(val.val.ptr); nested->assert_validity(); break; } case SV_VEC: { ASSERT(val.val.ptr != NULL); CrawlVector* nested; nested = static_cast(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); max_size = _size; vec.reserve(max_size); } vec_size CrawlVector::get_max_size() const { return max_size; } //////////////////////////////// // Accessors to contained values CrawlStoreValue& CrawlVector::get_value(const vec_size &index) { ASSERT_VALIDITY(); ASSERT(index <= max_size); ASSERT(index <= vec.size()); return vec[index]; } const CrawlStoreValue& CrawlVector::get_value(const vec_size &index) const { ASSERT_VALIDITY(); ASSERT(index <= max_size); ASSERT(index <= vec.size()); return vec[index]; } /////////////////////////// // std::vector style interface vec_size CrawlVector::size() const { return vec.size(); } bool CrawlVector::empty() const { return vec.empty(); } CrawlStoreValue& CrawlVector::pop_back() { ASSERT_VALIDITY(); ASSERT(!vec.empty()); CrawlStoreValue& val = vec[vec.size() - 1]; vec.pop_back(); return val; } void CrawlVector::push_back(CrawlStoreValue val) { #ifdef DEBUG if (type != SV_NONE) ASSERT(type == val.type); switch (val.type) { case SV_STR: case SV_COORD: case SV_ITEM: case SV_LEV_ID: case SV_LEV_POS: ASSERT(val.val.ptr != NULL); break; case SV_HASH: { ASSERT(val.val.ptr != NULL); CrawlHashTable* nested; nested = static_cast(val.val.ptr); nested->assert_validity(); break; } case SV_VEC: { ASSERT(val.val.ptr != NULL); CrawlVector* nested; nested = static_cast(val.val.ptr); nested->assert_validity(); break; } default: break; } #endif ASSERT_VALIDITY(); ASSERT(vec.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; } vec.push_back(val); ASSERT_VALIDITY(); } void CrawlVector::insert(const vec_size index, CrawlStoreValue val) { ASSERT_VALIDITY(); ASSERT(vec.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; } vec.insert(vec.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(); vec.resize(_size); for (vec_size i = old_size; i < _size; i++) { vec[i].flags = SFLAG_UNSET | default_flags; vec[i].type = SV_NONE; } } void CrawlVector::erase(const vec_size index) { ASSERT_VALIDITY(); ASSERT(index <= max_size); ASSERT(index <= vec.size()); vec.erase(vec.begin() + index); } void CrawlVector::clear() { ASSERT_VALIDITY(); ASSERT(!(default_flags & SFLAG_NO_ERASE)); for (vec_size i = 0, _size = size(); i < _size; i++) ASSERT(!(vec[i].flags & SFLAG_NO_ERASE)); vec.clear(); default_flags = 0; type = SV_NONE; } CrawlVector::iterator CrawlVector::begin() { ASSERT_VALIDITY(); return vec.begin(); } CrawlVector::iterator CrawlVector::end() { ASSERT_VALIDITY(); return vec.end(); } CrawlVector::const_iterator CrawlVector::begin() const { ASSERT_VALIDITY(); return vec.begin(); } CrawlVector::const_iterator CrawlVector::end() const { ASSERT_VALIDITY(); return vec.end(); } #ifdef DEBUG_PROPS static bool _cmp(string a, string b) { return accesses[a] > accesses[b]; } void dump_prop_accesses() { FILE *f = fopen("prop_accesses", "w"); ASSERT(f); vector props; for (map::const_iterator i = accesses.begin(); i != accesses.end(); ++i) { props.push_back(i->first); } sort(props.begin(), props.end(), _cmp); for (vector::const_iterator i = props.begin(); i != props.end(); ++i) { fprintf(f, "%10d %s\n", accesses[*i], i->c_str()); } fclose(f); } #endif