/* Copyright (C) 2013 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_ENTITYMAP #define INCLUDED_ENTITYMAP #include "Entity.h" /** * A fast replacement for map. * We make the following assumptions: * - entity id's (keys) are unique * - modifications (add / delete) are far less frequent then look-ups * - preformance for iteration is important */ template class EntityMap { private: EntityMap(const EntityMap&); // non-copyable EntityMap& operator=(const EntityMap&); // non-copyable public: typedef entity_id_t key_type; typedef T mapped_type; template struct key_val { typedef K first_type; typedef V second_type; K first; V second; }; typedef key_val value_type; private: size_t m_BufferSize; // number of elements in the buffer size_t m_BufferCapacity; // capacity of the buffer value_type* m_Buffer; // vector with all the mapped key-value pairs size_t m_Count; // number of 'valid' entity id's public: inline EntityMap() : m_BufferSize(1), m_BufferCapacity(4096), m_Count(0) { // for entitymap we allocate the buffer right away // with first element in buffer being the Invalid Entity m_Buffer = (value_type*)malloc(sizeof(value_type) * (m_BufferCapacity + 1)); // create the first element: m_Buffer[0].first = INVALID_ENTITY; m_Buffer[1].first = 0xFFFFFFFF; // ensure end() always has 0xFFFFFFFF } inline ~EntityMap() { free(m_Buffer); } // Iterators template struct _iter : public std::iterator { U* val; inline _iter(U* init) : val(init) {} inline U& operator*() { return *val; } inline U* operator->() { return val; } inline _iter& operator++() // ++it { ++val; while (val->first == INVALID_ENTITY) ++val; // skip any invalid entities return *this; } inline _iter& operator++(int) // it++ { U* ptr = val; ++val; while (val->first == INVALID_ENTITY) ++val; // skip any invalid entities return ptr; } inline bool operator==(_iter other) { return val == other.val; } inline bool operator!=(_iter other) { return val != other.val; } inline operator _iter() const { return _iter(val); } }; typedef _iter iterator; typedef _iter const_iterator; inline iterator begin() { value_type* ptr = m_Buffer + 1; // skip the first INVALID_ENTITY while (ptr->first == INVALID_ENTITY) ++ptr; // skip any other invalid entities return ptr; } inline iterator end() { return iterator(m_Buffer + m_BufferSize); } inline const_iterator begin() const { value_type* ptr = m_Buffer + 1; // skip the first INVALID_ENTITY while (ptr->first == INVALID_ENTITY) ++ptr; // skip any other invalid entities return ptr; } inline const_iterator end() const { return const_iterator(m_Buffer + m_BufferSize); } // Size inline bool empty() const { return m_Count == 0; } inline size_t size() const { return m_Count; } // Modification void insert(const key_type key, const mapped_type& value) { if (key >= m_BufferCapacity) // do we need to resize buffer? { size_t newCapacity = m_BufferCapacity + 4096; while (key >= newCapacity) newCapacity += 4096; // always allocate +1 behind the scenes, because end() must have a 0xFFFFFFFF key value_type* mem = (value_type*)realloc(m_Buffer, sizeof(value_type) * (newCapacity + 1)); if (!mem) { debug_warn("EntityMap::insert() realloc failed! Out of memory."); throw std::bad_alloc(); // fail to expand and insert } m_BufferCapacity = newCapacity; m_Buffer = mem; goto fill_gaps; } else if (key > m_BufferSize) // weird insert far beyond the end { fill_gaps: // set all entity id's to INVALID_ENTITY inside the new range for (size_t i = m_BufferSize; i <= key; ++i) m_Buffer[i].first = INVALID_ENTITY; m_BufferSize = key; // extend the new size } value_type& item = m_Buffer[key]; key_type oldKey = item.first; item.first = key; if (key == m_BufferSize) // push_back { ++m_BufferSize; // expand ++m_Count; new (&item.second) mapped_type(value); // copy ctor to init m_Buffer[m_BufferSize].first = 0xFFFFFFFF; // ensure end() always has 0xFFFFFFFF } else if(!item.first) // insert new to middle { ++m_Count; new (&item.second) mapped_type(value); // copy ctor to init } else // set existing value { if (oldKey == INVALID_ENTITY) m_Count++; item.second = value; // overwrite existing } } void erase(iterator it) { value_type* ptr = it.val; if (ptr->first != INVALID_ENTITY) { ptr->first = INVALID_ENTITY; ptr->second.~T(); // call dtor --m_Count; } } void erase(const entity_id_t key) { if (key < m_BufferSize) { value_type* ptr = m_Buffer + key; if (ptr->first != INVALID_ENTITY) { ptr->first = INVALID_ENTITY; ptr->second.~T(); // call dtor --m_Count; } } } inline void clear() { // orphan whole range value_type* ptr = m_Buffer; value_type* end = m_Buffer + m_BufferSize; for (; ptr != end; ++ptr) { if (ptr->first != INVALID_ENTITY) { ptr->first = INVALID_ENTITY; ptr->second.~T(); // call dtor } } m_Count = 0; // no more valid entities } // Operations inline iterator find(const entity_id_t key) { if (key < m_BufferSize) // is this key in the range of existing entitites? { value_type* ptr = m_Buffer + key; if (ptr->first != INVALID_ENTITY) return ptr; } return m_Buffer + m_BufferSize; // return iterator end() } inline const_iterator find(const entity_id_t key) const { if (key < m_BufferSize) // is this key in the range of existing entitites? { const value_type* ptr = m_Buffer + key; if (ptr->first != INVALID_ENTITY) return ptr; } return m_Buffer + m_BufferSize; // return iterator end() } inline size_t count(const entity_id_t key) const { if (key < m_BufferSize) { if (m_Buffer[key].first != INVALID_ENTITY) return 1; } return 0; } }; template struct SerializeEntityMap { template void operator()(ISerializer& serialize, const char* UNUSED(name), EntityMap& value) { size_t len = value.size(); serialize.NumberU32_Unbounded("length", (u32)len); size_t count = 0; for (typename EntityMap::iterator it = value.begin(); it != value.end(); ++it) { serialize.NumberU32_Unbounded("key", it->first); VSerializer()(serialize, "value", it->second); count++; } // test to see if the entityMap count wasn't wrong // (which causes a crashing deserialisation) ENSURE(count == len); } template void operator()(IDeserializer& deserialize, const char* UNUSED(name), EntityMap& value) { value.clear(); uint32_t len; deserialize.NumberU32_Unbounded("length", len); for (size_t i = 0; i < len; ++i) { entity_id_t k; V v; deserialize.NumberU32_Unbounded("key", k); VSerializer()(deserialize, "value", v); value.insert(k, v); } } }; #endif