043558a54d
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.
72 lines
3.1 KiB
C++
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
|