Program Listing for File AgentVector.cpp
↰ Return to documentation for file (src/flamegpu/simulation/AgentVector.cpp
)
#include "flamegpu/simulation/AgentVector.h"
#include <limits>
#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<AgentDataMap>()) {
resize(count);
}
AgentVector::AgentVector(const AgentVector& other)
: agent(other.agent->clone())
, _size(0)
, _capacity(0)
, _data(std::make_shared<AgentDataMap>()) {
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<AgentDataMap>()) {
// 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<AgentVector*>(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<AgentVector*>(this), agent, _data, 0);
}
AgentVector::const_iterator AgentVector::cbegin() const noexcept {
_requireLength();
return const_iterator(const_cast<AgentVector*>(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<AgentVector*>(this), agent, _data, _size);
}
AgentVector::const_iterator AgentVector::cend() const noexcept {
return const_iterator(const_cast<AgentVector*>(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<AgentVector*>(this), agent, _data, _size-1);
}
AgentVector::const_reverse_iterator AgentVector::crbegin() const noexcept {
_requireLength();
return const_reverse_iterator(const_cast<AgentVector*>(this), agent, _data, _size-1);
}
AgentVector::reverse_iterator AgentVector::rend() noexcept {
return reverse_iterator(this, agent, _data, std::numeric_limits<size_type>::max());
}
AgentVector::const_reverse_iterator AgentVector::rend() const noexcept {
return const_reverse_iterator(const_cast<AgentVector*>(this), agent, _data, std::numeric_limits<size_type>::max());
}
AgentVector::const_reverse_iterator AgentVector::crend() const noexcept {
return const_reverse_iterator(const_cast<AgentVector*>(this), agent, _data, std::numeric_limits<size_type>::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<size_type>::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<id_t*>(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<char*>(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<size_type>(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<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
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<size_type>(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<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
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<char*>(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<char*>(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<size_type>(_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<char*>(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<detail::GenericMemoryVector>(v.second.memory_vector->clone());
t->resize(count);
// Default init all new elements
if (init) {
char* t_data = static_cast<char*>(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<const char*>(it_a->second->getReadOnlyDataPtr());
const char* data_b = static_cast<const char*>(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