8#include <pixie/detail/serialization.h>
12#include <pixie/storage/aligned.h>
13#include <pixie/storage/read_only_view.h>
44 requires(std::same_as<Storage, AlignedStorage> ||
45 std::same_as<Storage, ReadOnlyStorageView>)
65 requires std::same_as<Storage, AlignedStorage>
67 inferred_width(values),
82 requires std::same_as<Storage, AlignedStorage>
94 return read_field(logical_words(), position, width_);
99 for (
size_type i = 0; i < output.size(); ++i) {
109 if constexpr (std::same_as<Storage, AlignedStorage>) {
110 return sizeof(*this) + storage_.allocated_bytes();
112 return sizeof(*this);
127 if (writer.
size_bytes() %
alignof(std::uint64_t) != 0) {
128 throw std::invalid_argument(
129 "Packed integer-vector serialization requires an aligned offset");
132 const Dimensions dimensions = checked_dimensions(size_, width_);
133 const std::size_t artifact_begin = writer.
size_bytes();
134 detail::write_magic(writer, kSerializationMagic);
136 writer.
write_u8(detail::kLittleEndianMarker);
137 writer.
write_u8(
static_cast<std::uint8_t
>(kValueDigits));
138 writer.
write_u8(
static_cast<std::uint8_t
>(width_));
143 const auto words = logical_words();
144 for (std::size_t i = 0; i < dimensions.word_count; ++i) {
145 std::uint64_t word = words[i];
146 if (i + 1 == dimensions.word_count &&
147 dimensions.bit_count % kWordBits != 0) {
148 word &= low_bits_mask(dimensions.bit_count % kWordBits);
152 writer.
patch_u64(size_position,
static_cast<std::uint64_t
>(
176 const std::size_t available_size = candidate.
remaining();
177 detail::require_magic(candidate, kSerializationMagic);
178 if (candidate.
read_u32() != kSerializationVersion ||
179 candidate.
read_u8() != detail::kLittleEndianMarker ||
180 candidate.
read_u8() != kValueDigits) {
181 throw std::invalid_argument(
182 "Incompatible serialized packed integer vector");
186 throw std::invalid_argument(
187 "Invalid serialized packed integer-vector width or reserved field");
189 const std::size_t artifact_size = detail::checked_artifact_size(
190 candidate.
read_u64(), kSerializationHeaderBytes, available_size);
191 const std::size_t count = candidate.
read_size();
192 const Dimensions dimensions = checked_dimensions(count,
width);
193 if ((count == 0 &&
width != 0) ||
194 dimensions.byte_count != artifact_size - kSerializationHeaderBytes) {
195 throw std::invalid_argument(
196 "Serialized packed integer vector has inconsistent dimensions");
202 bool has_nonzero_word =
false;
203 for (std::size_t i = 0; i < dimensions.word_count; ++i) {
204 const std::uint64_t word = validation_reader.
read_u64();
205 has_nonzero_word = has_nonzero_word || word != 0;
206 if (i + 1 == dimensions.word_count &&
207 dimensions.bit_count % kWordBits != 0 &&
208 (word & ~low_bits_mask(dimensions.bit_count % kWordBits)) != 0) {
209 throw std::invalid_argument(
210 "Serialized packed integer vector has non-zero unused bits");
213 if (count != 0 &&
width != 0 && !has_nonzero_word) {
214 throw std::invalid_argument(
215 "Serialized all-zero integer vector has noncanonical width");
220 if constexpr (std::same_as<Storage, AlignedStorage>) {
221 storage =
AlignedStorage(word_aligned_bit_count(dimensions.word_count));
222 auto words = storage.writable_words64();
223 for (std::size_t i = 0; i < dimensions.word_count; ++i) {
227 if constexpr (std::endian::native != std::endian::little) {
228 throw std::invalid_argument(
229 "Packed integer-vector views require little-endian word order");
231 const auto bytes = payload.
read_bytes(dimensions.byte_count);
232 if (dimensions.byte_count != 0 &&
233 reinterpret_cast<std::uintptr_t
>(bytes.data()) %
234 alignof(std::uint64_t) !=
236 throw std::invalid_argument(
237 "Serialized packed integer-vector payload is not word aligned");
241 if (!payload.
empty()) {
242 throw std::invalid_argument(
243 "Serialized packed integer vector has trailing payload bytes");
254 std::size_t bit_count;
255 std::size_t word_count;
256 std::size_t byte_count;
258 struct ExplicitTag {};
261 inline static constexpr std::array<std::uint8_t, 8> kSerializationMagic = {
262 'P',
'X',
'I',
'N',
'T',
'V',
'E',
'C'};
263 static constexpr std::uint32_t kSerializationVersion = 1;
264 static constexpr std::size_t kSerializationHeaderBytes = 32;
265 static constexpr std::size_t kValueDigits =
266 std::numeric_limits<value_type>::digits;
267 static constexpr size_type kWordBits =
268 std::numeric_limits<std::uint64_t>::digits;
273 requires std::same_as<Storage, AlignedStorage>
274 : size_(values.size()) {
275 if (width > kValueDigits) {
276 throw std::invalid_argument(
"Packed integer-vector width is too large");
278 bool all_zero =
true;
279 for (
const value_type value : values) {
280 all_zero = all_zero && value == 0;
281 if (required_width(value) > width) {
282 throw std::invalid_argument(
283 "Packed integer-vector value does not fit the width");
286 width_ = all_zero ? 0 : width;
287 const Dimensions dimensions = checked_dimensions(size_, width_);
288 storage_ =
AlignedStorage(word_aligned_bit_count(dimensions.word_count));
289 auto words = storage_.writable_words64();
290 for (std::size_t i = 0; i < values.size(); ++i) {
291 write_field(words, i, width_, values[i]);
299 : storage_(std::move(storage)), size_(size), width_(width) {}
301 static size_type required_width(value_type value) {
302 return static_cast<size_type
>(std::bit_width(value));
305 static constexpr std::uint64_t low_bits_mask(size_type count) {
306 return count >= kWordBits ? std::numeric_limits<std::uint64_t>::max()
307 : (std::uint64_t{1} << count) - 1;
310 static size_type inferred_width(std::span<const value_type> values) {
312 for (
const value_type value : values) {
313 width = std::max(width, required_width(value));
318 static Dimensions checked_dimensions(size_type count, size_type width) {
319 if (width > kValueDigits) {
320 throw std::invalid_argument(
"Packed integer-vector width is too large");
322 if (width != 0 && count > std::numeric_limits<size_type>::max() / width) {
323 throw std::length_error(
"Packed integer-vector bit count is too large");
325 const size_type bit_count = count * width;
326 const size_type word_count =
327 bit_count == 0 ? 0 : 1 + (bit_count - 1) / kWordBits;
329 std::numeric_limits<size_type>::max() /
sizeof(std::uint64_t)) {
330 throw std::length_error(
"Packed integer-vector payload is too large");
332 return {bit_count, word_count, word_count *
sizeof(std::uint64_t)};
335 static size_type word_aligned_bit_count(size_type word_count) {
336 if (word_count > std::numeric_limits<size_type>::max() / kWordBits) {
337 throw std::length_error(
"Packed integer-vector storage is too large");
339 return word_count * kWordBits;
342 std::span<const std::uint64_t> logical_words()
const {
346 const Dimensions dimensions = checked_dimensions(size_, width_);
347 return storage_.as_words64().first(dimensions.word_count);
350 static value_type read_field(std::span<const std::uint64_t> words,
356 if (width == kWordBits) {
357 return static_cast<value_type
>(words[position]);
359 const size_type bit_position = position * width;
360 const size_type word = bit_position / kWordBits;
361 const size_type offset = bit_position % kWordBits;
362 std::uint64_t value = words[word] >> offset;
363 if (offset + width > kWordBits) {
364 value |= words[word + 1] << (kWordBits - offset);
366 return static_cast<value_type
>(value & low_bits_mask(width));
369 static void write_field(std::span<std::uint64_t> words,
376 if (width == kWordBits) {
377 words[position] =
static_cast<std::uint64_t
>(value);
380 const size_type bit_position = position * width;
381 const size_type word = bit_position / kWordBits;
382 const size_type offset = bit_position % kWordBits;
383 words[word] |=
static_cast<std::uint64_t
>(value) << offset;
384 if (offset + width > kWordBits) {
386 static_cast<std::uint64_t
>(value) >> (kWordBits - offset);
392 size_type width_ = 0;
396template <IntegerVectorValue Value = std::u
int64_t>
403template <IntegerVectorValue Value = std::u
int64_t>
Owning storage with a logical byte size and 64-byte-aligned backing.
Definition aligned.h:41
Immutable runtime-width packed integer vector over configurable storage.
Definition packed.h:48
value_type value_at_impl(size_type position) const
Read one valid zero-based position without bounds checking.
Definition packed.h:93
size_type width() const
Definition packed.h:87
void serialize_impl(BinaryWriter &writer) const
Write a version-1 canonical little-endian packed-vector artifact.
Definition packed.h:126
BasicPackedIntegerVector(std::span< const value_type > values)
Pack values using their minimum required width.
Definition packed.h:64
size_type memory_usage_bytes_impl() const
Return inline bytes plus owned storage capacity.
Definition packed.h:108
Value value_type
Stored unsigned integer type.
Definition packed.h:51
BasicPackedIntegerVector()=default
Construct an empty width-zero vector.
size_type size_impl() const
Return the logical number of elements.
Definition packed.h:90
BasicPackedIntegerVector(std::span< const value_type > values, size_type width)
Pack values after validating an explicit maximum width.
Definition packed.h:81
static BasicPackedIntegerVector deserialize_impl(BinaryReader &reader, DeserializationValidation validation=DeserializationValidation::kQuick)
Restore one packed-vector artifact and advance reader on success.
Definition packed.h:171
void copy_to_impl(size_type begin, std::span< value_type > output) const
Copy a range already validated by the public facade.
Definition packed.h:98
std::size_t size_type
Type used for element counts, widths, and positions.
Definition packed.h:54
Bounds-checked reader for canonical little-endian binary data.
Definition serialization.h:526
std::size_t remaining() const noexcept
Return the number of unconsumed bytes.
Definition serialization.h:544
std::span< const std::byte > remaining_bytes() const noexcept
Return all currently unconsumed bytes.
Definition serialization.h:547
std::uint8_t read_u8()
Read an unsigned eight-bit integer.
Definition serialization.h:552
BinaryReader read_subreader(std::size_t count)
Read a bounded region as an independent child reader.
Definition serialization.h:615
std::span< const std::byte > read_bytes(std::size_t count)
Read exactly count uninterpreted bytes.
Definition serialization.h:604
std::uint64_t read_u64()
Read an unsigned little-endian 64-bit integer.
Definition serialization.h:561
std::uint32_t read_u32()
Read an unsigned little-endian 32-bit integer.
Definition serialization.h:558
bool empty() const noexcept
Return whether all input bytes have been consumed.
Definition serialization.h:532
std::size_t read_size()
Read an unsigned 64-bit size and convert it to size_t.
Definition serialization.h:587
Bounded-buffer writer for canonical little-endian binary data.
Definition serialization.h:198
void write_u32(std::uint32_t value)
Write an unsigned 32-bit integer in little-endian order.
Definition serialization.h:258
void write_u8(std::uint8_t value)
Write an unsigned eight-bit integer.
Definition serialization.h:252
std::size_t size_bytes() const noexcept
Return the logical number of bytes written.
Definition serialization.h:244
void patch_u64(std::size_t position, std::uint64_t value)
Replace an existing 64-bit field with value.
Definition serialization.h:373
std::size_t write_u64_placeholder()
Write a zero 64-bit field and return its byte position.
Definition serialization.h:363
void write_u64(std::uint64_t value)
Write an unsigned 64-bit integer in little-endian order.
Definition serialization.h:261
void write_size(std::size_t value)
Write a platform size as an unsigned 64-bit integer.
Definition serialization.h:287
CRTP facade for an immutable positional integer vector.
Definition integer_vector.h:40
A non-owning, read-only view of a byte sequence.
Definition read_only_view.h:15
CRTP facade for optional binary serialization and deserialization.
Definition serialization.h:693
An unsigned integer-vector value type no wider than 64 bits.
Definition integer_vector.h:23
A concrete CRTP implementation of StorageBase.
Definition storage.h:290
Common interfaces for immutable positional integer vectors.
BasicPackedIntegerVector< ReadOnlyStorageView, Value > PackedIntegerVectorView
Zero-copy read-only packed integer-vector view.
Definition packed.h:404
BasicPackedIntegerVector< AlignedStorage, Value > PackedIntegerVector
Owning aligned packed integer vector.
Definition packed.h:397
Checked byte-oriented binary serialization primitives.
DeserializationValidation
Validation strength used while restoring serialized indexes.
Definition serialization.h:28
@ kQuick
Check framing, dimensions, references, and other conditions needed for memory-safe terminating querie...
Definition serialization.h:33
@ kFull
Additionally authenticate all source-derived metadata against the supplied source contents.
Definition serialization.h:39
Common interface for byte-addressable storage.