Program Listing for File AgentVector.h

Return to documentation for file (include/flamegpu/simulation/AgentVector.h)

#ifndef INCLUDE_FLAMEGPU_SIMULATION_AGENTVECTOR_H_
#define INCLUDE_FLAMEGPU_SIMULATION_AGENTVECTOR_H_

#include <string>
#include <utility>
#include <memory>
#include <map>

#include "flamegpu/defines.h"
#include "flamegpu/simulation/detail/MemoryVector.h"
#include "flamegpu/model/AgentData.h"

namespace flamegpu {
namespace detail {
class CUDAAgentStateList;
}  // namespace detail
class AgentInstance;
class AgentDescription;
class AgentVector_CAgent;
class AgentVector_Agent;
struct AgentData;

class AgentVector {
    static const float RESIZE_FACTOR;
    friend class detail::CUDAAgentStateList;
    friend class AgentVector_CAgent;
    friend class AgentVector_Agent;

 public:
    typedef AgentVector_Agent Agent;
    typedef AgentVector_CAgent CAgent;
    typedef std::map<std::string, std::unique_ptr<detail::GenericMemoryVector>> AgentDataMap;

    // They might all be wrong
    class const_iterator;
    class const_reverse_iterator;
    class iterator {
        typedef std::input_iterator_tag iterator_category;
        typedef Agent value_type;
        typedef size_type difference_type;
        typedef const Agent* pointer;
        typedef Agent reference;
        friend class AgentVector;
        const std::shared_ptr<const AgentData>& _agent;
        const std::weak_ptr<AgentDataMap> _data;
        size_type _pos;
        AgentVector* _parent;

     public:
        operator AgentVector::const_iterator() const {
            return const_iterator(_parent, _agent, _data, _pos);
        }
        iterator(AgentVector* parent, const std::shared_ptr<const AgentData>& agent, std::weak_ptr<AgentDataMap> data, size_type pos = 0)
            : _agent(agent), _data(std::move(data)), _pos(pos), _parent(parent) { }
        iterator& operator++() { ++_pos; return *this; }
        iterator operator++(int) { iterator retval = *this; ++(*this); return retval; }
        bool operator==(iterator other) const { return _pos == other._pos &&
            (_data.lock() == other._data.lock() || (_data.lock() && other._data.lock() && *_data.lock() == *other._data.lock()));
        }
        bool operator!=(iterator other) const { return !(*this == other); }
        Agent operator*() const;
    };
    class const_iterator {
        typedef std::input_iterator_tag iterator_category;
        typedef CAgent value_type;
        typedef size_type difference_type;
        typedef const CAgent* pointer;
        typedef CAgent reference;
        friend class AgentVector;
        const std::shared_ptr<const AgentData> &_agent;
        const std::weak_ptr<AgentDataMap> _data;
        size_type _pos;
        AgentVector* _parent;

     public:
        const_iterator(AgentVector* parent, const std::shared_ptr<const AgentData>& agent, std::weak_ptr<AgentDataMap> data, size_type pos = 0)
            : _agent(agent), _data(std::move(data)), _pos(pos), _parent(parent) { }
        const_iterator& operator++() { ++_pos; return *this; }
        const_iterator operator++(int) { const_iterator retval = *this; ++(*this); return retval; }
        bool operator==(const_iterator other) const { return _pos == other._pos &&
            (_data.lock() == other._data.lock() || (_data.lock() && other._data.lock() && *_data.lock() == *other._data.lock()));
        }
        bool operator!=(const_iterator other) const { return !(*this == other); }
        CAgent operator*() const;
    };
    class reverse_iterator {
        typedef std::input_iterator_tag iterator_category;
        typedef Agent value_type;
        typedef size_type difference_type;
        typedef const Agent* pointer;
        typedef Agent reference;
        friend class AgentVector;
        const std::shared_ptr<const AgentData> &_agent;
        const std::weak_ptr<AgentDataMap> _data;
        size_type _pos;
        AgentVector* _parent;

     public:
        operator AgentVector::const_reverse_iterator() const {
            return const_reverse_iterator(_parent, _agent, _data, _pos);
        }
        explicit reverse_iterator(AgentVector* parent, const std::shared_ptr<const AgentData>& agent, std::weak_ptr<AgentDataMap> data, size_type pos = 0)
            : _agent(agent), _data(std::move(data)), _pos(pos), _parent(parent) { }
        reverse_iterator& operator++() { --_pos; return *this; }
        reverse_iterator operator++(int) { reverse_iterator retval = *this; ++(*this); return retval; }
        bool operator==(reverse_iterator other) const { return _pos == other._pos &&
            (_data.lock() == other._data.lock() || (_data.lock() && other._data.lock() && *_data.lock() == *other._data.lock()));
        }
        bool operator!=(reverse_iterator other) const { return !(*this == other); }
        Agent operator*() const;
    };
    class const_reverse_iterator {
        typedef std::input_iterator_tag iterator_category;
        typedef CAgent value_type;
        typedef size_type difference_type;
        typedef const CAgent* pointer;
        typedef CAgent reference;
        friend class AgentVector;
        const std::shared_ptr<const AgentData>& _agent;
        const std::weak_ptr<AgentDataMap> _data;
        size_type _pos;
        AgentVector* _parent;

     public:
        explicit const_reverse_iterator(AgentVector* parent, const std::shared_ptr<const AgentData>& agent, std::weak_ptr<AgentDataMap> data, size_type pos = 0)
            : _agent(agent), _data(std::move(data)), _pos(pos), _parent(parent) { }
        const_reverse_iterator& operator++() { --_pos; return *this; }
        const_reverse_iterator operator++(int) { const_reverse_iterator retval = *this; ++(*this); return retval; }
        bool operator==(const_reverse_iterator other) const { return _pos == other._pos &&
            (_data.lock() == other._data.lock() || (_data.lock() && other._data.lock() && *_data.lock() == *other._data.lock()));
        }
        bool operator!=(const_reverse_iterator other) const { return !(*this == other); }
        CAgent operator*() const;
    };
    explicit AgentVector(const CAgentDescription &agent_desc, size_type count = 0);
    explicit AgentVector(const AgentData &agent_desc, size_type count = 0);
    AgentVector(const AgentVector &other);
    AgentVector(AgentVector &&other) noexcept;
    AgentVector& operator=(const AgentVector &other);
    AgentVector& operator=(AgentVector &&other) noexcept;
    virtual ~AgentVector() = default;

    // Element access
    Agent at(size_type pos);
    CAgent at(size_type pos) const;
    Agent operator[](size_type pos);
    CAgent operator[](size_type pos) const;
    Agent front();
    CAgent front() const;
    Agent back();
    CAgent back() const;
    template<typename T>
    T *data(const std::string &variable_name);
    template<typename T>
    const T* data(const std::string &variable_name) const;
    void* data(const std::string& variable_name);
    const void* data(const std::string& variable_name) const;

    // Iterators
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    const_iterator cbegin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    const_iterator cend() const noexcept;
    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // Capacity
    bool empty() const;
    size_type size() const;
    static size_type max_size();
    void reserve(size_type new_cap);
    size_type capacity() const;
    void shrink_to_fit();

    // Modifiers
    void clear();
    void resetAllIDs();

    iterator insert(const_iterator pos, const AgentInstance& value);
    iterator insert(size_type pos, const AgentInstance& value);
    iterator insert(const_iterator pos, const Agent& value);
    iterator insert(size_type pos, const Agent& value);
#ifdef SWIG
    void py_insert(size_type pos, const AgentInstance& value);
    void py_insert(size_type pos, const Agent& value);
#endif
    iterator insert(const_iterator pos, size_type count, const AgentInstance& value);
    iterator insert(size_type pos, size_type count, const AgentInstance& value);
    iterator insert(const_iterator pos, size_type count, const Agent& value);
    iterator insert(size_type pos, size_type count, const Agent& value);
#ifdef SWIG
    void py_insert(size_type pos, size_type count, const AgentInstance& value);
    void py_insert(size_type pos, size_type count, const Agent& value);
#endif
    template<class InputIt>
    iterator insert(const_iterator pos, InputIt first, InputIt last);
    template<class InputIt>
    iterator insert(size_type pos, InputIt first, InputIt last);
    iterator erase(const_iterator pos);
    iterator erase(size_type pos);
#ifdef SWIG
    void py_erase(size_type pos);
#endif
    iterator erase(const_iterator first, const_iterator last);
    iterator erase(size_type first, size_type last);
#ifdef SWIG
    void py_erase(size_type first, size_type last);
#endif
    void push_back(const AgentInstance& value);
    void push_back();
    void pop_back();
    void resize(size_type count);
    void swap(AgentVector& other) noexcept;
    bool operator==(const AgentVector &other) const;
    bool operator!=(const AgentVector &other) const;

    // Util
    std::string getAgentName() const { return agent->name; }
    bool matchesAgentType(const AgentData &other) const;
    bool matchesAgentType(const CAgentDescription& other) const;
    std::type_index getVariableType(const std::string& variable_name) const;
    const VariableMap &getVariableMetaData() const;
    std::string getInitialState() const;

 protected:
    virtual void _insert(size_type pos, size_type count) { }
    virtual void _erase(size_type pos, size_type count) { }
    virtual void _changed(const std::string& variable_name, size_type pos) { }
    virtual void _changedAfter(const std::string &variable_name, size_type pos) { }
    virtual void _require(const std::string& variable_name) const { }
    virtual void _requireAll() const { }
    virtual void _requireLength() const { }
    void internal_resize(size_type count, bool init);
    void init(size_type first, size_type last);
    std::shared_ptr<const AgentData> agent;
    // Mutable, incase size is increased by DeviceAgentVector hidden application of HostAgentBirth
    mutable size_type _size;
    mutable size_type _capacity;
    std::shared_ptr<AgentDataMap> _data;
};

}  // namespace flamegpu

// @todo - why is this include part way down?
#include "flamegpu/simulation/AgentVector_Agent.h"

namespace flamegpu {

template<typename T>
T* AgentVector::data(const std::string& variable_name) {
    if (!variable_name.empty() && variable_name[0] == '_') {
        THROW exception::ReservedName("Agent variable names that begin with '_' are reserved for internal usage and cannot be changed directly, "
            "in AgentVector::data().");
    }
    // Is variable name found
    const auto &var = agent->variables.find(variable_name);
    if (var == agent->variables.end()) {
        THROW exception::InvalidAgentVar("Variable with name '%s' was not found in agent '%s', "
            "in AgentVector::data().",
            variable_name.c_str(), agent->name.c_str());
    }
    if (std::type_index(typeid(T)) != var->second.type) {
        THROW exception::InvalidVarType("Variable '%s' is of a different type. "
            "'%s' was expected, but '%s' was requested,"
            "in AgentVector::data().",
            variable_name.c_str(), var->second.type.name(), typeid(T).name());
    }
    // Does the map have a vector
    const auto& map_it = _data->find(variable_name);
    if (map_it != _data->end()) {
        _requireLength();
        _require(variable_name);
        _changedAfter(variable_name, 0);
        return static_cast<T*>(map_it->second->getDataPtr());
    }
    return nullptr;
}
template<typename T>
const T* AgentVector::data(const std::string& variable_name) const {
    // Is variable name found
    const auto& var = agent->variables.find(variable_name);
    if (var == agent->variables.end()) {
        THROW exception::InvalidAgentVar("Variable with name '%s' was not found in agent '%s', "
            "in AgentVector::data().",
            variable_name.c_str(), agent->name.c_str());
    }
    if (std::type_index(typeid(T)) != var->second.type) {
        THROW exception::InvalidVarType("Variable '%s' is of a different type. "
            "'%s' was expected, but '%s' was requested,"
            "in AgentVector::data().",
            variable_name.c_str(), var->second.type.name(), typeid(T).name());
    }
    // Does the map have a vector
    const auto& map_it = _data->find(variable_name);
    if (map_it != _data->end()) {
        _requireLength();
        _require(variable_name);
        return static_cast<T*>(map_it->second->getDataPtr());
    }
    return nullptr;
}

template<class InputIt>
AgentVector::iterator AgentVector::insert(const_iterator pos, InputIt first, InputIt last) {
    if (pos._agent != agent && *pos._agent != *agent) {
        THROW exception::InvalidAgent("Agent description mismatch, '%' provided to pos, '%' required, "
            "in AgentVector::push_back().\n",
            last._agent->name.c_str(), agent->name.c_str());
    }
    return insert(pos._pos, first, last);
}

template<class InputIt>
AgentVector::iterator AgentVector::insert(size_type pos, InputIt first, InputIt last) {
    // Insert elements inrange first-last before pos
    if (first == last)
        return iterator(this, agent, _data, pos);
    // Confirm they are for the same agent type
    if (first._agent != agent && *first._agent != *agent) {
        THROW exception::InvalidAgent("Agent description mismatch, '%' provided to first, '%' required, "
            "in AgentVector::push_back().\n",
            first._agent->name.c_str(), agent->name.c_str());
    }
    if (last._agent != agent && *last._agent != *agent) {
        THROW exception::InvalidAgent("Agent description mismatch, '%' provided to last, '%' required, "
            "in AgentVector::push_back().\n",
            last._agent->name.c_str(), agent->name.c_str());
    }
    // Expand capacity if required
    const size_type first_copy_index = first._pos < last._pos ? first._pos : last._pos;
    const size_type end_copy_index = first._pos < last._pos ? last._pos : first._pos;
    const size_type copy_count = end_copy_index - first_copy_index;
    {
        _requireLength();
        size_type new_capacity = _capacity;
        assert((_capacity * RESIZE_FACTOR) + 1 > _capacity);
        while (_size + copy_count > new_capacity) {
            new_capacity = static_cast<size_type>(new_capacity * RESIZE_FACTOR) + 1;
        }
        internal_resize(new_capacity, true);
    }
    // Get first index;
    const size_type insert_index = pos;
    // Fix each variable
    auto first_data = first._data.lock();
    if (!first_data) {
        THROW exception::ExpiredWeakPtr("The AgentVector which owns the passed iterators has been deallocated, "
            "in AgentVector::insert().\n");
    }
    // If we are not appending, ensure we have upto date device data
    if (pos < _size)
        _requireAll();
    const id_t ID_DEFAULT = ID_NOT_SET;
    for (const auto& v : agent->variables) {
        const auto it = _data->find(v.first);
        char* t_data = static_cast<char*>(it->second->getDataPtr());
        const size_t variable_size = v.second.type_size * v.second.elements;
        // Move all items after this index backwards count places
        if (_size) {
            for (unsigned int i = _size - 1; i >= insert_index; --i) {
                // Copy items individually, incase the src and destination overlap
                memcpy(t_data + (i + copy_count) * variable_size, t_data + i * variable_size, variable_size);
            }
        }
        // Copy across item data, ID has a special case, where it is default init instead of being copied
        if (v.first == ID_VARIABLE_NAME) {
            if (v.second.elements != 1 || v.second.type != std::type_index(typeid(id_t))) {
                THROW exception::InvalidOperation("Agent's internal ID variable is not type %s[1], "
                        "in AgentVector::insert()\n", std::type_index(typeid(id_t)).name());
            }
            for (unsigned int i = insert_index; i < insert_index + copy_count; ++i) {
                memcpy(t_data + i * variable_size, &ID_DEFAULT, sizeof(id_t));
            }
        } else {
            const auto other_it = first_data->find(v.first);
            const char* o_data = static_cast<const char*>(other_it->second->getReadOnlyDataPtr());
            memcpy(t_data + insert_index * variable_size, o_data + first_copy_index * variable_size, copy_count * variable_size);
        }
    }
    // Increase size
    _size += copy_count;
    // Notify subclasses
    _insert(pos, copy_count);
    // Return iterator to first inserted item
    return iterator(this, agent, _data, insert_index);
}

#ifdef SWIG
void AgentVector::py_insert(size_type pos, const AgentInstance& value) {
    insert(pos, value);
}
void AgentVector::py_insert(size_type pos, const Agent& value) {
    insert(pos, value);
}
void AgentVector::py_insert(size_type pos, size_type count, const AgentInstance& value) {
    insert(pos, count, value);
}
void AgentVector::py_insert(size_type pos, size_type count, const Agent& value) {
    insert(pos, count, value);
}
void AgentVector::py_erase(size_type pos) {
    erase(pos);
}
void AgentVector::py_erase(size_type first, size_type last) {
    erase(first, last);
}
#endif  // ifdef SWIG

}  // namespace flamegpu

#endif  // INCLUDE_FLAMEGPU_SIMULATION_AGENTVECTOR_H_