14 static_assert(
sizeof(TextSlice) == 2 *
sizeof(uint64_t));
22 static_assert(
sizeof(DynamicCell) == 3 *
sizeof(uint64_t));
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;
36 "CompositeLayout: cannot add field '{}' after finalization",
name);
41 "CompositeLayout: field name cannot be empty");
46 "CompositeLayout: duplicate field '{}'",
name);
50 m_fields.push_back({ .name = std::move(
name), .type =
type, .offset_bytes = 0, .size_bytes = size_bytes });
56 for (
size_t i = 0; i < m_fields.size(); ++i) {
69 Journal::error<std::invalid_argument>(
72 std::source_location::current(),
73 "CompositeLayout requires at least one field");
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);
87 field.offset_bytes =
offset;
88 offset += field.size_bytes;
96 : m_layout(
std::move(layout))
103 const auto&
rows = std::get<std::vector<uint8_t>>(
m_rows);
117 auto&
rows = std::get<std::vector<uint8_t>>(
m_rows);
121 Journal::error<std::length_error>(
124 std::source_location::current(),
125 "CompositeArray exceeds addressable size at element {}",
index);
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;
137 return (
rows[byte_index] &
static_cast<uint8_t
>(1U << (field_index % 8))) != 0;
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));
147 rows[byte_index] |= mask;
149 rows[byte_index] &=
static_cast<uint8_t
>(~mask);
154 size_t index, std::string_view field_name, std::type_index
type)
const
158 "CompositeArray: element {} is out of range for {} elements",
index,
size());
165 "CompositeArray: field '{}' does not exist", field_name);
170 if (field.type !=
type) {
172 "CompositeArray: field '{}' expects type '{}', received '{}'",
173 field_name, field.type.name(),
type.name());
181 size_t index, std::string_view field_name)
const
189 const std::type_index expected = declared
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);
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())
209 text.insert(text.end(), value_copy.begin(), value_copy.end());
211 auto&
rows = std::get<std::vector<uint8_t>>(
m_rows);
214 const DynamicCell cell { text_kind,
slice.offset,
slice.length };
215 std::memcpy(destination, &cell,
sizeof(cell));
217 std::memcpy(destination, &
slice,
sizeof(
slice));
230 const DynamicCell cell { null_kind, 0, 0 };
231 auto&
rows = std::get<std::vector<uint8_t>>(
m_rows);
242 DynamicCell cell { integer_kind, 0, 0 };
243 std::memcpy(&cell.payload, &
value,
sizeof(
value));
245 auto&
rows = std::get<std::vector<uint8_t>>(
m_rows);
256 DynamicCell cell { real_kind, 0, 0 };
257 std::memcpy(&cell.payload, &
value,
sizeof(
value));
259 auto&
rows = std::get<std::vector<uint8_t>>(
m_rows);
268 const std::type_index expected = declared
275 auto& blob = std::get<std::vector<uint8_t>>(
m_blob);
276 if (
value.size() > blob.max_size() - blob.size())
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());
283 auto&
rows = std::get<std::vector<uint8_t>>(
m_rows);
286 const DynamicCell cell { blob_kind,
slice.offset,
slice.length };
287 std::memcpy(destination, &cell,
sizeof(cell));
289 std::memcpy(destination, &
slice,
sizeof(
slice));
299 "CompositeArray: element {} is out of range for {} elements",
index,
size());
307 "CompositeArray: field '{}' does not exist", field_name);
317 const auto field_index = m_layout->find_field(field_name);
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;
324 return (
rows[byte_index] &
static_cast<uint8_t
>(1U << (*field_index % 8))) != 0;
332std::optional<std::string_view>
Composite::text(std::string_view field_name)
const noexcept
334 const auto field_index = m_layout->
find_field(field_name);
335 if (!field_index || !has(field_name))
338 const auto& field = m_layout->fields()[*field_index];
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;
347 std::memcpy(&cell, source,
sizeof(cell));
348 if (cell.kind != text_kind)
350 slice = { cell.payload, cell.length };
352 std::memcpy(&slice, source,
sizeof(slice));
355 const auto& text = std::get<std::vector<uint8_t>>(*m_text);
356 if (slice.offset > text.size() || slice.length > text.size() - slice.offset)
359 if (slice.length == 0)
360 return std::string_view {};
362 return std::string_view(
363 reinterpret_cast<const char*
>(text.data() + slice.offset),
364 static_cast<size_t>(slice.length));
367std::optional<std::span<const uint8_t>>
Composite::blob(std::string_view field_name)
const noexcept
369 const auto field_index = m_layout->find_field(field_name);
370 if (!field_index || !has(field_name))
372 const auto& field = m_layout->fields()[*field_index];
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;
381 std::memcpy(&cell, source,
sizeof(cell));
382 if (cell.kind != blob_kind)
384 slice = { cell.payload, cell.length };
386 std::memcpy(&slice, source,
sizeof(slice));
388 const auto& blob = std::get<std::vector<uint8_t>>(*m_blob);
389 if (slice.offset > blob.size() || slice.length > blob.size() - slice.offset)
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));
398 const auto field_index = m_layout->find_field(field_name);
399 if (!field_index || !has(field_name)
403 const auto&
rows = std::get<std::vector<uint8_t>>(*m_rows);
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)
409 if (cell.kind == integer_kind) {
411 std::memcpy(&result, &cell.payload,
sizeof(result));
414 if (cell.kind == real_kind) {
416 std::memcpy(&result, &cell.payload,
sizeof(result));
419 if (cell.kind == text_kind) {
420 if (
auto result = text(field_name))
423 if (cell.kind == blob_kind) {
424 if (
auto result = blob(field_name))
#define MF_WARN(comp, ctx,...)
vk::PhysicalDeviceType type
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.
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.
bool add_field_impl(std::string name, std::type_index type, size_t size_bytes)
const std::vector< CompositeField > & fields() const noexcept
Declared fields in element order, with finalized byte offsets.
std::vector< CompositeField > m_fields
std::optional< size_t > find_field(std::string_view name) const noexcept
Find a field index by name, or std::nullopt if absent.
Shared field description and byte layout for a CompositeArray.
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.