13 #include <string_view>
14 #include <type_traits>
20 template <
typename T,
typename... Ts>
21 inline constexpr
bool type_in_pack_v = std::disjunction_v<std::is_same<T, Ts>...>;
23 template <
typename T,
typename... Types>
29 template <
typename T,
typename Tuple>
31 template <
typename T,
typename... Ts>
33 static constexpr std::size_t
value = 0;
35 template <
typename T,
typename U,
typename... Ts>
39 template <
typename T,
typename... Ts>
43 static_assert(Ts_are_unique,
44 "index_of_type_v<T, Ts...> assumes there are no duplicates in Ts...");
45 static_assert(T_is_in_Ts,
"index_of_type_v<T, Ts...> assumes that T occurs in Ts...");
48 if constexpr (Ts_are_unique && T_is_in_Ts) {
51 return std::numeric_limits<std::size_t>::max();
57 template <
typename T,
typename =
void>
61 has_default_value_v<T, std::void_t<decltype(std::declval<T>().default_value())>> =
true;
67 -> decltype(
t.array_dimension(), 0) {
68 return t.array_dimension();
72 return t.array_dimension(
i);
80 template <
typename T,
typename =
void>
90 if constexpr (has_num_variables_v<T>) {
91 return t.num_variables();
98 template <
typename T,
typename =
void>
100 template <
typename T>
101 struct optional<T, std::void_t<decltype(T::optional)>> {
102 constexpr
static bool value = T::optional;
104 template <
typename T>
114 template <
typename Tag>
121 template <
typename Tag>
123 -> decltype(
static_cast<void>(tag.name(
field_index)), std::string()) {
127 template <
typename Tag>
134 constexpr std::string_view
prefix{
"neuron::container::"};
135 if (std::string_view{ret}.substr(0,
prefix.size()) ==
prefix) {
136 ret.erase(0,
prefix.size());
147 template <
typename Tag>
149 if constexpr (has_num_variables_v<Tag>) {
170 template <
typename Rng>
172 if (
range.size() != size) {
173 throw std::runtime_error(
"invalid permutation vector: wrong size");
176 std::vector<bool> seen(size,
false);
177 for (
auto i = 0ul;
i < size; ++
i) {
178 auto const val =
range[
i];
179 trivial = trivial && (
i == val);
180 if (!(val >= 0 && val < size)) {
181 throw std::runtime_error(
"invalid permutation vector: value out of range");
184 throw std::runtime_error(
"invalid permutation vector: repeated value " +
214 template <
typename T>
216 static_assert(std::is_trivially_destructible_v<T>,
"defer_delete does not call destructors");
222 template <
typename Tag, FieldImplementation impl>
227 static_assert(!has_num_variables_v<Tag>);
232 m_data_ptr = std::make_unique<data_type*[]>(1);
248 template <may_cause_reallocation might_reallocate,
typename Callable>
264 template <
typename Callable>
290 if constexpr (has_default_value_v<Tag>) {
295 m_data_ptr = std::make_unique<data_type*[]>(1);
383 template <
typename Tag>
386 static_assert(has_num_variables_v<Tag>);
390 , m_data_ptrs{std::make_unique<
data_type*[]>(
m_tag.num_variables())} {
391 update_data_ptr_storage();
392 auto const num =
m_tag.num_variables();
393 m_array_dims.reserve(num);
394 m_array_dim_prefix_sums.reserve(num);
395 for (
auto i = 0;
i <
m_tag.num_variables(); ++
i) {
397 m_array_dim_prefix_sums.push_back(
398 (m_array_dim_prefix_sums.empty() ? 0 : m_array_dim_prefix_sums.back()) +
399 m_array_dims.back());
423 return m_array_dims.data();
430 return m_array_dim_prefix_sums.data();
436 if (
auto const num_fields =
m_tag.num_variables();
field_index >= num_fields) {
441 if (array_index >= array_dim) {
455 return m_data_ptrs.get();
464 template <may_cause_reallocation might_reallocate,
typename Callable>
470 update_data_ptr_storage();
481 template <
typename Callable>
494 auto const num_fields =
m_tag.num_variables();
496 auto const array_dim = m_array_dims[
field];
497 if (legacy_index < total + array_dim) {
498 auto const array_index = legacy_index - total;
499 return {
field, array_index};
503 throw std::runtime_error(
"could not translate legacy index " +
582 std::string_view
field()
const override {
585 std::size_t
size()
const override {
595 std::vector<detail::index_column_tag::type>
const& vec,
600 (vec.capacity() - vec.size()) *
sizeof(vec[0]);
605 std::vector<typename Tag::type>
const& vec,
628 template <
typename Container>
655 template <
typename,
typename...>
682 template <
typename Storage,
typename... Tags>
696 soa(Tags... tag_instances)
738 [[nodiscard]] std::size_t
size()
const {
741 auto const check_size =
m_indices.size();
743 [check_size](
auto const& tag,
auto const& vec,
int field_index,
int array_dim) {
744 auto const size = vec.size();
769 throw_error(
"shrink() called on a frozen structure");
771 for_each_vector<detail::may_cause_reallocation::Yes>(
772 [](
auto const& tag,
auto& vec,
int field_index,
int array_dim) {
788 std::lock_guard _{
m_mut};
790 throw_error(
"erase() called on a frozen structure");
792 mark_as_unsorted_impl<true>();
793 auto const old_size =
size();
795 if (
i != old_size - 1) {
798 for_each_vector<detail::may_cause_reallocation::No>(
799 [
i](
auto const& tag,
auto& vec,
int field_index,
int array_dim) {
800 ::std::swap_ranges(::
std::next(vec.begin(),
i * array_dim),
801 ::
std::next(vec.begin(), (
i + 1) * array_dim),
807 for_each_vector<detail::may_cause_reallocation::No>(
808 [new_size = old_size - 1](
auto const& tag,
auto& vec,
int field_index,
int array_dim) {
809 vec.resize(new_size * array_dim);
816 static_assert(detail::are_types_unique_v<Tags...>,
"All tag types should be unique");
817 template <
typename Tag>
838 template <detail::may_cause_reallocation might_reallocate,
typename Callable>
850 typename... RemainingTags>
853 std::get<tag_index_v<Tag>>(
m_data).
template for_each_vector<might_reallocate>(callable);
857 template <detail::may_cause_reallocation,
typename Callable>
877 template <
typename Callable>
883 template <
typename Callable,
typename Tag,
typename... RemainingTags>
885 Callable tmp_callable = std::get<tag_index_v<Tag>>(
m_data).
template for_each_vector<>(
890 template <
typename Callable>
903 std::lock_guard _{
m_mut};
914 std::lock_guard _{
m_mut};
968 std::lock_guard _{
m_mut};
988 std::lock_guard _{
m_mut};
990 throw_error(
"mark_as_sorted() given a token that was not the only valid one");
1007 std::lock_guard _{
m_mut};
1008 mark_as_unsorted_impl<false>();
1044 template <
typename Arg>
1058 template <
typename Range>
1063 std::size_t
const my_size{
size()};
1066 std::lock_guard _{
m_mut};
1074 "apply_reverse_permutation() given a token that was not the only valid one");
1079 for (std::size_t
i = 0;
i < my_size; ++
i) {
1080 while (
i != permutation[
i]) {
1082 auto const next = permutation[
i];
1083 for_each_vector<detail::may_cause_reallocation::No>(
1086 ::std::swap_ranges(::
std::next(vec.begin(),
i * array_dim),
1087 ::
std::next(vec.begin(), (
i + 1) * array_dim),
1090 swap(permutation[
i], permutation[
next]);
1094 for (
auto i = 0ul;
i < my_size; ++
i) {
1117 template <
bool internal>
1124 throw_error(
"mark_as_unsorted() called on a frozen structure");
1128 bool const execute_callback{
m_sorted || !
internal};
1155 std::lock_guard _{
m_mut};
1157 throw_error(
"acquire_owning_identifier() called on a frozen structure");
1168 mark_as_unsorted_impl<true>();
1170 auto const old_size =
size();
1171 for_each_vector<detail::may_cause_reallocation::Yes>(
1172 [](
auto const& tag,
auto& vec,
auto field_index,
auto array_dim) {
1173 using Tag = ::std::decay_t<decltype(tag)>;
1174 if constexpr (detail::has_default_value_v<Tag>) {
1175 vec.insert(vec.end(), array_dim, tag.default_value());
1177 vec.insert(vec.end(), array_dim, {});
1196 return {
const_cast<Storage*
>(
static_cast<Storage const*
>(
this)),
m_indices[offset]};
1213 template <
typename Tag>
1214 [[nodiscard]] constexpr Tag
const&
get_tag()
const {
1215 return std::get<tag_index_v<Tag>>(
m_data).tag();
1218 template <
typename Tag>
1232 template <
typename Tag>
1234 static_assert(has_tag_v<Tag>);
1235 static_assert(!detail::has_num_variables_v<Tag>);
1236 auto& field_data = std::get<tag_index_v<Tag>>(
m_data);
1238 if (!field_data.active()) {
1239 std::lock_guard _{
m_mut};
1240 throw_error(
"get(offset) called for a disabled optional field");
1243 return field_data.data_ptrs()[0][offset];
1254 template <
typename Tag>
1256 static_assert(has_tag_v<Tag>);
1257 static_assert(!detail::has_num_variables_v<Tag>);
1258 auto const& field_data = std::get<tag_index_v<Tag>>(
m_data);
1260 if (!field_data.active()) {
1261 std::lock_guard _{
m_mut};
1262 throw_error(
"get(offset) const called for a disabled optional field");
1265 return field_data.data_ptrs()[0][offset];
1275 template <
typename Tag>
1278 int array_index = 0)
const {
1279 static_assert(has_tag_v<Tag>);
1280 static_assert(!detail::has_num_variables_v<Tag>);
1281 auto const& field_data = std::get<tag_index_v<Tag>>(
m_data);
1283 if constexpr (detail::optional_v<Tag>) {
1284 if (!field_data.active()) {
1288 auto const array_dim = field_data.array_dims()[0];
1290 assert(array_index >= 0);
1291 assert(array_index < array_dim);
1292 return {
std::move(
id), field_data.data_ptrs(), array_dim, array_index};
1300 template <
typename Tag>
1304 int array_index = 0)
const {
1305 static_assert(has_tag_v<Tag>);
1306 static_assert(detail::has_num_variables_v<Tag>);
1307 auto const array_dim = std::get<tag_index_v<Tag>>(
m_data).check_array_dim(
field_index,
1331 template <
typename Tag>
1334 int array_index = 0) {
1335 auto const array_dim = std::get<tag_index_v<Tag>>(
m_data).check_array_dim(
field_index,
1337 return std::get<tag_index_v<Tag>>(
m_data)
1338 .data_ptrs()[
field_index][offset * array_dim + array_index];
1349 template <
typename Tag>
1352 int array_index = 0)
const {
1353 auto const array_dim = std::get<tag_index_v<Tag>>(
m_data).check_array_dim(
field_index,
1355 return std::get<tag_index_v<Tag>>(
m_data)
1356 .data_ptrs()[
field_index][offset * array_dim + array_index];
1385 auto const& tag,
auto const& vec,
int field_index,
int array_dim) {
1386 using Tag = ::std::decay_t<decltype(tag)>;
1387 if constexpr (!std::is_same_v<Tag, detail::index_column_tag>) {
1401 auto*
const ptr = input_handle.
get<
Data*>();
1402 if (ptr < vec.data() || ptr >=
std::next(vec.data(), vec.size())) {
1405 auto const physical_row = ptr - vec.
data();
1406 assert(physical_row < vec.size());
1408 int const array_index = physical_row % array_dim;
1409 int const logical_row = physical_row / array_dim;
1430 template <
typename Tag>
1432 static_assert(has_tag_v<Tag>);
1433 static_assert(!detail::has_num_variables_v<Tag>);
1434 auto*
const data_ptrs = std::get<tag_index_v<Tag>>(
m_data).data_ptrs();
1435 if constexpr (detail::optional_v<Tag>) {
1441 return ptr == data_ptrs[0];
1451 std::unique_ptr<utils::storage_info> opt_info;
1457 this](
auto const& tag,
auto const& vec,
int field_index,
int array_dim) {
1462 if (vec.data() != cont) {
1468 auto impl_ptr = std::make_unique<detail::storage_info_impl>();
1469 auto&
info = *impl_ptr;
1470 info.m_container =
static_cast<Storage const&
>(*this).name();
1472 info.m_size = vec.size();
1483 template <
typename Tag>
1485 return std::get<tag_index_v<Tag>>(
m_data).data_ptrs();
1492 template <
typename Tag>
1494 return std::get<tag_index_v<Tag>>(
m_data).array_dims();
1497 template <
typename Tag>
1503 template <
typename Tag>
1511 template <
typename Tag>
1513 return std::get<tag_index_v<Tag>>(
m_data).array_dim_prefix_sums();
1516 template <
typename Tag>
1524 template <
typename Tag>
1526 static_assert(detail::optional_v<Tag>,
1527 "field_active can only be called with tag types for optional fields");
1534 template <
typename... TagsToChange>
1536 static_assert((detail::optional_v<TagsToChange> && ...),
1537 "set_field_status can only be called with tag types for optional fields");
1539 (std::get<tag_index_v<TagsToChange>>(
m_data).set_active(enabled,
size), ...);
1546 return accumulated.usage();
1555 std::ostringstream oss;
1557 <<
",sorted=" << std::boolalpha <<
m_sorted <<
"]: " << message;
1558 throw std::runtime_error(
std::move(oss).str());
1598 std::vector<non_owning_identifier_without_container>
m_indices{};
1609 std::tuple<detail::field_data<Tags, detail::field_impl_v<Tags>>...>
m_data{};
1618 template <
class Storage,
class... Tags>
1620 return data.memory_usage();
int cxx_demangle(const char *symbol, char **funcname, size_t *funcname_sz)
StorageMemoryUsage usage()
StorageMemoryUsage m_usage
void operator()(Tag const &tag, std::vector< typename Tag::type > const &vec, int field_index, int array_dim)
void operator()(detail::index_column_tag const &indices, std::vector< detail::index_column_tag::type > const &vec, int field_index, int array_dim)
static double active(void *v)
constexpr auto range(T &&iterable)
void move(Item *q1, Item *q2, Item *q3)
handle_interface< non_owning_identifier< storage > > handle
Non-owning handle to a Mechanism instance.
std::vector< void * > * defer_delete_storage
Defer deleting pointers to deallocated memory.
bool check_permutation_vector(Rng const &range, std::size_t size)
Check if the given range is a permutation of the first N integers.
void defer_delete(std::unique_ptr< T[]> data)
Storage for safe deletion of soa<...> containers.
constexpr FieldImplementation field_impl_v
VectorMemoryUsage compute_defer_delete_storage_size()
constexpr std::size_t index_of_type_v
constexpr bool has_num_variables_v
auto get_name(Tag const &tag, int field_index)
Get the nicest available name for the field_index-th instance of Tag.
size_t get_num_variables(T const &t)
constexpr bool are_types_unique_v
constexpr bool are_types_unique_v< T >
constexpr bool type_in_pack_v
constexpr bool optional_v
auto get_name_impl(Tag const &tag, int field_index, std::nullptr_t) -> decltype(static_cast< void >(tag.name(field_index)), std::string())
constexpr bool has_default_value_v
auto get_array_dimension(T const &t, std::nullptr_t) -> decltype(t.array_dimension(), 0)
cache::ModelMemoryUsage memory_usage(const std::optional< neuron::cache::Model > &model)
std::string to_string(const T &obj)
data_handle< T > transform(data_handle< T > handle, Transform type)
static double done(void *v)
Memory usage of a storage/soa container.
VectorMemoryUsage stable_identifiers
The memory usage for the stable identifiers in a soa.
VectorMemoryUsage heavy_data
The memory usage of the heavy data in a soa.
Size and capacity in bytes.
size_t size
Number of bytes used.
size_t capacity
Number of bytes allocated.
Stable handle to a generic value.
Tag const & tag() const
Return a reference to the tag instance.
Callable for_each_vector(Callable callable) const
Invoke the given callable for each vector.
int const * array_dims() const
Return a pointer to an array of array dimensions for this tag.
typename Tag::type data_type
std::vector< std::vector< data_type > > m_storage
Storage for the data associated with Tag.
std::vector< int > m_array_dim_prefix_sums
Prefix sum over array dimensions for the data associated with Tag.
std::vector< int > m_array_dims
Array dimensions of the data associated with Tag.
void update_data_ptr_storage()
int const * array_dim_prefix_sums() const
Return a pointer to an array of the prefix sum of array dimensions for this tag.
Tag m_tag
Tag type instance.
std::unique_ptr< data_type *[]> m_data_ptrs
Storage where we maintain an up-to-date cache of .data() pointers from m_storage.
data_type *const * data_ptrs() const
Return a pointer to an array of data pointers for this tag.
int check_array_dim(int field_index, int array_index) const
field_index translate_legacy_index(int legacy_index) const
Callable for_each_vector(Callable callable)
Invoke the given callable for each vector.
void set_active(bool enable, std::size_t size)
std::vector< data_type > m_storage
Storage for the data associated with Tag.
Tag const & tag() const
Return a reference to the tag instance.
typename Tag::type data_type
std::unique_ptr< data_type *[]> m_data_ptr
Storage where we maintain an up-to-date cache of m_storage.data().
Callable for_each_vector(Callable callable)
Callable for_each_vector(Callable callable) const
int const * array_dim_prefix_sums() const
int m_array_dim
Array dimension of the data associated with Tag.
int const * array_dims() const
Tag m_tag
Tag type instance.
data_type *const * data_ptrs() const
std::size_t size() const override
std::string_view field() const override
std::string_view container() const override
Struct used to index SoAoS data, such as array range variables.
Non-template stable handle to a generic value.
bool holds() const
Check if this handle refers to the specific type.
T get() const
Explicit conversion to any T.
A non-owning permutation-stable identifier for an entry in a container.
A non-owning permutation-stable identifier for a entry in a container.
An owning permutation-stable identifier for a entry in a container.
Utility for generating SOA data structures.
void set_unsorted_callback(std::function< void()> unsorted_callback)
Set the callback that is invoked when the container becomes unsorted.
owning_identifier< Storage > acquire_owning_identifier()
Create a new entry and return an identifier that owns it.
int const * get_array_dims() const
Get a pointer to an array holding the array dimensions of the fields associated with this tag.
void mark_as_unsorted_impl()
Set m_sorted = false and execute the callback.
bool is_sorted() const
Query if the underlying vectors are still "sorted".
soa & operator=(soa &&)=delete
soa is not move assignable
Callable for_each_vector(Callable callable) const
Apply the given function to const-qualified versions of all vectors.
soa(soa &&)=delete
soa is not movable
soa(soa const &)=delete
soa is not copiable
std::function< void()> m_unsorted_callback
Callback that is invoked when the container becomes unsorted.
bool field_active() const
Query whether the field associated with the given tag is active.
int const * get_array_dim_prefix_sums() const
Get a pointer to an array holding the prefix sum of array dimensions for this tag.
void erase(std::size_t i)
Remove the row from the container.
void decrease_frozen_count()
Flag that the storage is no longer frozen.
void increase_frozen_count()
Record that a state_token was copied.
Tag::type & get_field_instance(std::size_t offset, int field_index, int array_index=0)
Get the offset-th element of the field_index-th instance of the column named by Tag.
Tag::type & get(std::size_t offset)
Get the offset-th element of the column named by Tag.
frozen_token_type issue_frozen_token()
Create a token guaranteeing the container is in "frozen" state.
std::size_t size() const
Get the size of the container.
neuron::container::generic_data_handle find_data_handle(neuron::container::generic_data_handle input_handle) const
Return a permutation-stable handle if ptr is inside us.
Callable for_each_tag_vector_impl(Callable callable) const
std::vector< non_owning_identifier_without_container > m_indices
Pointers to identifiers that record the current physical row.
std::mutex m_mut
Mutex to protect m_frozen_count and m_sorted.
static constexpr bool has_tag_v
soa(Tags... tag_instances)
Construct with specific tag instances.
constexpr Tag const & get_tag() const
Get the instance of the given tag type.
non_owning_identifier< Storage > at(std::size_t offset) const
Get a non-owning identifier to the offset-th entry.
void mark_as_unsorted()
Tell the container it is no longer sorted.
void apply_reverse_permutation(Range permutation, frozen_token_type &sorted_token)
Permute the SoA-format data using an arbitrary range of integers.
Callable for_each_tag_vector_impl(Callable callable) const
void throw_error(std::string_view message) const
Throw an exception with a pretty prefix.
std::unique_ptr< utils::storage_info > find_container_info(void const *cont) const
Check if cont refers to a field in this container.
non_owning_identifier_without_container get_identifier(std::size_t offset) const
Get the offset-th identifier.
std::size_t m_frozen_count
Reference count for tokens guaranteeing the container is frozen.
Tag::type *const * get_data_ptrs() const
Get a pointer to a range of pointers that always point to the start of the contiguous storage.
soa & operator=(soa const &)=delete
soa is not copy assignable
void mark_as_sorted(frozen_token_type &write_token)
Tell the container it is sorted.
Callable for_each_tag_vector_impl(Callable callable)
static constexpr std::size_t tag_index_v
Callable for_each_tag_vector_impl(Callable callable)
soa()
Construct with default-constructed tag type instances.
size_t get_num_variables() const
std::tuple< detail::field_data< Tags, detail::field_impl_v< Tags > >... > m_data
Collection of data columns.
frozen_token_type apply_reverse_permutation(Arg &&permutation)
Permute the SoA-format data using an arbitrary range of integers.
Tag::type const & get(std::size_t offset) const
Get the offset-th element of the column named by Tag.
bool empty() const
Test if the container is empty.
Tag::type const & get_field_instance(std::size_t offset, int field_index, int array_index=0) const
Get the offset-th element of the field_index-th instance of the column named by Tag.
bool is_storage_pointer(typename Tag::type const *ptr) const
Query whether the given pointer-to-vector is the one associated to Tag.
data_handle< typename Tag::type > get_handle(non_owning_identifier_without_container id, int array_index=0) const
Get a handle to the given element of the column named by Tag.
bool m_sorted
Flag for issue_frozen_token(), mark_as_unsorted() and is_sorted().
int get_array_dims(int field_index) const
field_index translate_legacy_index(int legacy_index) const
void set_field_status(bool enabled)
Enable/disable the fields associated with the given tags.
StorageMemoryUsage memory_usage() const
data_handle< typename Tag::type > get_field_instance_handle(non_owning_identifier_without_container id, int field_index, int array_index=0) const
Get a handle to the given element of the field_index-th column named by Tag.
Callable for_each_vector(Callable callable)
Apply the given function to non-const versions of all vectors.
Token whose lifetime manages the frozen state of a container.
~state_token()
Destroy a token, decrementing the frozen count of the underlying container.
constexpr state_token(Container &container)
constexpr state_token & operator=(state_token const &)=delete
Copy assignment.
constexpr state_token(state_token const &other)
Copy a token, incrementing the frozen count of the underlying container.
Interface for obtaining information about model data containers.