format: v2 B-trees, dense groups and group listings over Storage

BTreeV2Header::parse_in, collect_btree_v2_records_in and
find_btree_v2_records_in read one bounded window per node (its size is
known from the parent before the node is read; a count stretched past
node_size is checked against the end of the file first), with the
whole-file bounds errors unchanged. With them, dense attributes, a SOHM
B-tree index and huge fractal-heap objects no longer answer
ContiguousStorageRequired, and group_v1/group_v2 listings, lookups and
path resolution get *_in cores (resolve_group_children_in,
resolve_child_in, resolve_path_any_in, ...). The &[u8] functions are
thin wrappers, as in M1.

The equivalence harness now fails on any ContiguousStorageRequired and
compares v2 B-tree headers, records and descents, group listings, child
lookups and paths; a unit test compares a two-level tree through a
read_at-only storage truncated at every length and with every node byte
flipped.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-26 16:13:01 -05:00
co-authored by Claude Opus 5.5
parent 8f59b2e1c2
commit 42894bf93b
9 changed files with 544 additions and 297 deletions
+15 -21
View File
@@ -7,10 +7,10 @@ use alloc::{format, vec::Vec};
use byteorder::{ByteOrder, LittleEndian};
use crate::addr::to_usize;
use crate::btree_v2::{BTreeV2Header, find_btree_v2_records};
use crate::btree_v2::{BTreeV2Header, find_btree_v2_records_in};
use crate::error::FormatError;
use crate::filter_pipeline::FilterPipeline;
use crate::storage::{Storage, Window, len_usize, read_exact_at, require_contiguous};
use crate::storage::{Storage, Window, len_usize, read_exact_at};
/// Parsed fractal heap header (signature "FRHP").
#[derive(Debug, Clone)]
@@ -386,9 +386,7 @@ impl FractalHeapHeader {
self.read_managed_object_in(file_data, id_bytes, offset_size)
}
/// [`Self::read_managed_object`] over any [`Storage`]. A huge object
/// found through the huge-object v2 B-tree still needs the whole file
/// in memory ([`FormatError::ContiguousStorageRequired`] otherwise).
/// [`Self::read_managed_object`] over any [`Storage`].
pub fn read_managed_object_in<S: Storage + ?Sized>(
&self,
file_data: &S,
@@ -491,11 +489,9 @@ impl FractalHeapHeader {
"huge object ID but the heap has no huge-object index",
));
}
// The v2 B-tree is read from a slice until it is converted.
let file_data = require_contiguous(file, "a huge fractal-heap object's B-tree")?;
let hdr = BTreeV2Header::parse(
file_data,
to_usize(self.huge_btree_address)?,
let hdr = BTreeV2Header::parse_in(
file,
self.huge_btree_address,
self.offset_size,
self.length_size,
)?;
@@ -513,7 +509,7 @@ impl FractalHeapHeader {
// Records are ordered by ID (the last field): descend to the ones
// equal to `key` instead of reading the whole index.
let id_at = rec_len - ls;
let records = find_btree_v2_records(file_data, &hdr, self.offset_size, &mut |r| {
let records = find_btree_v2_records_in(file, &hdr, self.offset_size, &mut |r| {
le_uint(&r[id_at..id_at + ls]).cmp(&key)
})?;
for rec in &records {
@@ -1317,22 +1313,20 @@ mod tests {
assert_eq!(hdr.read_managed_object_in(&storage, &id, 8), want);
}
/// A huge object found through the huge-object B-tree needs the whole
/// file in memory until the B-tree reader is converted: a clean error
/// on other storage.
/// A huge object found through the huge-object B-tree reads the
/// B-tree through Storage: the same result (here an error, there is no
/// B-tree at that address) as from the slice.
#[test]
fn huge_object_btree_needs_contiguous_storage() {
fn huge_object_btree_reads_through_storage() {
use crate::storage::CountingStorage;
let (file, _) = build_simple_heap(8, 8);
let mut hdr = FractalHeapHeader::parse(&file, 0, 8, 8).unwrap();
hdr.huge_btree_address = 700;
let id = [0x10, 1, 0, 0, 0, 0, 0];
let want = hdr.read_managed_object(&file, &id, 8);
assert!(want.is_err());
let storage = CountingStorage::new(file);
assert_eq!(
hdr.read_managed_object_in(&storage, &[0x10, 1, 0, 0, 0, 0, 0], 8),
Err(FormatError::ContiguousStorageRequired(
"a huge fractal-heap object's B-tree"
))
);
assert_eq!(hdr.read_managed_object_in(&storage, &id, 8), want);
}
/// A header with an I/O filter pipeline (read in a second, longer