The Sparta Modeling Framework
Loading...
Searching...
No Matches
BitBucket.hpp
1// <BitBucket.hpp> -*- C++ -*-
2
3#pragma once
4
6#include "sparta/utils/Utils.hpp"
7#include "simdb/apps/argos/EntryPoint.hpp"
8#include "simdb/Exceptions.hpp"
9#include <cstring>
10#include <vector>
11
12namespace sparta::collection {
13
21{
22public:
23 BitBucket(simdb::TinyStrings<>* tiny_strings, simdb::argos::EnumInspector* enum_inspector) :
24 tiny_strings_(tiny_strings),
25 enum_inspector_(enum_inspector)
26 {}
27
28 virtual ~BitBucket() = default;
29 virtual void clear() = 0;
30 virtual void writeTo(simdb::argos::EntryPoint* entry_point) = 0;
31
32 template <typename T>
33 void writeField(const T& val, uint32_t field_id) {
34 // Write bools as uint8_t
35 if constexpr (std::is_same_v<T, bool>) {
36 writeField(val ? uint8_t(1) : uint8_t(0), field_id);
37 }
38
39 // Write list-of-integer values as [count, elem0, elem1, ...].
40 else if constexpr (sparta::is_vector<T>::value) {
41 using value_type = typename T::value_type;
42 sparta_assert(val.size() <= 32u);
43 const uint8_t count = static_cast<uint8_t>(val.size());
44 writeField_(static_cast<const void*>(&count), sizeof(count), field_id);
45 if constexpr (std::is_integral_v<value_type> && !std::is_same_v<value_type, bool>) {
46 for (const auto& elem : val) {
47 writeField(elem, field_id);
48 }
49 } else {
50 using converted_t = simdb::type_traits::pod_convertible_t<value_type>;
51 static_assert(simdb::type_traits::is_pod_convertible_v<value_type> &&
52 std::is_integral_v<converted_t> && !std::is_same_v<converted_t, bool>,
53 "Argos vector collection only supports integer vectors or integer-like values.");
54 for (const auto& elem : val) {
55 writeField(static_cast<converted_t>(elem), field_id);
56 }
57 }
58 }
59
60 // Write strings as uint32_t via TinyStrings
61 else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char*>) {
62 writeField(tiny_strings_->getStringID(val), field_id);
63 }
64
65 // Write enums as their underlying integer type.
66 else if constexpr (std::is_enum_v<T>) {
67 enum_inspector_->inspect(val);
68 using underlying_t = std::underlying_type_t<T>;
69 const underlying_t enum_int = static_cast<underlying_t>(val);
70 writeField(enum_int, field_id);
71 }
72
73 // Write struct/class fields that provide exactly one cast-to-POD operator
74 else if constexpr (simdb::type_traits::is_pod_convertible_v<T> && !std::is_enum_v<T> &&
75 (!std::is_trivial_v<T> || !std::is_standard_layout_v<T>)) {
76 using converted_t = simdb::type_traits::pod_convertible_t<T>;
77 static_assert(std::is_trivial_v<converted_t> && std::is_standard_layout_v<converted_t>);
78 auto converted_val = static_cast<converted_t>(val);
79 writeField(converted_val, field_id);
80 }
81
82 // Write PODs (or TinyStrings uint32_t ID, or enums by their underlying type,
83 // or bools as uint8_t)
84 else if constexpr (std::is_trivial_v<T> && std::is_standard_layout_v<T>) {
85 writeField_(&val, sizeof(T), field_id);
86 }
87
88 // Invalid! We might not be able to get away with a static_assert here.
89 // The PEvent system collects std::pair's which work for PEvents, but
90 // are not valid for Argos. The switch is based on the presence of a
91 // BitBucket (Argos) or not (PEvents) and not something we can switch
92 // on with a constexpr. See the code in SpartaKeyPairs.hpp:
93 //
94 // bool finalizeCollection_(
95 // PairCache *& cache, const ValueType & tmp) {
96 //
97 // if(auto bit_bucket = this->getBitBucket_(false)) {
98 // *** ARGOS ***
99 // bit_bucket->writeField(tmp, id_);
100 // } else {
101 // *** PEVENTS ***
102 // ...
103 // }
104 // return false;
105 // }
106 else {
107 throw simdb::DBException("Invalid type! Must be a POD, enum, or string, not ")
108 << simdb::demangle_type<T>();
109 }
110 }
111
112 simdb::TinyStrings<>* getTinyStrings() const {
113 return tiny_strings_;
114 }
115
116 simdb::argos::EnumInspector* getEnumInspector() const {
117 return enum_inspector_;
118 }
119
120protected:
121 virtual void writeField_(const void* data, uint32_t bytes, uint32_t field_id) = 0;
122
123private:
124 simdb::TinyStrings<>* tiny_strings_ = nullptr;
125 simdb::argos::EnumInspector* enum_inspector_ = nullptr;
126};
127
128template <bool Sparse>
130
135{
136public:
137 using BitBucket::BitBucket;
138
139 void clear() override final {
140 buffer_.clear();
141 buffer_.reserve(bytes_per_pass_);
142 bytes_per_pass_ = 0;
143 }
144
146 void writeTo(simdb::argos::EntryPoint* entry_point) override final {
147 entry_point->setScalarValueBytes(std::move(buffer_));
148 clear();
149 }
150
152 void writeTo(std::vector<char> & dest) {
153 std::swap(dest, buffer_);
154 clear();
155 }
156
157private:
158 void writeField_(const void* data, uint32_t bytes, uint32_t) override final {
159 auto src = static_cast<const char*>(data);
160 buffer_.resize(bytes_per_pass_ + bytes);
161 auto dst = &buffer_[bytes_per_pass_];
162 memcpy(dst, src, bytes);
163 bytes_per_pass_ += bytes;
164 }
165
166 std::vector<char> buffer_;
167 size_t bytes_per_pass_ = 0;
168
169 template <bool Sparse>
170 friend class IterableCollectorBitBucket;
171};
172
175template <>
177{
178public:
179 IterableCollectorBitBucket(simdb::TinyStrings<>* tiny_strings, simdb::argos::EnumInspector* enum_inspector, size_t capacity)
180 : BitBucket(tiny_strings, enum_inspector)
181 , capacity_(capacity)
182 {
183 sparta_assert(capacity_ <= UINT16_MAX);
184 all_bin_idxs_.reserve(capacity_);
185 while (capacity--)
186 {
187 bin_buckets_.emplace_back(std::make_unique<CollectableBitBucket>(tiny_strings, enum_inspector));
188 }
189 }
190
191 void clear() override final {
192 active_bin_idx_.clearValid();
193 all_bin_idxs_.clear();
194 all_bin_idxs_.reserve(capacity_);
195 }
196
197 void setActiveBinIdx(uint32_t bin_idx) {
198 sparta_assert(bin_idx < capacity_);
199 active_bin_idx_ = static_cast<uint16_t>(bin_idx);
200 all_bin_idxs_.emplace_back(bin_idx);
201 }
202
203 void writeTo(simdb::argos::EntryPoint* entry_point) override final {
204 for (auto bin_idx : all_bin_idxs_) {
205 bin_buckets_.at(bin_idx)->writeTo(all_bin_bytes_[bin_idx]);
206 }
207
208 entry_point->setSparseContainerBinBytes(std::move(all_bin_bytes_));
209 clear();
210 }
211
212private:
213 void writeField_(const void* data, uint32_t bytes, uint32_t field_id) override final {
214 auto& bin_bucket = bin_buckets_.at(active_bin_idx_.getValue());
215 assert(bin_bucket);
216 bin_bucket->writeField_(data, bytes, field_id);
217 }
218
219 std::vector<std::unique_ptr<CollectableBitBucket>> bin_buckets_;
220 std::map<uint16_t, std::vector<char>> all_bin_bytes_;
221 utils::ValidValue<uint16_t> active_bin_idx_;
222 std::vector<uint16_t> all_bin_idxs_;
223 size_t capacity_ = 0;
224};
225
228template <>
230{
231public:
232 IterableCollectorBitBucket(simdb::TinyStrings<>* tiny_strings, simdb::argos::EnumInspector* enum_inspector, size_t capacity)
233 : BitBucket(tiny_strings, enum_inspector)
234 , capacity_(capacity)
235 {
236 all_bin_bytes_.reserve(capacity_);
237 while (capacity--)
238 {
239 bin_buckets_.emplace_back(std::make_unique<CollectableBitBucket>(tiny_strings, enum_inspector));
240 }
241 }
242
243 void clear() override final {
244 container_size_ = 0;
245 all_bin_bytes_.reserve(capacity_);
246 }
247
248 void setActiveBinIdx(uint32_t bin_idx) {
249 sparta_assert(bin_idx <= UINT16_MAX);
250 sparta_assert(bin_idx == container_size_);
251 ++container_size_;
252 }
253
254 void writeTo(simdb::argos::EntryPoint* entry_point) override final {
255 all_bin_bytes_.resize(container_size_);
256 for (size_t i = 0; i < container_size_; ++i) {
257 auto & bin_bucket = bin_buckets_.at(i);
258 auto & bin_buffer = all_bin_bytes_.at(i);
259 bin_bucket->writeTo(bin_buffer);
260 }
261
262 entry_point->setContigContainerBinBytes(std::move(all_bin_bytes_));
263 clear();
264 }
265
266private:
267 void writeField_(const void* data, uint32_t bytes, uint32_t field_id) override final {
268 if (SPARTA_EXPECT_FALSE(container_size_ == 0)) {
269 setActiveBinIdx(0);
270 }
271 auto& bin_bucket = bin_buckets_.at(container_size_ - 1);
272 bin_bucket->writeField_(data, bytes, field_id);
273 }
274
275 std::vector<std::unique_ptr<CollectableBitBucket>> bin_buckets_;
276 std::vector<std::vector<char>> all_bin_bytes_;
277 uint16_t container_size_ = 0;
278 size_t capacity_ = 0;
279};
280
281} // namespace sparta::collection
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
File that defines a ValidValue.
Base class for all collectable bit buckets (scalars, sparse/contig iterables). We use this class so t...
Definition BitBucket.hpp:21
BitBucket implementation for Collectable objects (whether "standalone" or inside an IterableCollector...
void writeTo(std::vector< char > &dest)
Called when inside an IterableCollector.
void writeTo(simdb::argos::EntryPoint *entry_point) override final
Called when using a standalone Collectable.
BitBucket implementation for sparse IterableCollectors.
Provides a wrapper around a value to ensure that the value is assigned.
Templated for determining if ValueType is std::vector for use in metaprogramming constructs....
Definition Utils.hpp:247