#pragma once // bank_package — RSBK framing and layout arithmetic: header encode, prefix // decode, and the ordered {name, offset, length} entry layout. Framing only, // never a payload: this module never holds, copies, or hashes an entry's audio // — the shell streams payloads one at a time against the layout produced here. #include #include #include #include #include "core/package/package_format.h" #include "core/package/package_manifest.h" namespace reasampler::package { // Where one payload sits in the finished package file (absolute byte offset). struct PackageEntrySpan { std::string name; std::uint64_t offset = 0; std::uint64_t length = 0; bool operator==(const PackageEntrySpan& o) const { return name == o.name && offset == o.offset && length == o.length; } }; // The write side's product: the prefix bytes (magic through manifest, written // verbatim as the file's head), the layout to stream each payload at, and the // finished file's exact size — what the shell verifies after the last append. struct EncodedPackage { std::vector prefix; std::vector layout; std::uint64_t totalSize = 0; }; // Encodes the package prefix for `m`, stamping this build's version pair and // version::stampVersion() as the writer semver. nullopt when the manifest // cannot be represented (serializeManifest's rejections) — refused on encode so // an undecodable package is never written. std::optional encodePackage(const PackageManifest& m); // The read side's product. header is meaningful for Readable and TooNew (a // refusal must still name the writer); manifest, layout, and prefixSize only // for Readable — TooNew produces NO manifest, so a refused decode cannot // half-succeed. struct DecodedPackage { PackageReadability status = PackageReadability::Malformed; PackageHeader header; PackageManifest manifest; std::vector layout; std::uint64_t prefixSize = 0; }; // Decodes a package's leading bytes. `totalFileSize` is the on-disk size the // caller observed: decode proves prefix + payload lengths equal it exactly, so // a truncated or garbage-extended file is Malformed even though the payloads // themselves are never read here. `prefix` may be the whole file or any head of // it that requiredPrefixSize accepted. Error signaled, never UB. DecodedPackage decodePackage(const std::vector& prefix, std::uint64_t totalFileSize); // How many leading bytes decodePackage needs. May grow as bytes arrive: with // fewer than the returned count on hand, read to that count and ask again. // For a TooNew package it stops at the frozen region (through the writer // semver) — field positions beyond it belong to the newer format and are not // trusted. nullopt: these bytes can never frame a package (bad magic, // incoherent versions, an over-cap length field) — stop reading. std::optional requiredPrefixSize(const std::vector& bytes); } // namespace reasampler::package