1. 4FipS.com
  2. Photos
  3. Videos
  4. Code
  5. Forums
  6. pfQuizzz
  7. About
/*
(c) 2012 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=768
*/

#include <vector>
#include <algorithm>
#include <cstdint> // uint16_t, ...
#include <cassert>

/// A simple handle-like index, combination of a table index and magic number for consistency checks.
template <typename Bound_T>
struct Record_index_pod
{
    /// Constructs a new index (we don't have a CTOR for this as we want the index to act as a POD).
    static Record_index_pod make(size_t value, size_t magic)
    {
        assert(value <= Value_max_value && magic <= Magic_max_value); // check for overflow
        const Record_index_pod index = { static_cast<uint16_t>(value), static_cast<uint16_t>(magic) };
        assert(index.value() == value && index.magic() == magic); // double check ;)
        return index;
    }

    /// Returns an invalid index (a kind of null-pointer equivalent).
    static Record_index_pod invalid()
    {
        return make(0, Magic_invalid_value);
    }

    /// Checks for invalid index (it, however, doesn't make any assumptions about the record it might refer to).
    bool valid() const
    {
        return *this != invalid();
    }

    size_t value() const { return _value; }
    size_t magic() const { return _magic; }
    size_t key() const { return _key; }

    bool operator == (const Record_index_pod &rhs) const { return _key == rhs._key; }
    bool operator != (const Record_index_pod &rhs) const { return _key != rhs._key; }
    bool operator < (const Record_index_pod &rhs) const { return _key < rhs._key; }
    bool operator > (const Record_index_pod &rhs) const { return _key > rhs._key; }
    bool operator <= (const Record_index_pod &rhs) const { return _key <= rhs._key; }
    bool operator >= (const Record_index_pod &rhs) const { return _key >= rhs._key; }

    enum // define sizes and limits
    {
        Value_bit_count = 16,
        Magic_bit_count = 16,

        Value_max_value = (1 << Value_bit_count) - 1,
        Magic_max_value = (1 << Magic_bit_count) - 1,

        Magic_invalid_value = 0
    };

    union
    {
        struct // bit fields are much easier to debug than masks...
        {
            uint16_t _value : Value_bit_count;
            uint16_t _magic : Magic_bit_count;
        };

        uint32_t _key; // a composite (value & magic)
    };
};

/// A record pool, it stores records by value and provides safe access to them through the index.
template <typename T, typename Index_T = Record_index_pod<T>>
class Record_pool
{
 public:

    typedef Index_T Index;

    /// Creates a new pool, optionally pre-allocated to the given size.
    explicit Record_pool(size_t size = 0)
    {
        grow(size);
    } 

    /// Adds a new record to the pool, returns its index.
    Index add(const T &rec)
    {
        if(_free_list.empty())
            grow(_table.empty() ? 16 : _table.size()); // grow to 16 if empty

        // pop a free table index
        assert(!_free_list.empty());
        const size_t table_index = _free_list.back();
        _free_list.pop_back();

        // store the record & update the magic number
        assert(table_index < _table.size());
        Entry &entry = _table[table_index];
        entry = Entry(rec, entry.magic + 1, true);
        entry.magic += (entry.magic == Index::Magic_invalid_value) ? 1 : 0; // handle wrapping

        assert(entry.magic != Index::Magic_invalid_value);
        return Index::make(table_index, entry.magic);
    }

    /// Removes an existing record from the pool.
    void remove(Index index)
    {
        assert(has(index));

        // deactivate & return to the free list
        _table[index.value()].active = false;
        _free_list.emplace_back(index.value());
    }

    /// Gets a record at the given index, asserts if not found.
    const T & get(Index index) const
    {
        assert(has(index));
        return _table[index.value()].payload;
    }

    /// Gets a record at the given index, asserts if not found.
    T & get(Index index)
    {
        assert(has(index));
        return _table[index.value()].payload;
    }

    /// Checks whether there's a valid record at the given index. 
    bool has(Index index) const
    {
        const size_t table_index = index.value();
        const size_t magic = index.magic();

        const Entry &entry = _table[table_index];

        return
         magic != Index::Magic_invalid_value &&
         table_index < _table.size() &&
         entry.active &&
         entry.magic == magic
        ;
    }

 private:

    /// Grows the pool by the given size.
    void grow(size_t size)
    {
        if(size == 0)
            return;

        const size_t orig_size = _table.size();
        const size_t new_size = orig_size + size;

        // resize the table using T(), therefore T must be default-constructible
        _table.resize(new_size, Entry(T(), Index::Magic_invalid_value, false));
        _free_list.resize(new_size);

        // generate free indices
        size_t magic = new_size;
        std::generate(
         _free_list.begin() + orig_size,
         _free_list.end(),
         [&magic]()->size_t { return --magic; }
        );
    }

    struct Entry
    {
        T payload; // the actual record
        uint32_t magic : Index::Magic_bit_count; // its corresponding magic number
        bool active : 1; // used / unused pool entry

        Entry(const T &payload, size_t magic, bool active):
        payload(payload), magic(magic), active(active)
        {}
    };

    std::vector<Entry> _table;
    std::vector<size_t> _free_list;
};

/// A demo Entity type managed by Record_pool.
struct Entity
{
    std::string name;
    int x, y;

    Entity() : name(), x(), y() {} ///< Must be default-constructible.
    Entity(const std::string &name, int x, int y) : name(name), x(x), y(y) {}
};

int main()
{
    // introduce a new index type (it's typically done in an interface header)
    struct Entity_pool_index_tag {}; // it's bound to this tag to make the index type-safe
    typedef Record_index_pod<Entity_pool_index_tag> Entity_pool_index;

    // define the pool (it's typically done inside implementation)
    typedef Record_pool<Entity, Entity_pool_index> Entity_pool;
    Entity_pool pool;

    // add a new entity into the pool
    const Entity_pool_index foo_index = pool.add(Entity("foo", 10, 20));
    assert(foo_index.valid()); // 'foo_index' is valid
    assert(pool.has(foo_index)); // 'pool' has 'foo'

    // access the entity using the index
    const Entity &foo = pool.get(foo_index);
    printf("entity : name='%s', pos=[%d, %d]\n", foo.name.c_str(), int(foo.x), int(foo.y));

    // remove the entity from the pool
    pool.remove(foo_index);
    assert(foo_index.valid()); // 'foo_index' is valid
    assert(!pool.has(foo_index)); // 'pool' doesn't have 'foo'

    // add a new entity into the empty pool
    const Entity_pool_index bar_index = pool.add(Entity("bar", 10, 20));
    assert(bar_index.valid()); // 'bar_index' is valid
    assert(pool.has(bar_index)); // 'pool' has 'bar'
    assert(foo_index.valid()); // 'foo_index' is valid
    assert(!pool.has(foo_index)); // 'pool' doesn't have 'foo'
    // pool.get(foo_index); // this would assert/crash

    // note that a new index gets the same 'value', but a different 'magic'
    // (the exact behaviour is just an implementation detail though)
    assert(foo_index.value() == bar_index.value() && foo_index.magic() != bar_index.magic());

    return 0;
}

// output:
// entity : name='foo', pos=[10, 20]