hurray_core/error.rs
1/// Crate-level error type for `hurray-core`.
2#[derive(Debug, thiserror::Error)]
3#[non_exhaustive]
4pub enum Error {
5 /// An element type is not supported or recognized.
6 #[error("unsupported element type: {0}")]
7 UnsupportedElementType(String),
8
9 /// A shape or stride value is invalid.
10 #[error("invalid shape or stride: {0}")]
11 InvalidShape(String),
12
13 /// A buffer alignment requirement was not satisfied.
14 ///
15 /// This variant is preserved for callers that check alignment against an
16 /// externally observed value (e.g., checking the actual base-address
17 /// alignment of a pointer). For descriptor-level validation, prefer
18 /// [`Error::AlignmentNotPowerOfTwo`] and [`Error::AlignmentBelowMinimum`].
19 #[error("buffer alignment error: expected {expected}-byte alignment, got {actual}")]
20 AlignmentError { expected: usize, actual: usize },
21
22 /// The `alignment` field is not a power of two.
23 ///
24 /// The spec requires that `alignment` is always a power of two
25 /// (see `docs/spec/buffer-protocol.md § Alignment`).
26 #[error("alignment {alignment} is not a power of two")]
27 AlignmentNotPowerOfTwo { alignment: u32 },
28
29 /// The `alignment` field is below the minimum required for a non-empty buffer.
30 ///
31 /// Non-empty buffers (those with `byte_size > 0`) MUST declare an alignment
32 /// of at least [`crate::MIN_BUFFER_ALIGNMENT`] (64 bytes) to guarantee
33 /// compatibility with all current SIMD instruction sets.
34 #[error(
35 "alignment {alignment} is below the minimum of {minimum} bytes required for non-empty buffers"
36 )]
37 AlignmentBelowMinimum { alignment: u32, minimum: u32 },
38
39 /// The device tag byte `0xFF` is permanently invalid.
40 ///
41 /// This sentinel is reserved by the spec and MUST be rejected by all
42 /// conforming readers (see `docs/spec/buffer-protocol.md § Device Tags`).
43 #[error("invalid device tag: 0x{0:02X} is permanently reserved")]
44 InvalidDeviceTag(u8),
45
46 /// The device tag falls in the range `0x04`–`0xEF` reserved for future spec versions.
47 ///
48 /// Implementations MUST NOT assign semantics to tags in this range.
49 #[error("reserved device tag: 0x{0:02X} is reserved for future specification versions")]
50 ReservedDeviceTag(u8),
51
52 /// All buffers in a tensor descriptor must reside on the same device.
53 ///
54 /// The `expected` and `found` fields are raw wire bytes so that the error
55 /// message is independent of which device tags the current implementation
56 /// recognises.
57 #[error("device tag mismatch: expected 0x{expected:02X}, found 0x{found:02X}")]
58 DeviceTagMismatch {
59 /// The wire byte of the first buffer's device tag.
60 expected: u8,
61 /// The wire byte of the mismatching buffer's device tag.
62 found: u8,
63 },
64
65 /// Buffer list is empty; at least one buffer handle is required.
66 ///
67 /// Returned by [`crate::validate_colocation`] when called with an empty
68 /// slice — there is no device tag to validate against.
69 #[error("buffer list is empty")]
70 EmptyBufferList,
71
72 /// The memory class byte is `0xFF`, which is permanently reserved by the spec.
73 ///
74 /// Readers MUST reject a buffer handle whose `memory_class` is `0xFF`.
75 #[error("invalid memory class: 0x{0:02X} is permanently reserved")]
76 InvalidMemoryClass(u8),
77
78 /// The memory class byte falls in the range `0x04`–`0xEF` reserved for future spec versions.
79 ///
80 /// Readers MUST reject a buffer handle with a `memory_class` in this range.
81 #[error("reserved memory class: 0x{0:02X} is reserved for future specification versions")]
82 ReservedMemoryClass(u8),
83
84 /// All buffers in a tensor descriptor must share the same memory class.
85 ///
86 /// The `expected` and `found` fields are raw wire bytes so that the error
87 /// message is independent of which memory classes the current implementation
88 /// recognises.
89 #[error("memory class mismatch: expected 0x{expected:02X}, found 0x{found:02X}")]
90 MemoryClassMismatch {
91 /// The wire byte of the first buffer's memory class.
92 expected: u8,
93 /// The wire byte of the mismatching buffer's memory class.
94 found: u8,
95 },
96
97 /// A quantization descriptor is malformed.
98 #[error("invalid quantization descriptor: {0}")]
99 InvalidQuantization(String),
100
101 /// The quantization descriptor payload is shorter than the minimum required.
102 #[error("quantization descriptor too short: have {found} bytes, need {needed}")]
103 QuantizationDescriptorTooShort {
104 /// Number of bytes available.
105 found: usize,
106 /// Number of bytes required.
107 needed: usize,
108 },
109
110 /// The scheme tag `0x00` or `0xFF` is permanently reserved by the spec.
111 ///
112 /// A reader MUST reject any descriptor whose `scheme_tag` is `0x00` or `0xFF`.
113 #[error("invalid quantization scheme tag: 0x{0:02X} is permanently reserved")]
114 InvalidQuantizationSchemeTag(u8),
115
116 /// The scheme tag falls in a range reserved for future specification versions.
117 ///
118 /// Ranges: `0x60`–`0xEF`. Implementations MUST NOT assign semantics to these tags.
119 #[error(
120 "reserved quantization scheme tag: 0x{0:02X} is reserved for future specification versions"
121 )]
122 ReservedQuantizationSchemeTag(u8),
123
124 /// The scheme tag is in the implementation-private range `0xF0`–`0xFE`.
125 ///
126 /// Private scheme tags have unconstrained payloads beyond the 4-byte header;
127 /// `hurray-core` cannot interpret them. Callers that need private scheme
128 /// support must handle the raw bytes at a higher layer.
129 ///
130 /// WHY rejected here: the payload beyond the 4-byte header is unconstrained
131 /// for private tags, giving this crate nothing useful to return. Callers that
132 /// need private schemes handle the raw bytes at a higher layer (design decision #1).
133 #[error(
134 "private quantization scheme tag: 0x{0:02X} is implementation-private and not interpretable by this crate"
135 )]
136 PrivateQuantizationSchemeTag(u8),
137
138 /// The scheme tag is in an allocated range but not assigned to any known scheme.
139 #[error("unknown quantization scheme tag: 0x{0:02X}")]
140 UnknownQuantizationSchemeTag(u8),
141
142 /// The `scheme_version` field exceeds the highest version this implementation supports.
143 ///
144 /// Per the spec, a reader MUST reject a descriptor whose `scheme_version` exceeds
145 /// the highest version defined for the given `scheme_tag`.
146 #[error(
147 "unsupported scheme version: scheme tag 0x{tag:02X} version {version} is not supported (highest supported: {supported})"
148 )]
149 UnsupportedSchemeVersion {
150 /// The scheme tag being decoded.
151 tag: u8,
152 /// The version found on the wire.
153 version: u8,
154 /// The highest version this implementation supports for this tag.
155 supported: u8,
156 },
157
158 /// Reserved `flags` bits are set in the quantization descriptor header.
159 ///
160 /// Per the spec, a reader MUST reject a descriptor with any reserved `flags` bit set.
161 #[error("reserved quantization flags bits set: 0x{flags:04X} (reserved mask: 0x{mask:04X})")]
162 ReservedQuantizationFlagsBits {
163 /// The full flags value found on the wire.
164 flags: u16,
165 /// The bitmask of bits that must be zero.
166 mask: u16,
167 },
168
169 /// The `block_size` field is out of range or not a power of two for the given scheme.
170 #[error(
171 "invalid block_size {block_size} for scheme 0x{scheme_tag:02X}: must be a power of two in [{min}, {max}]"
172 )]
173 InvalidBlockSize {
174 /// The quantization scheme tag.
175 scheme_tag: u8,
176 /// The block size found on the wire.
177 block_size: u32,
178 /// Minimum permitted block size for this scheme.
179 min: u32,
180 /// Maximum permitted block size for this scheme.
181 max: u32,
182 },
183
184 /// The tensor's storage `type_tag` is not valid for the given quantization scheme.
185 ///
186 /// Each quantization scheme defines a fixed set of permitted storage types.
187 #[error(
188 "element type 0x{type_tag:02X} is not a valid storage type for scheme 0x{scheme_tag:02X}"
189 )]
190 InvalidStorageTypeForScheme {
191 /// The quantization scheme tag.
192 scheme_tag: u8,
193 /// The storage type tag found in the tensor descriptor.
194 type_tag: u8,
195 },
196
197 /// The quantization axis index is out of bounds for the tensor's rank.
198 ///
199 /// Per the spec, `axis` MUST satisfy `axis < rank`.
200 #[error("quantization axis {axis} out of bounds: rank is {rank}")]
201 QuantizationAxisOutOfBounds {
202 /// The axis value from the quantization descriptor.
203 axis: u32,
204 /// The rank of the tensor descriptor.
205 rank: u32,
206 },
207
208 /// The tensor shape along the quantization axis is incompatible with the block size.
209 ///
210 /// For MXFP, `shape[axis]` must be a positive multiple of `block_size`.
211 /// For per-block-affine and NF4, `block_size` must not exceed `shape[axis]`
212 /// when `shape[axis] > 0`.
213 #[error(
214 "quantization shape mismatch on axis {axis}: shape[axis] = {shape_axis}, block_size = {block_size}: {reason}"
215 )]
216 QuantizationShapeMismatch {
217 /// The quantization axis.
218 axis: u32,
219 /// The resolved size of `shape[axis]`.
220 shape_axis: u64,
221 /// The block size from the quantization descriptor.
222 block_size: u32,
223 /// A human-readable description of the constraint violated.
224 reason: &'static str,
225 },
226
227 /// The `zero_point` value is outside the representable range of the storage type.
228 ///
229 /// Per the spec, `zero_point` MUST lie within the representable range of the
230 /// storage type (e.g., `[0, 255]` for `uint8`).
231 #[error(
232 "zero_point {zero_point} is outside the range of storage type 0x{type_tag:02X}: [{min}, {max}]"
233 )]
234 ZeroPointOutOfRange {
235 /// The storage type tag.
236 type_tag: u8,
237 /// The zero-point value that was rejected.
238 zero_point: i32,
239 /// Minimum representable value for the storage type.
240 min: i64,
241 /// Maximum representable value for the storage type.
242 max: i64,
243 },
244
245 /// A quantization-parameter buffer index aliases the tensor data buffer.
246 ///
247 /// Per the spec, quantization-parameter buffers MUST occupy distinct indices
248 /// from the tensor data buffer.
249 #[error("quantization parameter buffer index {index} aliases the tensor data buffer index")]
250 QuantizationBufferAliasesData {
251 /// The offending quantization-parameter buffer index.
252 index: u32,
253 },
254
255 /// A quantization-parameter buffer index is out of range.
256 ///
257 /// The index must be less than `buffer_count` in the tensor descriptor's
258 /// buffer table.
259 #[error(
260 "quantization parameter buffer index {index} is out of range (buffer_count = {buffer_count})"
261 )]
262 QuantizationBufferIndexOutOfRange {
263 /// The out-of-range buffer index.
264 index: u32,
265 /// The number of buffers in the tensor descriptor's buffer table.
266 buffer_count: u32,
267 },
268
269 /// The type tag `0x00` or `0xFF` is explicitly invalid per the spec.
270 ///
271 /// These two sentinels are permanently reserved and MUST be rejected by all
272 /// conforming readers regardless of operating mode.
273 #[error(
274 "invalid type tag: 0x{0:02X} is permanently reserved and must never appear in a descriptor"
275 )]
276 InvalidTypeTag(u8),
277
278 /// The type tag falls in a range reserved for future specification versions.
279 ///
280 /// Reserved ranges: `0x47` and `0x80`–`0xEF`. Implementations MUST NOT
281 /// assign semantics to these tags.
282 #[error("reserved type tag: 0x{0:02X} is reserved for future specification versions")]
283 ReservedTypeTag(u8),
284
285 /// The type tag is not recognized by this implementation.
286 ///
287 /// This covers the private-extension range `0xF0`–`0xFE` and any other
288 /// unassigned tag value not covered by [`Error::InvalidTypeTag`] or
289 /// [`Error::ReservedTypeTag`].
290 #[error("unknown type tag: 0x{0:02X} is not recognized by this implementation")]
291 UnknownTypeTag(u8),
292
293 /// The tensor rank exceeds the maximum of 64 defined by the spec.
294 #[error("rank {rank} exceeds the maximum permitted rank of {max}")]
295 RankExceedsMaximum {
296 /// The rank value that was rejected.
297 rank: u32,
298 /// The maximum permitted rank (`64`).
299 max: u32,
300 },
301
302 /// Layout tag `0x00` or `0xFF` is permanently invalid.
303 ///
304 /// These sentinels are reserved by the spec and MUST be rejected by all
305 /// conforming readers regardless of operating mode
306 /// (see `docs/spec/memory-layout.md § Layout Taxonomy`).
307 #[error("invalid layout tag: 0x{0:02X} is permanently reserved")]
308 InvalidLayoutTag(u8),
309
310 /// Layout tag is in a range reserved for future specification versions.
311 ///
312 /// Reserved ranges: `0x0C`–`0x3F`, `0x41`–`0x7F`, `0x80`–`0xEF`.
313 /// (`0x0B` is the composite / virtual head (ADR-027) and `0x40` is Hilbert;
314 /// both are named tags in this crate.)
315 /// Implementations MUST NOT assign semantics to these tags in strict mode.
316 #[error("reserved layout tag: 0x{0:02X} is reserved for future specification versions")]
317 ReservedLayoutTag(u8),
318
319 /// A tag with a named layout descriptor was passed to the permissive
320 /// [`UnknownLayout`](crate::layout::UnknownLayout) constructor.
321 ///
322 /// "Unknown" is what lets a permissive reader relay a tag it does not
323 /// understand. Applied to a tag this implementation *does* understand, it
324 /// becomes a bypass: `Unknown` has no buffer count and no shape constraints,
325 /// so such a descriptor would skip every check the named variant applies and
326 /// then encode to a wire tag a conforming reader parses as that named layout.
327 #[error("layout tag 0x{0:02X} has a named descriptor and must not be wrapped as unknown")]
328 NamedLayoutTag(u8),
329
330 /// Layout tag is in the private-extension range `0xF0`–`0xFE`.
331 ///
332 /// Private extension layouts have unconstrained payloads beyond the standard
333 /// header fields; strict-mode readers reject them unless both parties have
334 /// agreed on semantics out of band (see `docs/spec/memory-layout.md § Extension Layouts`).
335 #[error("private layout tag: 0x{0:02X} is in the private-extension range (0xF0–0xFE)")]
336 PrivateLayoutTag(u8),
337
338 /// Layout tag is not recognized by this implementation.
339 ///
340 /// Covers any allocated but unassigned tag not already matched by
341 /// [`Error::InvalidLayoutTag`] or [`Error::ReservedLayoutTag`].
342 #[error("unknown layout tag: 0x{0:02X} is not recognized by this implementation")]
343 UnknownLayoutTag(u8),
344
345 /// A layout descriptor field is invalid.
346 ///
347 /// The inner message describes the specific field and the constraint violated.
348 #[error("invalid layout descriptor: {0}")]
349 InvalidLayout(String),
350
351 /// The number of index components does not match the tensor rank.
352 #[error("index rank {index_rank} does not match shape rank {shape_rank}")]
353 IndexRankMismatch {
354 index_rank: usize,
355 shape_rank: usize,
356 },
357
358 /// An index component exceeds the dimension size.
359 #[error("index[{dim}] = {index} is out of range [0, {size})")]
360 IndexOutOfRange { dim: u32, index: u64, size: u64 },
361
362 /// Arithmetic overflow when computing an element or byte address.
363 #[error("address arithmetic overflow")]
364 AddressOverflow,
365
366 /// The computed byte address falls outside the buffer bounds.
367 #[error("byte address falls outside buffer bounds [0, {buffer_size})")]
368 ByteAddressOverflow { buffer_size: u64 },
369
370 /// This layout requires multi-buffer access via an inherent method, not the trait.
371 #[error("layout tag 0x{layout_tag:02X} requires multi-buffer access; use the layout-specific method")]
372 LayoutRequiresMultiBuffer { layout_tag: u8 },
373
374 /// Recursive layout nesting depth exceeded the implementation limit of 8 levels.
375 ///
376 /// Despite the name, this guards recursion depth for every recursive
377 /// structure in the crate: [`crate::layout::TiledLayout`]'s nested layout
378 /// payload, and [`crate::composite::CompositeTensor`]'s cross-descriptor
379 /// nesting (a member whose own layout is itself `Composite`). Kept as
380 /// `SubpavingNestingTooDeep` for wire/API stability rather than renamed
381 /// as part of the subpaving removal.
382 #[error("layout nesting depth exceeds the implementation limit of 8 levels")]
383 SubpavingNestingTooDeep,
384
385 /// A DYNAMIC dimension cannot be used for element addressing.
386 #[error("dimension {dim} is DYNAMIC and cannot be used for element addressing")]
387 DynamicDimInIndexing { dim: u32 },
388
389 /// Arithmetic overflow in Morton or Hilbert index computation.
390 #[error("index arithmetic overflow in space-filling curve computation")]
391 IndexArithmeticOverflow,
392
393 // ── Descriptor (Layer 4) errors ───────────────────────────────────────────
394 /// The sync mode byte is not a recognized value.
395 ///
396 /// Valid sync mode bytes are `0x00` (`ProducerSynced`), `0x01` (`Event`),
397 /// and `0x02` (`ConsumerStream`). Bytes `0x03`–`0xFF` are reserved or
398 /// permanently invalid; all conforming readers MUST reject them.
399 ///
400 /// See `docs/spec/buffer-protocol.md § Synchronization Mode` and ADR-018.
401 #[error("invalid sync mode byte: 0x{0:02X} (reserved or invalid)")]
402 InvalidSyncMode(u8),
403
404 /// Magic bytes are not `"HRRY"` (`0x48 0x52 0x52 0x59`).
405 #[error("invalid magic: expected 48 52 52 59, got {got:02X?}")]
406 InvalidMagic {
407 /// The four bytes actually found at offset 0.
408 got: [u8; 4],
409 },
410
411 /// `version_major` exceeds the supported major version (`1`).
412 #[error("unsupported descriptor version {major}.{minor}")]
413 UnsupportedDescriptorVersion {
414 /// The major version found on the wire.
415 major: u8,
416 /// The minor version found on the wire.
417 minor: u8,
418 },
419
420 /// `descriptor_length` is less than the minimum valid size (`20`).
421 #[error("descriptor_length {length} is below the minimum of 20 bytes")]
422 DescriptorTooShort {
423 /// The `descriptor_length` value found on the wire.
424 length: u32,
425 },
426
427 /// Cursor ran out of bytes before a field could be read.
428 #[error("descriptor truncated: need {needed} bytes at offset {offset}, have {available}")]
429 DescriptorTruncated {
430 /// Byte offset at which the read was attempted.
431 offset: usize,
432 /// Number of bytes needed.
433 needed: usize,
434 /// Number of bytes actually available.
435 available: usize,
436 },
437
438 /// A reserved flag bit is set.
439 #[error("reserved flag bits set: 0x{flags:08X} (reserved mask: 0x{mask:08X})")]
440 ReservedDescriptorFlagBitsSet {
441 /// The full flags value found on the wire.
442 flags: u32,
443 /// The bitmask of bits that must be zero.
444 mask: u32,
445 },
446
447 /// A reserved field that MUST be `0x00` contains a non-zero byte.
448 #[error("reserved field '{field}' must be 0x00")]
449 ReservedBytesNonZero {
450 /// Human-readable name of the reserved field.
451 field: &'static str,
452 },
453
454 /// `buffer_count` is `0` (minimum is `1`).
455 #[error("buffer_count is 0; at least one buffer handle is required")]
456 EmptyBufferTable,
457
458 /// `HAS_EXTENSION_TYPE` flag ↔ `type_tag` range disagree.
459 #[error(
460 "HAS_EXTENSION_TYPE flag is {flag_set} but type_tag 0x{type_tag:02X} is {type_tag_in_range}"
461 )]
462 ExtensionTypeFlagMismatch {
463 /// Whether the `HAS_EXTENSION_TYPE` flag was set.
464 flag_set: bool,
465 /// The `type_tag` byte found on the wire.
466 type_tag: u8,
467 /// Human-readable range description.
468 type_tag_in_range: &'static str,
469 },
470
471 /// Extension type `bit_width` / `packing_factor` is invalid.
472 #[error(
473 "extension type packing invalid: bit_width={bit_width}, packing_factor={packing_factor}"
474 )]
475 ExtensionTypePackingInvalid {
476 /// The `bit_width` value from the extension type descriptor.
477 bit_width: u32,
478 /// The `packing_factor` value from the extension type descriptor.
479 packing_factor: u8,
480 },
481
482 /// An extension type field carries a value the spec forbids for its family.
483 #[error("extension type field invalid: {reason}")]
484 ExtensionTypeFieldInvalid {
485 /// Which rule was violated, in the spec's own terms.
486 reason: &'static str,
487 },
488
489 /// `shard_offset[k] + shape[k] > parent_shape[k]`.
490 #[error("shard out of bounds on dim {dim}: offset {offset} + size {size} > parent {parent}")]
491 ShardOutOfBounds {
492 /// The dimension index where the constraint was violated.
493 dim: usize,
494 /// The `shard_offset` value for that dimension.
495 offset: u64,
496 /// The tensor `shape` value for that dimension.
497 size: u64,
498 /// The `parent_shape` value for that dimension.
499 parent: u64,
500 },
501
502 /// `computed_mask` has reserved bits set (bits ≥ 6).
503 #[error("statistics computed_mask 0x{mask:08X} has reserved bits set")]
504 StatisticsReservedMaskBitsSet {
505 /// The full `computed_mask` value found on the wire.
506 mask: u32,
507 },
508
509 /// `descriptor_length` declared in the header does not match the actual encoded length.
510 #[error("descriptor_length mismatch: declared {declared}, actual {actual}")]
511 DescriptorLengthMismatch {
512 /// The `descriptor_length` value declared in the header.
513 declared: u32,
514 /// The actual number of bytes consumed when parsing.
515 actual: usize,
516 },
517
518 // ── Composite / Virtual (ADR-027, Layer 4) errors ─────────────────────────
519 /// A composite head (`layout_tag = 0x0B`) declared a non-empty buffer table.
520 #[error(
521 "composite head has {count} buffer(s); a composite head (layout_tag 0x0B) MUST have buffer_count = 0"
522 )]
523 CompositeHeadHasBuffers {
524 /// The (non-zero) buffer count found on the wire.
525 count: u8,
526 },
527
528 /// A composite head declared a non-zero `byte_offset`.
529 #[error(
530 "composite head has byte_offset {byte_offset}; a composite head MUST have byte_offset = 0"
531 )]
532 CompositeHeadHasByteOffset {
533 /// The offending `byte_offset` value.
534 byte_offset: u64,
535 },
536
537 /// A composite head set the `HAS_QUANTIZATION` flag, but a virtual head owns no stored data.
538 #[error("composite head MUST NOT set HAS_QUANTIZATION (a virtual head owns no stored data)")]
539 CompositeHeadHasQuantization,
540
541 /// The `composition_rule` byte `0xFF` is permanently invalid.
542 #[error("invalid composition rule: 0x{0:02X} is permanently reserved")]
543 InvalidCompositionRule(u8),
544
545 /// The `composition_rule` byte falls in the range reserved for future specification
546 /// versions (`0x00` and `0x04`–`0xEF`).
547 #[error("reserved composition rule: 0x{0:02X} is reserved for future specification versions")]
548 ReservedCompositionRule(u8),
549
550 /// The `composition_rule` byte is in the implementation-private range `0xF0`–`0xFE`.
551 #[error(
552 "private composition rule: 0x{0:02X} is implementation-private and not interpretable by this crate"
553 )]
554 PrivateCompositionRule(u8),
555
556 /// The `combine_op` byte is not legal for the given `composition_rule`.
557 #[error("invalid combine_op 0x{combine_op:02X} for composition rule 0x{rule:02X}")]
558 InvalidCombineOp {
559 /// The wire `composition_rule` byte.
560 rule: u8,
561 /// The offending `combine_op` byte.
562 combine_op: u8,
563 },
564
565 /// The `member_count` sentinel `0xFFFFFFFF` (open composite) is RESERVED and not usable in v1.0.
566 #[error("member_count = 0xFFFFFFFF (open composite) is reserved and not usable in v1.0")]
567 OpenCompositeReserved,
568
569 /// The Composite Member section's `member_role` byte is not `0x00` or `0x01`.
570 #[error("invalid composite member_role: 0x{0:02X} (MUST be 0x00 correction or 0x01 base)")]
571 InvalidMemberRole(u8),
572
573 /// A member's `HAS_COMPOSITE_MEMBER` flag disagrees with what its composition rule requires.
574 ///
575 /// Overlay members MUST carry a Composite Member section; partition and group members
576 /// MUST NOT.
577 #[error(
578 "composite member HAS_COMPOSITE_MEMBER presence ({has_flag}) disagrees with composition rule 0x{rule:02X}"
579 )]
580 CompositeMemberFlagMismatch {
581 /// The wire `composition_rule` byte of the enclosing head.
582 rule: u8,
583 /// Whether the member actually carried a Composite Member section.
584 has_flag: bool,
585 },
586
587 /// The number of members actually supplied does not match the head's declared `member_count`.
588 #[error(
589 "composite member_count mismatch: head declared {declared}, but {actual} member(s) were supplied"
590 )]
591 CompositeMemberCountMismatch {
592 /// The `member_count` declared by the head.
593 declared: u32,
594 /// The number of members actually pushed to the validator.
595 actual: usize,
596 },
597
598 /// A partition or overlay member did not carry a shard section (`HAS_SHARD`).
599 #[error(
600 "composite member {index} is missing a shard section (partition/overlay members MUST carry HAS_SHARD)"
601 )]
602 CompositeMemberMissingShard {
603 /// The 0-based index of the offending member.
604 index: usize,
605 },
606
607 /// A member's shard `parent_shape` does not equal the composite head's `shape`.
608 #[error("composite member {index}: shard parent_shape does not equal the head's shape")]
609 CompositeMemberParentShapeMismatch {
610 /// The 0-based index of the offending member.
611 index: usize,
612 },
613
614 /// A member's decoded value type does not equal the composite head's `type_tag`.
615 #[error(
616 "composite member {index}: decoded type 0x{member:02X} does not equal head type_tag 0x{head:02X}"
617 )]
618 CompositeMemberTypeMismatch {
619 /// The 0-based index of the offending member.
620 index: usize,
621 /// The member's own `type_tag` byte.
622 member: u8,
623 /// The head's `type_tag` byte.
624 head: u8,
625 },
626
627 /// An overlay composite's first member is not the base, or a base member appeared at a
628 /// position other than first.
629 #[error(
630 "overlay composite: the base member (member_role = 0x01) MUST be first, and only the first member may be the base"
631 )]
632 CompositeOverlayBaseNotFirst,
633
634 /// An overlay composite's base member does not span the whole index space.
635 #[error(
636 "overlay composite: the base member MUST span the whole index space (zero shard_offset, shape == head shape)"
637 )]
638 CompositeOverlayBaseNotSpanning,
639
640 /// An overlay composite head declared `member_count = 0` (no base member available).
641 #[error(
642 "overlay composite MUST have at least one member (the base); member_count = 0 is rejected"
643 )]
644 CompositeOverlayEmpty,
645
646 /// A partition composite's members leave a gap in the head's index space.
647 #[error(
648 "partition composite: member boxes do not exactly cover the head's index space (gap detected)"
649 )]
650 CompositePartitionGap,
651
652 /// Two partition members' boxes overlap.
653 #[error("partition composite: member {a} and member {b} boxes overlap")]
654 CompositePartitionOverlap {
655 /// The 0-based index of the first overlapping member.
656 a: usize,
657 /// The 0-based index of the second overlapping member.
658 b: usize,
659 },
660
661 /// A layout is virtual (owns no data buffer) and does not support element addressing.
662 ///
663 /// Currently only the composite head (`layout_tag = 0x0B`) is virtual; addressing a
664 /// composite requires resolving to a member first (see `hurray_core::composite`).
665 #[error("layout tag 0x{layout_tag:02X} is virtual (owns no data) and does not support element addressing")]
666 LayoutIsVirtual {
667 /// The virtual layout's wire tag.
668 layout_tag: u8,
669 },
670}
671
672/// Convenience alias for `Result` with [`Error`].
673pub type Result<T> = std::result::Result<T, Error>;