Files
reasampler/src/core/package/bank_package.h
T
daniel 043558a54d Land src/core/package: the pure RSBK container — format ladder, JSON manifest, framing/layout codec
Two-integer ladder (formatVersion/minReaderVersion), bare-name-only entries
validated on encode and decode, prefix decode that proves exact file size
without ever reading a payload.
2026-08-02 17:19:29 -04:00

72 lines
3.1 KiB
C++

#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 <cstdint>
#include <optional>
#include <string>
#include <vector>
#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<std::uint8_t> prefix;
std::vector<PackageEntrySpan> 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<EncodedPackage> 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<PackageEntrySpan> 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<std::uint8_t>& 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<std::uint64_t> requiredPrefixSize(const std::vector<std::uint8_t>& bytes);
} // namespace reasampler::package