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.
81 lines
3.8 KiB
C++
81 lines
3.8 KiB
C++
#pragma once
|
|
// package_format — the RSBK bank-package contract: magic, the version ladder,
|
|
// the readability classification, and the entry-name rule. Pure: standard
|
|
// library only. The framing codec that acts on this contract is bank_package;
|
|
// the manifest grammar is package_manifest.
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
namespace reasampler::package {
|
|
|
|
// Version ladder for the RSBK container (read-and-validate, like the origin
|
|
// ledger's "v"):
|
|
//
|
|
// format 1 (current) magic "RSBK" | u32 formatVersion | u32 minReaderVersion
|
|
// | u32 len + writer semver | u32 len + JSON manifest
|
|
// | payloads concatenated in manifest entry order.
|
|
// All integers little-endian.
|
|
//
|
|
// Two integers, two jobs: formatVersion is what the writer emitted (monotonic,
|
|
// bumped on ANY change); minReaderVersion is the oldest reader that can read the
|
|
// package safely (bumped only on a STRUCTURAL change — a field's meaning shifts,
|
|
// a section is removed, framing moves; an additive change — a new optional
|
|
// manifest key, a new enum value with a defined degrade — leaves it alone). The
|
|
// reader's whole rule: read iff minReaderVersion <= kPackageFormatVersion.
|
|
// formatVersion beyond that is message text and log material only.
|
|
//
|
|
// FROZEN FOR ALL FUTURE VERSIONS: the fields through the writer semver. A
|
|
// too-new package must still yield the writer's version so the refusal can name
|
|
// what to install — a structural change may rearrange anything after the semver,
|
|
// never before it.
|
|
inline constexpr char kPackageMagic[4] = {'R', 'S', 'B', 'K'};
|
|
inline constexpr std::uint32_t kPackageFormatVersion = 1;
|
|
inline constexpr std::uint32_t kPackageMinReaderVersion = 1;
|
|
|
|
// Hostile-input allocation caps (error signaled, never a multi-gigabyte
|
|
// allocation off a forged length field). Generous against real content: a
|
|
// semver is ~10 bytes; a manifest for hundreds of samples is well under 1 MB.
|
|
inline constexpr std::uint32_t kMaxWriterVersionBytes = 64;
|
|
inline constexpr std::uint32_t kMaxManifestBytes = 64u * 1024u * 1024u;
|
|
inline constexpr std::size_t kMaxEntryNameBytes = 255;
|
|
|
|
// The three-way read verdict (the FutureVersion precedent): TooNew refuses the
|
|
// whole package before anything is produced; Malformed is a header no honest
|
|
// writer emits. Also the status of a full prefix decode in bank_package.
|
|
enum class PackageReadability {
|
|
Readable,
|
|
TooNew,
|
|
Malformed,
|
|
};
|
|
|
|
// Classify a stored header pair against THIS build's ladder. minReaderVersion
|
|
// above kPackageFormatVersion is TooNew; a zero version or minReader >
|
|
// formatVersion is Malformed (a writer cannot require a reader newer than what
|
|
// it wrote).
|
|
PackageReadability classifyPackageVersion(std::uint32_t formatVersion,
|
|
std::uint32_t minReaderVersion);
|
|
|
|
// The entry-name rule that makes path expression structurally impossible: a
|
|
// bare file name only. Rejects empty, ".", any ".." occurrence, any '/', '\\'
|
|
// or ':' (which also bans every absolute form — drive, UNC, rooted), and names
|
|
// over kMaxEntryNameBytes. Enforced on encode AND decode by package_manifest.
|
|
bool isValidEntryName(const std::string& name);
|
|
|
|
// The fixed header, informational semver included. writerVersion is
|
|
// version::stampVersion() on the write side — it exists so a TooNew refusal can
|
|
// tell the user which build to install; it never gates.
|
|
struct PackageHeader {
|
|
std::uint32_t formatVersion = kPackageFormatVersion;
|
|
std::uint32_t minReaderVersion = kPackageMinReaderVersion;
|
|
std::string writerVersion;
|
|
|
|
bool operator==(const PackageHeader& o) const {
|
|
return formatVersion == o.formatVersion &&
|
|
minReaderVersion == o.minReaderVersion &&
|
|
writerVersion == o.writerVersion;
|
|
}
|
|
};
|
|
|
|
} // namespace reasampler::package
|