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}