MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Composite.cpp
Go to the documentation of this file.
1#include "Composite.hpp"
2
4
5namespace MayaFlux::Kakshya {
6
7namespace {
8
9 struct TextSlice {
10 uint64_t offset {};
11 uint64_t length {};
12 };
13
14 static_assert(sizeof(TextSlice) == 2 * sizeof(uint64_t));
15
16 struct DynamicCell {
17 uint64_t kind {};
18 uint64_t payload {};
19 uint64_t length {};
20 };
21
22 static_assert(sizeof(DynamicCell) == 3 * sizeof(uint64_t));
23
24 constexpr uint64_t null_kind = 0;
25 constexpr uint64_t integer_kind = 1;
26 constexpr uint64_t real_kind = 2;
27 constexpr uint64_t text_kind = 3;
28 constexpr uint64_t blob_kind = 4;
29
30}
31
32bool CompositeLayout::add_field_impl(std::string name, std::type_index type, size_t size_bytes)
33{
34 if (m_finalized) {
36 "CompositeLayout: cannot add field '{}' after finalization", name);
37 return false;
38 }
39 if (name.empty()) {
41 "CompositeLayout: field name cannot be empty");
42 return false;
43 }
44 if (find_field(name)) {
46 "CompositeLayout: duplicate field '{}'", name);
47 return false;
48 }
49
50 m_fields.push_back({ .name = std::move(name), .type = type, .offset_bytes = 0, .size_bytes = size_bytes });
51 return true;
52}
53
54std::optional<size_t> CompositeLayout::find_field(std::string_view name) const noexcept
55{
56 for (size_t i = 0; i < m_fields.size(); ++i) {
57 if (m_fields[i].name == name)
58 return i;
59 }
60 return std::nullopt;
61}
62
64{
65 if (m_finalized)
66 return;
67
68 if (m_fields.empty()) {
69 Journal::error<std::invalid_argument>(
72 std::source_location::current(),
73 "CompositeLayout requires at least one field");
74 }
75
76 m_presence_bytes = m_fields.size() / 8 + (m_fields.size() % 8 != 0);
77 size_t offset = m_presence_bytes;
78
79 for (auto& field : m_fields) {
80 if (field.size_bytes > std::numeric_limits<size_t>::max() - offset) {
81 Journal::error<std::length_error>(
84 std::source_location::current(),
85 "CompositeLayout exceeds addressable size at field '{}'", field.name);
86 }
87 field.offset_bytes = offset;
88 offset += field.size_bytes;
89 }
90
92 m_finalized = true;
93}
94
96 : m_layout(std::move(layout))
97{
99}
100
101size_t CompositeArray::size() const noexcept
102{
103 const auto& rows = std::get<std::vector<uint8_t>>(m_rows);
104 return rows.size() / m_layout.stride_bytes();
105}
106
107std::optional<Composite> CompositeArray::at(size_t index) const noexcept
108{
109 if (index >= size())
110 return std::nullopt;
111
112 return Composite(m_layout, m_rows, m_text, m_blob, index);
113}
114
116{
117 auto& rows = std::get<std::vector<uint8_t>>(m_rows);
118 const size_t index = size();
119
120 if (m_layout.stride_bytes() > rows.max_size() - rows.size()) {
121 Journal::error<std::length_error>(
124 std::source_location::current(),
125 "CompositeArray exceeds addressable size at element {}", index);
126 }
127
128 rows.resize(rows.size() + m_layout.stride_bytes(), 0);
129 return index;
130}
131
132bool CompositeArray::is_present(size_t index, size_t field_index) const noexcept
133{
134 const auto& rows = std::get<std::vector<uint8_t>>(m_rows);
135 const size_t byte_index = index * m_layout.stride_bytes() + field_index / 8;
136
137 return (rows[byte_index] & static_cast<uint8_t>(1U << (field_index % 8))) != 0;
138}
139
140void CompositeArray::set_present(size_t index, size_t field_index, bool present) noexcept
141{
142 auto& rows = std::get<std::vector<uint8_t>>(m_rows);
143 const size_t byte_index = index * m_layout.stride_bytes() + field_index / 8;
144 const auto mask = static_cast<uint8_t>(1U << (field_index % 8));
145
146 if (present) {
147 rows[byte_index] |= mask;
148 } else {
149 rows[byte_index] &= static_cast<uint8_t>(~mask);
150 }
151}
152
153std::optional<size_t> CompositeArray::validate_write(
154 size_t index, std::string_view field_name, std::type_index type) const
155{
156 if (index >= size()) {
158 "CompositeArray: element {} is out of range for {} elements", index, size());
159 return std::nullopt;
160 }
161
162 const auto field_index = m_layout.find_field(field_name);
163 if (!field_index) {
165 "CompositeArray: field '{}' does not exist", field_name);
166 return std::nullopt;
167 }
168
169 const auto& field = m_layout.fields()[*field_index];
170 if (field.type != type) {
172 "CompositeArray: field '{}' expects type '{}', received '{}'",
173 field_name, field.type.name(), type.name());
174 return std::nullopt;
175 }
176
177 return field_index;
178}
179
181 size_t index, std::string_view field_name) const
182{
183 return validate_write(index, field_name, typeid(detail::CompositeDynamic));
184}
185
186bool CompositeArray::set_text(size_t index, std::string_view field_name, std::string_view value)
187{
188 const auto declared = m_layout.find_field(field_name);
189 const std::type_index expected = declared
190 && m_layout.fields()[*declared].type == typeid(detail::CompositeDynamic)
191 ? typeid(detail::CompositeDynamic) : typeid(std::string);
192 const auto field_index = validate_write(index, field_name, expected);
193 if (!field_index)
194 return false;
195
196 const auto& field = m_layout.fields()[*field_index];
197 auto& text = std::get<std::vector<uint8_t>>(m_text);
198 if (value.size() > text.max_size() - text.size()) {
200 "CompositeArray: text value for field '{}' exceeds addressable size", field_name);
201 return false;
202 }
203
204 const std::string value_copy(value);
205 const TextSlice slice {
206 .offset = static_cast<uint64_t>(text.size()),
207 .length = static_cast<uint64_t>(value_copy.size())
208 };
209 text.insert(text.end(), value_copy.begin(), value_copy.end());
210
211 auto& rows = std::get<std::vector<uint8_t>>(m_rows);
212 auto* destination = rows.data() + index * m_layout.stride_bytes() + field.offset_bytes;
213 if (field.type == typeid(detail::CompositeDynamic)) {
214 const DynamicCell cell { text_kind, slice.offset, slice.length };
215 std::memcpy(destination, &cell, sizeof(cell));
216 } else {
217 std::memcpy(destination, &slice, sizeof(slice));
218 }
219
220 set_present(index, *field_index, true);
221 return true;
222}
223
224bool CompositeArray::set_null(size_t index, std::string_view field_name)
225{
226 const auto field_index = validate_dynamic_write(index, field_name);
227 if (!field_index)
228 return false;
229 const auto& field = m_layout.fields()[*field_index];
230 const DynamicCell cell { null_kind, 0, 0 };
231 auto& rows = std::get<std::vector<uint8_t>>(m_rows);
232 std::memcpy(rows.data() + index * m_layout.stride_bytes() + field.offset_bytes, &cell, sizeof(cell));
233 set_present(index, *field_index, true);
234 return true;
235}
236
237bool CompositeArray::set_integer(size_t index, std::string_view field_name, int64_t value)
238{
239 const auto field_index = validate_dynamic_write(index, field_name);
240 if (!field_index)
241 return false;
242 DynamicCell cell { integer_kind, 0, 0 };
243 std::memcpy(&cell.payload, &value, sizeof(value));
244 const auto& field = m_layout.fields()[*field_index];
245 auto& rows = std::get<std::vector<uint8_t>>(m_rows);
246 std::memcpy(rows.data() + index * m_layout.stride_bytes() + field.offset_bytes, &cell, sizeof(cell));
247 set_present(index, *field_index, true);
248 return true;
249}
250
251bool CompositeArray::set_real(size_t index, std::string_view field_name, double value)
252{
253 const auto field_index = validate_dynamic_write(index, field_name);
254 if (!field_index)
255 return false;
256 DynamicCell cell { real_kind, 0, 0 };
257 std::memcpy(&cell.payload, &value, sizeof(value));
258 const auto& field = m_layout.fields()[*field_index];
259 auto& rows = std::get<std::vector<uint8_t>>(m_rows);
260 std::memcpy(rows.data() + index * m_layout.stride_bytes() + field.offset_bytes, &cell, sizeof(cell));
261 set_present(index, *field_index, true);
262 return true;
263}
264
265bool CompositeArray::set_blob(size_t index, std::string_view field_name, std::span<const uint8_t> value)
266{
267 const auto declared = m_layout.find_field(field_name);
268 const std::type_index expected = declared
269 && m_layout.fields()[*declared].type == typeid(detail::CompositeDynamic)
270 ? typeid(detail::CompositeDynamic) : typeid(std::vector<uint8_t>);
271 const auto field_index = validate_write(index, field_name, expected);
272 if (!field_index)
273 return false;
274
275 auto& blob = std::get<std::vector<uint8_t>>(m_blob);
276 if (value.size() > blob.max_size() - blob.size())
277 return false;
278 const std::vector<uint8_t> value_copy(value.begin(), value.end());
279 const TextSlice slice { static_cast<uint64_t>(blob.size()), static_cast<uint64_t>(value_copy.size()) };
280 blob.insert(blob.end(), value_copy.begin(), value_copy.end());
281
282 const auto& field = m_layout.fields()[*field_index];
283 auto& rows = std::get<std::vector<uint8_t>>(m_rows);
284 auto* destination = rows.data() + index * m_layout.stride_bytes() + field.offset_bytes;
285 if (field.type == typeid(detail::CompositeDynamic)) {
286 const DynamicCell cell { blob_kind, slice.offset, slice.length };
287 std::memcpy(destination, &cell, sizeof(cell));
288 } else {
289 std::memcpy(destination, &slice, sizeof(slice));
290 }
291 set_present(index, *field_index, true);
292 return true;
293}
294
295bool CompositeArray::clear(size_t index, std::string_view field_name)
296{
297 if (index >= size()) {
299 "CompositeArray: element {} is out of range for {} elements", index, size());
300 return false;
301 }
302
303 const auto field_index = m_layout.find_field(field_name);
304
305 if (!field_index) {
307 "CompositeArray: field '{}' does not exist", field_name);
308 return false;
309 }
310
311 set_present(index, *field_index, false);
312 return true;
313}
314
315bool Composite::has(std::string_view field_name) const noexcept
316{
317 const auto field_index = m_layout->find_field(field_name);
318 if (!field_index)
319 return false;
320
321 const auto& rows = std::get<std::vector<uint8_t>>(*m_rows);
322 const size_t byte_index = m_index * m_layout->stride_bytes() + *field_index / 8;
323
324 return (rows[byte_index] & static_cast<uint8_t>(1U << (*field_index % 8))) != 0;
325}
326
327const CompositeLayout& Composite::layout() const noexcept
328{
329 return *m_layout;
330}
331
332std::optional<std::string_view> Composite::text(std::string_view field_name) const noexcept
333{
334 const auto field_index = m_layout->find_field(field_name);
335 if (!field_index || !has(field_name))
336 return std::nullopt;
337
338 const auto& field = m_layout->fields()[*field_index];
339 if (field.type != typeid(std::string) && field.type != typeid(detail::CompositeDynamic))
340 return std::nullopt;
341
342 const auto& rows = std::get<std::vector<uint8_t>>(*m_rows);
343 const auto* source = rows.data() + m_index * m_layout->stride_bytes() + field.offset_bytes;
344 TextSlice slice;
345 if (field.type == typeid(detail::CompositeDynamic)) {
346 DynamicCell cell;
347 std::memcpy(&cell, source, sizeof(cell));
348 if (cell.kind != text_kind)
349 return std::nullopt;
350 slice = { cell.payload, cell.length };
351 } else {
352 std::memcpy(&slice, source, sizeof(slice));
353 }
354
355 const auto& text = std::get<std::vector<uint8_t>>(*m_text);
356 if (slice.offset > text.size() || slice.length > text.size() - slice.offset)
357 return std::nullopt;
358
359 if (slice.length == 0)
360 return std::string_view {};
361
362 return std::string_view(
363 reinterpret_cast<const char*>(text.data() + slice.offset),
364 static_cast<size_t>(slice.length));
365}
366
367std::optional<std::span<const uint8_t>> Composite::blob(std::string_view field_name) const noexcept
368{
369 const auto field_index = m_layout->find_field(field_name);
370 if (!field_index || !has(field_name))
371 return std::nullopt;
372 const auto& field = m_layout->fields()[*field_index];
373 if (field.type != typeid(std::vector<uint8_t>) && field.type != typeid(detail::CompositeDynamic))
374 return std::nullopt;
375
376 const auto& rows = std::get<std::vector<uint8_t>>(*m_rows);
377 const auto* source = rows.data() + m_index * m_layout->stride_bytes() + field.offset_bytes;
378 TextSlice slice;
379 if (field.type == typeid(detail::CompositeDynamic)) {
380 DynamicCell cell;
381 std::memcpy(&cell, source, sizeof(cell));
382 if (cell.kind != blob_kind)
383 return std::nullopt;
384 slice = { cell.payload, cell.length };
385 } else {
386 std::memcpy(&slice, source, sizeof(slice));
387 }
388 const auto& blob = std::get<std::vector<uint8_t>>(*m_blob);
389 if (slice.offset > blob.size() || slice.length > blob.size() - slice.offset)
390 return std::nullopt;
391 if (slice.length == 0)
392 return std::span<const uint8_t> {};
393 return std::span<const uint8_t>(blob.data() + slice.offset, static_cast<size_t>(slice.length));
394}
395
396std::optional<CompositeValue> Composite::value(std::string_view field_name) const noexcept
397{
398 const auto field_index = m_layout->find_field(field_name);
399 if (!field_index || !has(field_name)
400 || m_layout->fields()[*field_index].type != typeid(detail::CompositeDynamic))
401 return std::nullopt;
402
403 const auto& rows = std::get<std::vector<uint8_t>>(*m_rows);
404 DynamicCell cell;
405 std::memcpy(&cell, rows.data() + m_index * m_layout->stride_bytes()
406 + m_layout->fields()[*field_index].offset_bytes, sizeof(cell));
407 if (cell.kind == null_kind)
408 return CompositeValue { std::monostate {} };
409 if (cell.kind == integer_kind) {
410 int64_t result;
411 std::memcpy(&result, &cell.payload, sizeof(result));
412 return CompositeValue { result };
413 }
414 if (cell.kind == real_kind) {
415 double result;
416 std::memcpy(&result, &cell.payload, sizeof(result));
417 return CompositeValue { result };
418 }
419 if (cell.kind == text_kind) {
420 if (auto result = text(field_name))
421 return CompositeValue { *result };
422 }
423 if (cell.kind == blob_kind) {
424 if (auto result = blob(field_name))
425 return CompositeValue { *result };
426 }
427 return std::nullopt;
428}
429
430}
#define MF_WARN(comp, ctx,...)
uint64_t length
Definition Composite.cpp:11
uint64_t offset
Definition Composite.cpp:10
uint64_t payload
Definition Composite.cpp:18
bool size
uint32_t rows
AttributeKind kind
glm::vec3 value
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
std::string name
Definition VKDevice.cpp:123
uint32_t index
Definition VKDevice.cpp:122
bool set_integer(size_t index, std::string_view field_name, int64_t value)
size_t size() const noexcept
Number of packed elements.
bool clear(size_t index, std::string_view field_name)
Mark a field absent without removing its stored bytes.
bool set_null(size_t index, std::string_view field_name)
Write a present NULL to a dynamic field.
bool set_blob(size_t index, std::string_view field_name, std::span< const uint8_t > value)
Write binary bytes to a fixed or dynamic BLOB field.
std::optional< Composite > at(size_t index) const noexcept
Borrow an element by index.
void set_present(size_t index, size_t field_index, bool present) noexcept
CompositeArray(CompositeLayout layout)
Finalize a layout and construct an empty array.
Definition Composite.cpp:95
std::optional< CompositeSlice > slice(size_t start, size_t count) const
Borrow consecutive elements without copying.
std::optional< size_t > validate_write(size_t index, std::string_view field_name, std::type_index type) const
bool set_real(size_t index, std::string_view field_name, double value)
size_t append()
Append one zero-initialized element with no fields present.
bool is_present(size_t index, size_t field_index) const noexcept
std::optional< size_t > validate_dynamic_write(size_t index, std::string_view field_name) const
bool set_text(size_t index, std::string_view field_name, std::string_view value)
Write UTF-8 bytes to a std::string field.
size_t stride_bytes() const noexcept
Bytes per packed element after finalization.
Definition Composite.hpp:86
bool add_field_impl(std::string name, std::type_index type, size_t size_bytes)
Definition Composite.cpp:32
const std::vector< CompositeField > & fields() const noexcept
Declared fields in element order, with finalized byte offsets.
Definition Composite.hpp:82
std::vector< CompositeField > m_fields
Definition Composite.hpp:93
std::optional< size_t > find_field(std::string_view name) const noexcept
Find a field index by name, or std::nullopt if absent.
Definition Composite.cpp:54
Shared field description and byte layout for a CompositeArray.
Definition Composite.hpp:52
std::optional< std::string_view > text(std::string_view field_name) const noexcept
Read a present std::string field as borrowed UTF-8 bytes.
std::optional< CompositeValue > value(std::string_view field_name) const noexcept
Borrow a dynamic value; an absent field returns std::nullopt.
const CompositeLayout * m_layout
const CompositeLayout & layout() const noexcept
Layout used to interpret this element's packed bytes.
bool has(std::string_view field_name) const noexcept
Whether a named field exists and is present in this element.
std::optional< std::span< const uint8_t > > blob(std::string_view field_name) const noexcept
Borrow the bytes of a fixed or dynamic BLOB field.
Read view of one value in a CompositeArray.
@ Runtime
General runtime operations (default fallback)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
std::variant< std::monostate, int64_t, double, std::string_view, std::span< const uint8_t > > CompositeValue
Borrowed value of a present dynamic field; monostate represents NULL.
Definition Composite.hpp:29