blackbox_reader.hpp¶
BlackboxReader — THE DECODER. It ships in the same chunk as the encoder, because a format nothing can read is not a record: the first time a blackbox genuinely matters is a competition afternoon, and a file that cannot be opened that afternoon is worth exac…
This header declares 3 types (19 members) and 1 free function.
Extracted from include/shulib/diag/blackbox_reader.hpp — this page is that header's documentation, reformatted, so it cannot disagree with the code. Prose about how to think about the API lives in the user guide; worked recipes live in the cookbook; this page is the complete, mechanical list of what exists.
Contents¶
enum class ReadStatusOkEmptyHeaderTruncatedBadMagicUnsupportedVersionLayoutMismatchreadStatusName— free functionclass BlackboxReaderBlackboxReaderstatususableheadernexttruncatedtruncatedFrameTypesawEndframesReadskippedFramesbytesConsumedstruct BlackboxReader::Frame
enum class ReadStatus¶
Why a file is or is not readable by THIS build. Anything other than Ok means no frames are delivered at all — the refuse-don't-misread rule.
enum class, declared at include/shulib/diag/blackbox_reader.hpp:68.
ReadStatus::Ok¶
header parsed and this build can read this version
enumerator, declared at include/shulib/diag/blackbox_reader.hpp:69.
ReadStatus::Empty¶
nothing at all was written (a run with nothing to say)
enumerator, declared at include/shulib/diag/blackbox_reader.hpp:70.
ReadStatus::HeaderTruncated¶
fewer bytes than a complete header
enumerator, declared at include/shulib/diag/blackbox_reader.hpp:71.
ReadStatus::BadMagic¶
not a shulib blackbox
enumerator, declared at include/shulib/diag/blackbox_reader.hpp:72.
ReadStatus::UnsupportedVersion¶
a version this build was not written for — REFUSED
enumerator, declared at include/shulib/diag/blackbox_reader.hpp:73.
ReadStatus::LayoutMismatch¶
right version, wrong record width — REFUSED (header note)
enumerator, declared at include/shulib/diag/blackbox_reader.hpp:74.
readStatusName¶
The §18.5 spelling of a ReadStatus, for messages. Never returns null.
free function, declared at include/shulib/diag/blackbox_reader.hpp:78.
class BlackboxReader¶
The decoder for a blackbox file: point it at bytes the caller already holds — it BORROWS the span, copies nothing and allocates nothing — and pull frames with next() until that returns false. Three rules it will not bend. A file this build cannot interpret is REFUSED WHOLE: status() says why and not one frame is delivered, because a number read wrongly but confidently sends an investigation somewhere false. It never throws and never reads past the end, which matters most for the damaged files that matter most. And truncation is a RESULT, not an error: every frame before the cut is delivered, truncated() is true, sawEnd() is false — the exact signature a brownout leaves. Frame kinds this build does not know are skipped by their declared length and counted in skippedFrames(), which is what lets a v1 reader survive a later writer. There is deliberately NO per-frame checksum, so a bit flip landing on a merely-wrong value inside a known frame decodes silently as that value (measured, not assumed — the header states the boundary in full). A consumer needing end-to-end integrity must add its own check rather than inherit one from here that does not exist.
class, declared at include/shulib/diag/blackbox_reader.hpp:104.
BlackboxReader::BlackboxReader¶
Parse the header of file and position at the first frame. Never throws; the verdict is in status(). The span must outlive the reader.
function, declared at include/shulib/diag/blackbox_reader.hpp:115.
BlackboxReader::status¶
Whether this build can read this file at all (header note, rule 1).
function, declared at include/shulib/diag/blackbox_reader.hpp:142.
BlackboxReader::usable¶
Shorthand for status() == Ok.
function, declared at include/shulib/diag/blackbox_reader.hpp:144.
BlackboxReader::header¶
The decoded header. Meaningful for Ok and UnsupportedVersion (a refused file still tells you which version it claims to be — that is how you find the build that wrote it).
function, declared at include/shulib/diag/blackbox_reader.hpp:148.
BlackboxReader::next¶
Deliver the next KNOWN frame, skipping (and counting) unknown ones. Returns false at the end of the file, on a cut, or when the file is not usable.
function, declared at include/shulib/diag/blackbox_reader.hpp:152.
BlackboxReader::truncated¶
True when the file ended mid-frame — the run was cut short (brownout, pulled card, a program that never closed). The frames delivered before the cut are all valid; this says the story stops there.
function, declared at include/shulib/diag/blackbox_reader.hpp:193.
BlackboxReader::truncatedFrameType¶
The raw frame-type byte of the frame that was cut, when truncated() (0 if the cut fell in a frame prefix).
function, declared at include/shulib/diag/blackbox_reader.hpp:196.
BlackboxReader::sawEnd¶
True once the graceful-end frame has been delivered. Its ABSENCE at the end of iteration is the honest signal that the run did not close cleanly.
function, declared at include/shulib/diag/blackbox_reader.hpp:199.
BlackboxReader::framesRead¶
Known frames delivered so far.
function, declared at include/shulib/diag/blackbox_reader.hpp:201.
BlackboxReader::skippedFrames¶
Frames skipped because this build does not know their type, or because a known type carried a payload of the wrong size (a corruption signal that must not stop the rest of the file from being read).
function, declared at include/shulib/diag/blackbox_reader.hpp:205.
BlackboxReader::bytesConsumed¶
Bytes consumed so far, including the header.
function, declared at include/shulib/diag/blackbox_reader.hpp:207.
struct BlackboxReader::Frame¶
One frame as delivered by next(): its type and a view of its payload, valid for as long as the caller's file buffer is.
struct, declared at include/shulib/diag/blackbox_reader.hpp:108.
BlackboxReader::Frame::type¶
the frame's kind (known types only)
field, declared at include/shulib/diag/blackbox_reader.hpp:109.
BlackboxReader::Frame::payload¶
exactly the declared payload bytes
field, declared at include/shulib/diag/blackbox_reader.hpp:110.
Design commentary, from the header¶
The header opens with the reasoning behind these shapes. It is reproduced here in full because a reference that only lists signatures teaches nobody why.
The header’s own reasoning — 55 lines, click to expand
BlackboxReader — THE DECODER. It ships in the same chunk as the encoder, because a
format nothing can read is not a record: the first time a blackbox genuinely matters
is a competition afternoon, and a file that cannot be opened that afternoon is worth
exactly nothing. (WS13, chunk E1; format in diag/blackbox_format.hpp.)
It is also the thing that makes the encoder TESTABLE. The round trip — encode a known
stream, decode it, compare field by field — is the chunk's central test, and it needs
both halves to exist in the same suite.
── Three rules it will not bend ────────────────────────────────────────────────────
1. REFUSE, NEVER MISREAD. A file whose version this build does not know is rejected
whole; so is a file whose self-declared tick width disagrees with this build's.
A wrong number read confidently is worse than no number: it sends an investigation
somewhere false with full confidence. (The width cross-check exists for one very
human failure: changing the layout and forgetting to bump the version.)
2. NEVER THROW, NEVER READ PAST THE END. The files that matter most are the damaged
ones — a run that died mid-write, a card pulled at the wrong moment. Every read
goes through a bounds-latching cursor, and a non-finite heading (which the Angle
type refuses by precondition) decodes to zero with `corrupt()` raised rather than
aborting the program that is trying to read the evidence.
3. TRUNCATION IS A RESULT, NOT AN ERROR. A cut file decodes up to the cut and SAYS
SO: truncated() is true, the frames before the cut are all delivered, and sawEnd()
is false because the graceful-end frame never arrived. That combination — good
frames, no end stamp — is exactly what a brownout leaves behind.
Forward compatibility: a frame type this build does not know is SKIPPED BY ITS
DECLARED LENGTH and counted in skippedFrames(). That is what lets a v1 reader survive
a later writer that appended a frame kind, without ever guessing at its content.
── The boundary of rule 1, stated so nobody over-trusts it ─────────────────────────
"Refuse, never misread" is about INTERPRETATION, not INTEGRITY, and the difference
matters to anyone building on this format (H1's SHUL/2 in particular).
What is detected: a file that is not a blackbox (BadMagic), a version this build does
not know (UnsupportedVersion), a layout whose declared widths disagree (LayoutMismatch),
a cut (truncated()), a frame kind this build lacks (skippedFrames()), and a decoded
value that is implausible on its face — a non-finite heading raises corrupt() rather
than tripping Angle's precondition.
What is NOT detected: there is deliberately NO per-frame checksum, so a bit flip inside
a frame this build CAN interpret, landing on a value that is merely wrong rather than
impossible, decodes silently as that wrong value. That was measured, not assumed:
flipping a payload byte mid-file leaves status Ok and every frame delivered.
This is a considered trade, not an oversight — a CRC costs bytes and cycles on every
tick of a fixed-width budget, and the SD card already carries hardware ECC beneath us.
Recorded because the honest scope of a guarantee is part of the guarantee: if a future
consumer needs end-to-end integrity (a wire protocol over a lossy link is the obvious
case, and F9 is exactly that), it must add its own frame check rather than inherit one
from here that does not exist. (Boundary verified during E1's independent review.)
Allocation-free and PROS-free, like everything in this tree: it reads a span the
caller already holds (a whole file loaded into a buffer, or the bytes a test just
captured), and iterates.
Last updated 2026-08-15 — published from the main release branch, commit fa545ff. This site tracks releases, so between them it can lag the repository — that lag is deliberate, and it is what keeps the development log off the public site. For the current state of the code, see the repository.