pub struct FileView<'a> { /* private fields */ }Expand description
Implementations§
Source§impl<'a> FileView<'a>
impl<'a> FileView<'a>
Sourcepub fn open(buf: &'a [u8]) -> Result<FileView<'a>>
pub fn open(buf: &'a [u8]) -> Result<FileView<'a>>
Open a file image, recovering the newest valid commit.
Follows spec §7.2: scan back from the end over 8-aligned offsets for a footer whose trailing magic, CRC, and self-referential length all agree. The first hit is the newest generation, so an intact file resolves on the first candidate and a file torn mid-append transparently opens at the previous generation — the crash-safety guarantee, exercised as a read.
pub fn is_empty(&self) -> bool
Sourcepub fn generation(&self) -> u64
pub fn generation(&self) -> u64
The commit counter of the generation this view resolved to. A value lower than expected after a crash means the torn commit was rolled back.
The authoritative footer.
Sourcepub fn file_len(&self) -> u64
pub fn file_len(&self) -> u64
The committed extent. Bytes at or beyond this are uncommitted debris and carry no meaning.
Sourcepub fn next_record_id(&self) -> u64
pub fn next_record_id(&self) -> u64
The id the next appended record will take. Every live record’s id is strictly below this, and no id at or above it has ever been used.
Sourcepub fn has_record_checksums(&self) -> bool
pub fn has_record_checksums(&self) -> bool
Whether this file carries a CRC-32 per record (OPT_RECORD_CRC).
Sourcepub fn record_checksum(&self, i: usize) -> Option<u32>
pub fn record_checksum(&self, i: usize) -> Option<u32>
The stored CRC-32 for record i, or None when the file carries none.
The array ends exactly at index_offset, in the space a reader without
the feature already skips — which is what makes the feature optional
rather than a format change.
Sourcepub fn verify_checksums(&self) -> Result<usize>
pub fn verify_checksums(&self) -> Result<usize>
Verify every record against its stored checksum.
Returns the number checked — Ok(0) for a file that carries none, which
is not an error: checksums are optional, and their absence is a property
of the file, not a fault. A mismatch is Error::ChecksumMismatch,
naming the record’s id rather than its position.
Sourcepub fn schemas(&self) -> &SchemaRegistry
pub fn schemas(&self) -> &SchemaRegistry
The file’s schema section, decoded. Every schema needed to read every record is here — this is what “self-contained” means concretely.
Sourcepub fn get(&self, i: usize) -> Result<&'a [u8]>
pub fn get(&self, i: usize) -> Result<&'a [u8]>
The raw message bytes of record i, borrowing the file image. Hand the
result straight to Message::parse.
Sourcepub fn find_by_id(&self, id: u64) -> Option<usize>
pub fn find_by_id(&self, id: u64) -> Option<usize>
The position of the record with this id, or None if it is not live.
A binary search: ids ascend with the index (appends are monotonic and
neither removal nor compaction reorders), and open proved it. No
secondary structure, and nothing outside the index bytes is touched.
Sourcepub fn records_after(&self, id: u64) -> impl Iterator<Item = Record> + '_
pub fn records_after(&self, id: u64) -> impl Iterator<Item = Record> + '_
Every record whose id is greater than id, in order — a tailing
reader’s “everything since my checkpoint”.
Pass the last id you processed; pass 0 for the whole file. The search
for the starting position is logarithmic, so polling a large file is
cheap even when nothing has changed.
Sourcepub fn schema_id(&self, i: usize) -> Result<u128>
pub fn schema_id(&self, i: usize) -> Result<u128>
The schema id of record i, read from the index — no record bytes are
touched, so filtering a large mapped file by type stays in one
contiguous region instead of paging the whole file in.
Sourcepub fn schema(&self, i: usize) -> Result<&Schema>
pub fn schema(&self, i: usize) -> Result<&Schema>
The writer schema of record i, from the file’s own schema section.
Sourcepub fn message(&self, i: usize) -> Result<Message<'a>>
pub fn message(&self, i: usize) -> Result<Message<'a>>
Parse record i into a Message, zero-copy over the file image.
Sourcepub fn resolver_for(&self, i: usize, reader: &Schema) -> Result<Resolver>
pub fn resolver_for(&self, i: usize, reader: &Schema) -> Result<Resolver>
A Resolver reading record i’s writer schema into reader — the
schema-evolution payoff at rest. A record written years ago under an
older schema resolves into today’s type, because the file kept the
writer schema alongside it.
Builds a resolver on every call. Resolution is meant to be paid once
per schema pair, not once per record, so calling this inside a loop
over a large file repeats identical work. Use
resolvers there — it resolves each distinct schema
in the file once and hands back a lookup.
Sourcepub fn resolvers(&self, reader: &Schema) -> Resolvers
pub fn resolvers(&self, reader: &Schema) -> Resolvers
Resolve every schema this file’s records use into reader, once
each, and return the lookup to use across the whole file.
This is the shape the “paid once per schema pair” promise actually needs: hoist it out of the loop, then ask it per record.
let file = FileView::open(&bytes).unwrap();
let resolvers = file.resolvers(&schema); // once
for i in 0..file.len() {
let resolver = resolvers.for_record(&file, i).unwrap();
let root = file.message(i).unwrap().root(resolver).unwrap();
assert_eq!(root.get_i32(1).unwrap(), Some(7));
}A file may hold records this reader cannot interpret — a mixed-schema
file read by a type that only covers one of them. Those simply do not
appear in the lookup, and Resolvers::for_record reports them as
incompatible rather than the whole call failing, so a reader can walk a
mixed file and skip what is not for it.
Sourcepub fn dump_json(&self, i: usize) -> Result<String>
pub fn dump_json(&self, i: usize) -> Result<String>
Render record i as JSON using only this file’s bytes.