Skip to main content

hurray_ffi/
status.rs

1//! Status codes and error mapping for the Hurray C ABI.
2//!
3//! All exported `extern "C"` functions return a [`HurrayStatus`] integer. Zero
4//! indicates success; negative values indicate a specific error condition.
5//! Callers MUST check the return value of every function before using output
6//! pointer arguments.
7//!
8//! One positive value exists: [`HURRAY_END_OF_STREAM`]. It is not an error, which is why
9//! it is not negative, and why `status < 0` remains the error test.
10
11use hurray_core::Error;
12
13// ── Status code type ─────────────────────────────────────────────────────────
14
15/// Integer status code returned by every Hurray C ABI function.
16///
17/// Zero (`HURRAY_OK`) indicates success. All negative values indicate errors.
18/// Positive values report a normal, non-error outcome that is not plain success;
19/// [`HURRAY_END_OF_STREAM`] is the only one so far.
20///
21/// # Examples
22///
23/// ```
24/// use hurray_ffi::{HurrayStatus, HURRAY_OK};
25///
26/// fn check(s: HurrayStatus) {
27///     assert_eq!(s, HURRAY_OK);
28/// }
29/// check(HURRAY_OK);
30/// ```
31pub type HurrayStatus = i32;
32
33/// Operation completed successfully.
34pub const HURRAY_OK: HurrayStatus = 0;
35
36/// The descriptor magic bytes were not `"HRRY"` (`0x48 0x52 0x52 0x59`).
37pub const HURRAY_ERR_INVALID_MAGIC: HurrayStatus = -1;
38
39/// The descriptor version is not supported by this implementation.
40pub const HURRAY_ERR_VERSION_MISMATCH: HurrayStatus = -2;
41
42/// A layout descriptor field or tag is invalid.
43pub const HURRAY_ERR_INVALID_LAYOUT: HurrayStatus = -3;
44
45/// A type tag, device tag, or memory class is invalid.
46pub const HURRAY_ERR_INVALID_TYPE: HurrayStatus = -4;
47
48/// An output buffer is too small to hold the result.
49pub const HURRAY_ERR_BUFFER_TOO_SMALL: HurrayStatus = -5;
50
51/// A required pointer argument is null.
52pub const HURRAY_ERR_NULL_POINTER: HurrayStatus = -6;
53
54/// The function panicked internally; the handle MUST NOT be reused.
55pub const HURRAY_ERR_INTERNAL_PANIC: HurrayStatus = -7;
56
57/// The sync mode byte is not a recognized value.
58pub const HURRAY_ERR_INVALID_SYNC_MODE: HurrayStatus = -8;
59
60/// A sync-mode handoff payload does not match the buffer's declared sync mode.
61pub const HURRAY_ERR_SYNC_MODE_MISMATCH: HurrayStatus = -9;
62
63/// An unclassified internal error occurred.
64pub const HURRAY_ERR_INTERNAL: HurrayStatus = -10;
65
66/// An index argument is outside the valid range for the collection.
67pub const HURRAY_ERR_INDEX_OUT_OF_BOUNDS: HurrayStatus = -11;
68
69/// The requested field is valid for this layout but absent from this descriptor.
70///
71/// Distinct from [`HURRAY_ERR_FIELD_NOT_APPLICABLE`]: absence is a state this
72/// format expresses (a cleared flag bit), so it is reported rather than
73/// flattened to a zero the caller could mistake for data.
74pub const HURRAY_ERR_FIELD_NOT_PRESENT: HurrayStatus = -12;
75
76/// The requested field does not belong to this descriptor's layout.
77pub const HURRAY_ERR_FIELD_NOT_APPLICABLE: HurrayStatus = -13;
78
79/// A caller-supplied transport callback reported failure.
80///
81/// The handle is left usable: a callback that returns a failing status MUST NOT have
82/// moved any bytes (`hurray-ffi`'s transport contract, ADR-041 § 8), so the stream
83/// position is intact and the same call MAY be retried.
84pub const HURRAY_ERR_TRANSPORT: HurrayStatus = -14;
85
86/// The sink accepted zero bytes while an item was in flight.
87///
88/// Distinct from [`HURRAY_ERR_TRANSPORT`]: a truncated item is already on the wire, so
89/// the writer is poisoned and no further call on it can succeed (ADR-041 § 4).
90pub const HURRAY_ERR_SINK_CLOSED: HurrayStatus = -15;
91
92/// Input ended in the middle of a descriptor or a buffer.
93///
94/// Distinct from [`HURRAY_END_OF_STREAM`], which is input ending cleanly at a tensor
95/// boundary.
96pub const HURRAY_ERR_UNEXPECTED_EOF: HurrayStatus = -16;
97
98/// A transport moved bytes whose count was lost, so the handle's position no longer
99/// matches the transport's and nothing can recover it (ADR-041 § 6).
100///
101/// Unreachable from a conforming transport vtable, which reports a count for every
102/// transfer; the code exists because the state does.
103pub const HURRAY_ERR_CORE_UNUSABLE: HurrayStatus = -17;
104
105/// A descriptor, buffer, or nesting depth exceeded a configured limit.
106pub const HURRAY_ERR_LIMIT_EXCEEDED: HurrayStatus = -18;
107
108/// The byte stream is not a valid Hurray stream, or a stream that already failed was
109/// used again.
110pub const HURRAY_ERR_INVALID_STREAM: HurrayStatus = -19;
111
112/// No tensor of that name is in the file's footer index.
113///
114/// Its own code because it is the one file-read failure that is a question, not a defect:
115/// the file is fine and the caller asked for something that is not in it.
116pub const HURRAY_ERR_NOT_FOUND: HurrayStatus = -20;
117
118/// The file is not a valid HRRYFILE container, or the file being written would not be one.
119///
120/// Covers the footer family — a CRC mismatch, an index that overruns the trailer,
121/// unknown header flag bits — and the name and KV-key rules a writer must keep. A wrong
122/// magic or an unsupported container version report
123/// [`HURRAY_ERR_INVALID_MAGIC`] and [`HURRAY_ERR_VERSION_MISMATCH`] instead, which say
124/// more.
125pub const HURRAY_ERR_INVALID_FILE: HurrayStatus = -21;
126
127/// A composite was asked for where there is none, or offered where one is required.
128///
129/// Its own code for the reason [`HURRAY_ERR_NOT_FOUND`] has one: a composite read on a
130/// plain tensor, or a plain tensor at the head of a composite write, is a question the
131/// caller asked wrongly — nothing about the stream or the file is wrong.
132pub const HURRAY_ERR_NOT_COMPOSITE: HurrayStatus = -22;
133
134// ── Non-error outcomes ────────────────────────────────────────────────────────
135
136/// Input ended cleanly at a tensor boundary: there is no next tensor.
137///
138/// Positive, because end of input is the normal way a stream finishes and callers test
139/// errors with `status < 0`.
140pub const HURRAY_END_OF_STREAM: HurrayStatus = 1;
141
142// ── Error mapping ─────────────────────────────────────────────────────────────
143
144/// Maps a [`hurray_core::Error`] to the closest [`HurrayStatus`] error code.
145///
146/// This function is exhaustive over all known variants and falls back to
147/// [`HURRAY_ERR_INTERNAL`] for variants added in future crate versions
148/// (the `Error` enum is `#[non_exhaustive]`).
149pub(crate) fn status_from_core_error(e: &Error) -> HurrayStatus {
150    match e {
151        Error::InvalidMagic { .. } => HURRAY_ERR_INVALID_MAGIC,
152
153        Error::UnsupportedDescriptorVersion { .. } => HURRAY_ERR_VERSION_MISMATCH,
154
155        // Layout-structural errors
156        Error::DescriptorTooShort { .. }
157        | Error::DescriptorTruncated { .. }
158        | Error::DescriptorLengthMismatch { .. }
159        | Error::ReservedDescriptorFlagBitsSet { .. }
160        | Error::ReservedBytesNonZero { .. }
161        | Error::EmptyBufferTable
162        | Error::RankExceedsMaximum { .. }
163        | Error::InvalidLayout(_)
164        | Error::InvalidLayoutTag(_)
165        | Error::ReservedLayoutTag(_)
166        | Error::PrivateLayoutTag(_)
167        | Error::UnknownLayoutTag(_)
168        | Error::ExtensionTypeFlagMismatch { .. }
169        | Error::ExtensionTypePackingInvalid { .. }
170        | Error::ShardOutOfBounds { .. }
171        | Error::StatisticsReservedMaskBitsSet { .. }
172        | Error::AlignmentNotPowerOfTwo { .. }
173        | Error::AlignmentBelowMinimum { .. }
174        | Error::AlignmentError { .. }
175        | Error::InvalidShape(_)
176        // The composite family is layout-structural too: every one of them is the
177        // composite layout's contract with its members, broken. They became reachable
178        // from C with step 6's composite writes — before that no C entry point could run
179        // the validator, and they fell through to `HURRAY_ERR_INTERNAL`, which would have
180        // blamed the library for a caller's tree.
181        | Error::CompositeHeadHasBuffers { .. }
182        | Error::CompositeHeadHasByteOffset { .. }
183        | Error::CompositeHeadHasQuantization
184        | Error::OpenCompositeReserved
185        | Error::CompositeMemberFlagMismatch { .. }
186        | Error::CompositeMemberCountMismatch { .. }
187        | Error::CompositeMemberMissingShard { .. }
188        | Error::CompositeMemberParentShapeMismatch { .. }
189        | Error::CompositeMemberTypeMismatch { .. }
190        | Error::CompositeOverlayBaseNotFirst
191        | Error::CompositeOverlayBaseNotSpanning
192        | Error::CompositeOverlayEmpty
193        | Error::CompositePartitionGap
194        | Error::CompositePartitionOverlap { .. } => HURRAY_ERR_INVALID_LAYOUT,
195
196        // Type / device / memory class errors
197        Error::InvalidTypeTag(_)
198        | Error::ReservedTypeTag(_)
199        | Error::UnknownTypeTag(_)
200        | Error::UnsupportedElementType(_)
201        | Error::InvalidDeviceTag(_)
202        | Error::ReservedDeviceTag(_)
203        | Error::InvalidMemoryClass(_)
204        | Error::ReservedMemoryClass(_) => HURRAY_ERR_INVALID_TYPE,
205
206        Error::InvalidSyncMode(_) => HURRAY_ERR_INVALID_SYNC_MODE,
207
208        // Everything else (including future non_exhaustive variants)
209        _ => HURRAY_ERR_INTERNAL,
210    }
211}
212
213/// Maps a [`hurray_io::Error`] to the closest [`HurrayStatus`] error code.
214///
215/// Format errors are delegated to [`status_from_core_error`] so that a malformed
216/// descriptor reports the same code whether it arrived through
217/// [`hurray_descriptor_decode`][crate::hurray_descriptor_decode] or through a stream.
218pub(crate) fn status_from_io_error(e: &hurray_io::Error) -> HurrayStatus {
219    use hurray_io::Error as E;
220    match e {
221        E::Core(inner) => status_from_core_error(inner),
222
223        E::Io(_) => HURRAY_ERR_TRANSPORT,
224        E::UnexpectedEof => HURRAY_ERR_UNEXPECTED_EOF,
225        E::SinkClosed => HURRAY_ERR_SINK_CLOSED,
226        E::CoreUnusable(_) => HURRAY_ERR_CORE_UNUSABLE,
227
228        E::FrameTooLarge { .. } | E::CompositeNestingTooDeep { .. } => HURRAY_ERR_LIMIT_EXCEEDED,
229
230        E::MultiBufferLengthMismatch { .. }
231        | E::BufferSizeMismatch { .. }
232        | E::InvalidCrossMachineSyncMode { .. }
233        | E::UnallocatableBuffer { .. }
234        | E::TornComposite { .. }
235        | E::CoreFailed => HURRAY_ERR_INVALID_STREAM,
236
237        // Not a malformed stream or file: the caller asked for a composite read on a
238        // tensor that is not one, which the index or the wire answered correctly.
239        E::NotAComposite(_) => HURRAY_ERR_NOT_COMPOSITE,
240
241        // A driver bug, not a caller error: this crate's driver always reports a count it
242        // was offered. Reachable only if that invariant breaks, and never silently.
243        E::InvalidProgressReport { .. } | E::ZeroLengthWrite => HURRAY_ERR_INTERNAL,
244
245        // The file container. Magic and version reuse the codes that already name them,
246        // rather than flattening into one "bad file" status that says less.
247        E::InvalidFileMagic | E::InvalidTrailerMagic => HURRAY_ERR_INVALID_MAGIC,
248        E::UnsupportedContainerVersion { .. } => HURRAY_ERR_VERSION_MISMATCH,
249        E::TensorNotFound(_) => HURRAY_ERR_NOT_FOUND,
250        // `InvalidHeader` is the container's own variant — every site that produces it is
251        // under `file/` — so it belongs here rather than with the stream codes.
252        E::InvalidHeader(_)
253        | E::IndexCrc32cMismatch { .. }
254        | E::IndexOverrunsTrailer
255        | E::ReservedFileFlagBits { .. }
256        | E::DuplicateTensorName(_)
257        // A driver bug rather than a caller error, but unreachable from C — this crate
258        // writes the header in `hurray_file_writer_new` — so it maps with the container's
259        // own code instead of making the wildcard below load-bearing.
260        | E::HeaderNotWritten
261        | E::TensorNameEmpty
262        | E::TensorNameTooLong { .. }
263        | E::DuplicateKvKey(_)
264        | E::KvKeyEmpty
265        | E::KvKeyTooLong { .. } => HURRAY_ERR_INVALID_FILE,
266
267        // Any future variants.
268        _ => HURRAY_ERR_INTERNAL,
269    }
270}