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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user