.. _program_listing_file_src_flamegpu_simulation_AgentVector.cpp: Program Listing for File AgentVector.cpp ======================================== |exhale_lsh| :ref:`Return to documentation for file ` (``src/flamegpu/simulation/AgentVector.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "flamegpu/simulation/AgentVector.h" #include #include "flamegpu/model/AgentDescription.h" #include "flamegpu/simulation/AgentVector_Agent.h" // @todo - this shouldn't be required anymore? #ifdef max #undef max // Unclear where this definition is leaking from #endif namespace flamegpu { const float AgentVector::RESIZE_FACTOR = 1.5f; AgentVector::AgentVector(const CAgentDescription& agent_desc, size_type count) : AgentVector(*agent_desc.agent, count) { } AgentVector::AgentVector(const AgentData& agent_desc, size_type count) : agent(agent_desc.clone()) , _size(0) , _capacity(0) , _data(std::make_shared()) { resize(count); } AgentVector::AgentVector(const AgentVector& other) : agent(other.agent->clone()) , _size(0) , _capacity(0) , _data(std::make_shared()) { clear(); insert(0, other.begin(), other.end()); } AgentVector::AgentVector(AgentVector&& other) noexcept : agent(other.agent->clone()) , _size(other._size) , _capacity(other._capacity) , _data(std::make_shared()) { // Purge our data _data->clear(); // Swap data std::swap(*_data, *other._data); // Not 100% sure this will work as intended // Purge other other._size = 0; other._capacity = 0; } AgentVector& AgentVector::operator=(const AgentVector& other) { // Self assignment if (this == &other) return *this; if (*agent != *other.agent) { throw std::exception(); // AgentVectors are for different AgentDescriptions } // Copy size internal_resize(other.size(), false); _size = other.size(); if (_size) { // Copy data for (const auto& v : agent->variables) { auto &this_it = _data->at(v.first); const auto &other_it = other._data->at(v.first); memcpy(this_it->getDataPtr(), other_it->getReadOnlyDataPtr(), _size * v.second.type_size * v.second.elements); } } return *this; } AgentVector& AgentVector::operator=(AgentVector&& other) noexcept { agent = other.agent->clone(); _size = other._size; _capacity = other._capacity; // Purge our data _data->clear(); // Swap data std::swap(*_data, *other._data); // Not 100% sure this will work as intended // Purge other other._size = 0; other._capacity = 0; return *this; } AgentVector::Agent AgentVector::at(size_type pos) { if (pos >= _size) { _requireLength(); if (pos >= _size) { THROW exception::OutOfBoundsException("pos (%u) exceeds length of vector (%u) in AgentVector::at()", pos, _size); } } // Return the agent instance return Agent(this, agent, _data, pos); } AgentVector::CAgent AgentVector::at(size_type pos) const { if (pos >= _size) { _requireLength(); if (pos >= _size) { THROW exception::OutOfBoundsException("pos (%u) exceeds length of vector (%u) in AgentVector::at()", pos, _size); } } return CAgent(const_cast(this), agent, _data, pos); } AgentVector::Agent AgentVector::operator[](size_type pos) { return at(pos); } AgentVector::CAgent AgentVector::operator[](size_type pos) const { return at(pos); } AgentVector::Agent AgentVector::front() { return at(0); } AgentVector::CAgent AgentVector::front() const { return at(0); } AgentVector::Agent AgentVector::back() { _requireLength(); return at(_size - 1); } AgentVector::CAgent AgentVector::back() const { _requireLength(); return at(_size - 1); } void* 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()); } // 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 map_it->second->getDataPtr(); } return nullptr; } const void* 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()); } // Does the map have a vector const auto& map_it = _data->find(variable_name); if (map_it != _data->end()) { _requireLength(); _require(variable_name); return map_it->second->getDataPtr(); } return nullptr; } AgentVector::iterator AgentVector::begin() noexcept { _requireLength(); return iterator(this, agent, _data, 0); } AgentVector::const_iterator AgentVector::begin() const noexcept { _requireLength(); return const_iterator(const_cast(this), agent, _data, 0); } AgentVector::const_iterator AgentVector::cbegin() const noexcept { _requireLength(); return const_iterator(const_cast(this), agent, _data, 0); } AgentVector::iterator AgentVector::end() noexcept { return iterator(this, agent, _data, _size); } AgentVector::const_iterator AgentVector::end() const noexcept { return const_iterator(const_cast(this), agent, _data, _size); } AgentVector::const_iterator AgentVector::cend() const noexcept { return const_iterator(const_cast(this), agent, _data, _size); } AgentVector::reverse_iterator AgentVector::rbegin() noexcept { _requireLength(); return reverse_iterator(this, agent, _data, _size - 1); } AgentVector::const_reverse_iterator AgentVector::rbegin() const noexcept { _requireLength(); return const_reverse_iterator(const_cast(this), agent, _data, _size-1); } AgentVector::const_reverse_iterator AgentVector::crbegin() const noexcept { _requireLength(); return const_reverse_iterator(const_cast(this), agent, _data, _size-1); } AgentVector::reverse_iterator AgentVector::rend() noexcept { return reverse_iterator(this, agent, _data, std::numeric_limits::max()); } AgentVector::const_reverse_iterator AgentVector::rend() const noexcept { return const_reverse_iterator(const_cast(this), agent, _data, std::numeric_limits::max()); } AgentVector::const_reverse_iterator AgentVector::crend() const noexcept { return const_reverse_iterator(const_cast(this), agent, _data, std::numeric_limits::max()); } bool AgentVector::empty() const { _requireLength(); return _size == 0; } flamegpu::size_type AgentVector::size() const { _requireLength(); return _size; } flamegpu::size_type AgentVector::max_size() { return std::numeric_limits::max() - 1; } void AgentVector::reserve(size_type new_cap) { if (new_cap > _capacity) { internal_resize(new_cap, true); } } flamegpu::size_type AgentVector::capacity() const { return _capacity; } void AgentVector::shrink_to_fit() { _requireLength(); if (_size > _capacity) { internal_resize(_size, false); } } void AgentVector::clear() { _requireLength(); // Re initialise all variables if (_capacity) { init(0, _size); } _size = 0; _erase(0, _size); } #ifdef _MSC_VER #pragma warning(push, 1) #pragma warning(disable : 4127) // Suppress condition expression is constant // The constant condition can be made constexpr in future with C++17 #endif void AgentVector::resetAllIDs() { _require(ID_VARIABLE_NAME); const auto it = _data->find(ID_VARIABLE_NAME); if (it != _data->end()) { constexpr id_t DEFAULT_VALUE = ID_NOT_SET; id_t* t_data = static_cast(it->second->getDataPtr()); if (DEFAULT_VALUE == 0) { memset(t_data, 0, _size * sizeof(id_t)); } else { for (unsigned int i = 0; i < _size; ++i) { memcpy(t_data + i, &DEFAULT_VALUE, sizeof(id_t)); } } } else { THROW exception::InvalidOperation("Agent '%s' is missing internal ID variable, " "in AgentVector::resetAllIDs()\n", agent->name.c_str()); } if (_size) { // Mark all indices as changed (there isn't currently a single fn for this) _changed(ID_VARIABLE_NAME, 0); _changedAfter(ID_VARIABLE_NAME, 0); } } #ifdef _MSC_VER #pragma warning(pop) #endif void AgentVector::init(size_type first, size_type last) { if (first >= last) { THROW exception::InvalidOperation("Last (%u) must exceed first(%u), " "in AgentVector::init()\n", last, first); } else if (last > _capacity) { THROW exception::OutOfBoundsException("Last (%u) exceeds capacity (%u) in AgentVector::init(), " "in AgentVector::init()\n", last, _capacity); } // Re initialise all variables for (const auto& v : agent->variables) { const auto it = _data->find(v.first); const size_t variable_size = v.second.type_size * v.second.elements; char* t_data = static_cast(it->second->getDataPtr()); for (unsigned int i = first; i < last; ++i) { memcpy(t_data + i * variable_size, v.second.default_value, variable_size); } } } AgentVector::iterator AgentVector::insert(const_iterator pos, const AgentInstance& value) { return insert(pos, 1, value); } AgentVector::iterator AgentVector::insert(size_type pos, const AgentInstance& value) { return insert(pos, 1, value); } AgentVector::iterator AgentVector::insert(const_iterator pos, const Agent& value) { return insert(pos, 1, value); } AgentVector::iterator AgentVector::insert(size_type pos, const Agent& value) { return insert(pos, 1, value); } AgentVector::iterator AgentVector::insert(const_iterator pos, size_type count, const AgentInstance& value) { return insert(pos._pos, count, value); } AgentVector::iterator AgentVector::insert(size_type pos, size_type count, const AgentInstance& value) { if (count == 0) return iterator(this, agent, _data, pos); // Confirm they are for the same agent type if (value._agent != agent && *value._agent != *agent) { THROW exception::InvalidAgent("Agent description mismatch, '%' provided, '%' required, " "in AgentVector::push_back().\n", value._agent->name.c_str(), agent->name.c_str()); } // Expand capacity if required { _requireLength(); size_type new_capacity = _capacity; assert((_capacity * RESIZE_FACTOR) + 1 > _capacity); while (_size + count > new_capacity) { new_capacity = static_cast(new_capacity * RESIZE_FACTOR) + 1; } internal_resize(new_capacity, true); } // If we are not appending, ensure we have upto date device data if (pos < _size) _requireAll(); // Get first index; const size_type insert_index = pos; // Fix each variable for (const auto& v : agent->variables) { const auto it = _data->find(v.first); char* t_data = static_cast(it->second->getDataPtr()); const size_t variable_size = v.second.type_size * v.second.elements; // Move all items after this index backwards count places for (unsigned int i = _size - 1; i >= insert_index; --i) { // Copy items individually, incase the src and destination overlap memcpy(t_data + (i + count) * variable_size, t_data + i * variable_size, variable_size); } // Copy across item data const auto other_it = value._data.find(v.first); for (unsigned int i = insert_index; i < insert_index + count; ++i) { memcpy(t_data + i * variable_size, other_it->second.ptr, variable_size); } } // Increase size _size += count; // Notify subclasses _insert(pos, count); // Return iterator to first inserted item return iterator(this, agent, _data, insert_index); } AgentVector::iterator AgentVector::insert(const_iterator pos, size_type count, const Agent& value) { return insert(pos._pos, count, value); } AgentVector::iterator AgentVector::insert(size_type pos, size_type count, const Agent& value) { if (count == 0) return iterator(this, agent, _data, pos); // Confirm they are for the same agent type if (value._agent != agent && *value._agent != *agent) { THROW exception::InvalidAgent("Agent description mismatch, '%' provided, '%' required, " "in AgentVector::push_back().\n", value._agent->name.c_str(), agent->name.c_str()); } // Expand capacity if required { _requireLength(); size_type new_capacity = _capacity; assert((_capacity * RESIZE_FACTOR) + 1 > _capacity); while (_size + count > new_capacity) { new_capacity = static_cast(new_capacity * RESIZE_FACTOR) + 1; } internal_resize(new_capacity, true); } // If we are not appending, ensure we have upto date device data if (pos < _size) _requireAll(); // Get first index; const size_type insert_index = pos; // Fix each variable auto value_data = value._data.lock(); if (!value_data) { THROW exception::ExpiredWeakPtr("The AgentVector which owns the passed AgentVector::Agent has been deallocated, " "in AgentVector::insert().\n"); } 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(it->second->getDataPtr()); const size_t variable_size = v.second.type_size * v.second.elements; // Move all items after this index backwards count places for (unsigned int i = _size - 1; i >= insert_index; --i) { // Copy items individually, incase the src and destination overlap memcpy(t_data + (i + 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 + count; ++i) { memcpy(t_data + i * variable_size, &ID_DEFAULT, sizeof(id_t)); } } else { const auto other_it = value_data->find(v.first); char* src_data = static_cast(other_it->second->getDataPtr()); for (unsigned int i = insert_index; i < insert_index + count; ++i) { memcpy(t_data + i * variable_size, src_data + value.index * variable_size, variable_size); } } } // Increase size _size += count; // Notify subclasses _insert(pos, count); // Return iterator to first inserted item return iterator(this, agent, _data, insert_index); } AgentVector::iterator AgentVector::erase(const_iterator pos) { const auto first = pos++; return erase(first._pos, pos._pos); } AgentVector::iterator AgentVector::erase(size_type pos) { const auto first = pos++; return erase(first, pos); } AgentVector::iterator AgentVector::erase(const_iterator first, const_iterator last) { // Confirm they are for the same agent type if (first._agent != agent && *first._agent != *agent) { THROW exception::InvalidAgent("Agent description mismatch, '%' provided, '%' 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, '%' required, " "in AgentVector::push_back().\n", last._agent->name.c_str(), agent->name.c_str()); } return erase(first._pos, last._pos); } AgentVector::iterator AgentVector::erase(size_type first, size_type last) { if (first == last) return iterator(this, agent, _data, last); // Get first index; const size_type first_remove_index = first < last ? first : last; const size_type first_move_index = first < last ? last : first; const size_type erase_count = first_move_index - first_remove_index; _requireLength(); const size_type first_empty_index = _size - erase_count; const size_type last_empty_index = _size; // Ensure indicies are in bounds if (first_remove_index >= _size) { THROW exception::OutOfBoundsException("%u is not a valid index into the vector, " "in AgentVector::erase()\n", first_remove_index); } else if (first_move_index > _size) { THROW exception::OutOfBoundsException("%u is not a valid index to the end of a range of vector items, " "it must point to after the final selected item, " "in AgentVector::erase()\n", first_move_index); } // If we are not erasing from the end, ensure we have upto date device data if (first_move_index != _size) _requireAll(); // Fix each variable for (const auto& v : agent->variables) { const auto it = _data->find(v.first); char* t_data = static_cast(it->second->getDataPtr()); const size_t variable_size = v.second.type_size * v.second.elements; // Move all items after this index forwards count places for (unsigned int i = first_move_index; i < _size; ++i) { // Copy items individually, incase the src and destination overlap memcpy(t_data + (i - erase_count) * variable_size, t_data + i * variable_size, variable_size); } } // Initialise newly empty variables init(first_empty_index, last_empty_index); // Decrease size _size -= erase_count; // Notify subclasses _erase(first_remove_index, erase_count); // Return iterator following the last removed element return iterator(this, agent, _data, first_remove_index + 1); } void AgentVector::push_back(const AgentInstance& value) { insert(cend(), value); } void AgentVector::push_back() { // Expand capacity if required { _requireLength(); size_type new_capacity = _capacity; assert((_capacity * RESIZE_FACTOR) + 1 > _capacity); while (_size + 1 > new_capacity) { new_capacity = static_cast(_capacity * RESIZE_FACTOR) + 1; } internal_resize(new_capacity, true); } // Notify subclasses & increase size _insert(_size++, 1); } void AgentVector::pop_back() { _requireLength(); if (_size) { --_size; // Reset removed item to default value for (const auto& v : agent->variables) { const auto it = _data->find(v.first); const size_t variable_size = v.second.type_size * v.second.elements; char* t_data = static_cast(it->second->getDataPtr()); memcpy(t_data + _size * variable_size, v.second.default_value, variable_size); } // Notify subclasses _erase(_size, 1); } } void AgentVector::resize(size_type count) { _requireLength(); const size_type old_size = _size; internal_resize(count, true); _size = count; // Notify subclasses if (count > old_size) { _insert(old_size, count - old_size); } else if (count < old_size) { _erase(count, old_size - count); } } void AgentVector::internal_resize(size_type count, bool init) { if (count == _capacity) return; for (const auto& v : agent->variables) { // For each variable inside agent, add it to the map or replace it in the map const auto it = _data->find(v.first); const size_t variable_size = v.second.type_size * v.second.elements; if (it == _data->end()) { // Need to create the variable's vector auto t = std::unique_ptr(v.second.memory_vector->clone()); t->resize(count); // Default init all new elements if (init) { char* t_data = static_cast(t->getDataPtr()); for (unsigned int i = 0; i < count; ++i) { memcpy(t_data + i * variable_size, v.second.default_value, variable_size); } } _data->emplace(v.first, std::move(t)); } else { // Need to resize the variables vector it->second->resize(count); } } size_type old_capacity = _capacity; _capacity = count; // Default init all new elements if (init && count > old_capacity) { this->init(old_capacity, _capacity); } } void AgentVector::swap(AgentVector& other) noexcept { std::swap(_data, other._data); std::swap(_capacity, other._capacity); std::swap(_size, other._size); std::swap(agent, other.agent); } bool AgentVector::operator==(const AgentVector& other) const { _requireLength(); if (_size != other._size) return false; if (*agent != *other.agent) return false; _requireAll(); for (const auto& v : agent->variables) { const auto it_a = _data->find(v.first); const auto it_b = other._data->find(v.first); const char* data_a = static_cast(it_a->second->getReadOnlyDataPtr()); const char* data_b = static_cast(it_b->second->getReadOnlyDataPtr()); for (size_type i = 0; i < _size; ++i) if (data_a[i] != data_b[i]) return false; } return true; } bool AgentVector::operator!=(const AgentVector& other) const { return !((*this) == other); } bool AgentVector::matchesAgentType(const AgentData& other) const { return *agent == other; } bool AgentVector::matchesAgentType(const CAgentDescription& other) const { return *agent == *other.agent; } std::type_index AgentVector::getVariableType(const std::string &variable_name) const { const auto &it = agent->variables.find(variable_name); if (it == agent->variables.end()) { THROW exception::InvalidAgentVar("Agent '%s' does not contain variable with name '%s', " "in AgentVector::getVariableType()\n", agent->name.c_str(), variable_name.c_str()); } return it->second.type; } const VariableMap& AgentVector::getVariableMetaData() const { return agent->variables; } std::string AgentVector::getInitialState() const { return agent->initial_state; } AgentVector::Agent AgentVector::iterator::operator*() const { return Agent(_parent, _agent, _data, _pos); } AgentVector::CAgent AgentVector::const_iterator::operator*() const { return CAgent(_parent, _agent, _data, _pos); } AgentVector::Agent AgentVector::reverse_iterator::operator*() const { return Agent(_parent, _agent, _data, _pos); } AgentVector::CAgent AgentVector::const_reverse_iterator::operator*() const { return CAgent(_parent, _agent, _data, _pos); } } // namespace flamegpu