Merge branch 'feat/p3-storage-trait' into feat/p3-range-zfp-edit
# Conflicts: # CHANGELOG.md # crates/clawhdf5-format/src/attribute.rs # crates/clawhdf5-format/src/btree_v1.rs # crates/clawhdf5-format/src/data_layout.rs # crates/clawhdf5-format/src/extensible_array.rs # crates/clawhdf5-format/src/fixed_array.rs # crates/clawhdf5-format/src/fractal_heap.rs # crates/clawhdf5-format/src/local_heap.rs # crates/clawhdf5-format/src/shared_message.rs
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::addr::to_usize;
|
||||
use crate::error::FormatError;
|
||||
use crate::storage::{Storage, read_exact_at};
|
||||
|
||||
/// A parsed B-tree v1 node.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -75,13 +75,28 @@ impl BTreeV1Node {
|
||||
file_data: &[u8],
|
||||
offset: usize,
|
||||
offset_size: u8,
|
||||
length_size: u8,
|
||||
) -> Result<BTreeV1Node, FormatError> {
|
||||
Self::parse_in(file_data, offset as u64, offset_size, length_size)
|
||||
}
|
||||
|
||||
/// [`Self::parse`] over any [`Storage`]: one read of the node's header,
|
||||
/// one of its keys and children.
|
||||
pub fn parse_in<S: Storage + ?Sized>(
|
||||
file: &S,
|
||||
offset: u64,
|
||||
offset_size: u8,
|
||||
_length_size: u8,
|
||||
) -> Result<BTreeV1Node, FormatError> {
|
||||
// signature(4) + node_type(1) + node_level(1) + entries_used(2) = 8
|
||||
// + left_sibling(offset_size) + right_sibling(offset_size)
|
||||
let os = offset_size as usize;
|
||||
let header_size = 8 + os * 2;
|
||||
ensure_len(file_data, offset, header_size)?;
|
||||
let header = read_exact_at(file, offset, header_size)?;
|
||||
let file_data: &[u8] = &header;
|
||||
// The header's read checked that `offset + header_size` fits.
|
||||
let body_start = offset + header_size as u64;
|
||||
let offset = 0usize;
|
||||
|
||||
if &file_data[offset..offset + 4] != b"TREE" {
|
||||
return Err(FormatError::InvalidBTreeSignature);
|
||||
@@ -103,31 +118,30 @@ impl BTreeV1Node {
|
||||
} else {
|
||||
Some(read_offset(file_data, pos, offset_size)?)
|
||||
};
|
||||
pos += os;
|
||||
|
||||
// For type 0: keys are offset_size bytes, children are offset_size bytes
|
||||
// Layout: key[0], child[0], key[1], child[1], ..., key[N-1], child[N-1], key[N]
|
||||
let eu = entries_used as usize;
|
||||
let key_size = os; // For type 0, key = offset_size
|
||||
let needed = eu * (key_size + os) + key_size; // eu children + (eu+1) keys
|
||||
ensure_len(file_data, pos, needed)?;
|
||||
let body = read_exact_at(file, body_start, needed)?;
|
||||
let file_data: &[u8] = &body;
|
||||
|
||||
let mut keys = Vec::with_capacity(eu + 1);
|
||||
let mut children = Vec::with_capacity(eu);
|
||||
|
||||
for _i in 0..eu {
|
||||
// key[i]
|
||||
let key = read_offset(file_data, pos, offset_size)?;
|
||||
keys.push(key);
|
||||
pos += key_size;
|
||||
// child[i]
|
||||
let child = read_offset(file_data, pos, offset_size)?;
|
||||
children.push(child);
|
||||
pos += os;
|
||||
if os == 0 {
|
||||
// What reading the first key reports (and keeps `chunks_exact`
|
||||
// below from being given a zero size).
|
||||
return Err(FormatError::InvalidOffsetSize(offset_size));
|
||||
}
|
||||
// final key
|
||||
let key = read_offset(file_data, pos, offset_size)?;
|
||||
keys.push(key);
|
||||
// `needed` bytes: key[0], child[0], ..., child[eu - 1], key[eu].
|
||||
let (pairs, last) = file_data.split_at(eu * (key_size + os));
|
||||
for pair in pairs.chunks_exact(key_size + os) {
|
||||
keys.push(read_offset(pair, 0, offset_size)?);
|
||||
children.push(read_offset(pair, key_size, offset_size)?);
|
||||
}
|
||||
keys.push(read_offset(last, 0, offset_size)?);
|
||||
|
||||
Ok(BTreeV1Node {
|
||||
node_type,
|
||||
@@ -151,11 +165,21 @@ pub fn collect_symbol_table_nodes(
|
||||
offset_size: u8,
|
||||
length_size: u8,
|
||||
) -> Result<Vec<u64>, FormatError> {
|
||||
collect_symbol_table_nodes_inner(file_data, btree_address, offset_size, length_size, 0)
|
||||
collect_symbol_table_nodes_in(file_data, btree_address, offset_size, length_size)
|
||||
}
|
||||
|
||||
fn collect_symbol_table_nodes_inner(
|
||||
file_data: &[u8],
|
||||
/// [`collect_symbol_table_nodes`] over any [`Storage`]: two reads per node.
|
||||
pub fn collect_symbol_table_nodes_in<S: Storage + ?Sized>(
|
||||
file: &S,
|
||||
btree_address: u64,
|
||||
offset_size: u8,
|
||||
length_size: u8,
|
||||
) -> Result<Vec<u64>, FormatError> {
|
||||
collect_symbol_table_nodes_inner(file, btree_address, offset_size, length_size, 0)
|
||||
}
|
||||
|
||||
fn collect_symbol_table_nodes_inner<S: Storage + ?Sized>(
|
||||
file: &S,
|
||||
btree_address: u64,
|
||||
offset_size: u8,
|
||||
length_size: u8,
|
||||
@@ -165,12 +189,7 @@ fn collect_symbol_table_nodes_inner(
|
||||
return Err(FormatError::NestingDepthExceeded);
|
||||
}
|
||||
|
||||
let node = BTreeV1Node::parse(
|
||||
file_data,
|
||||
to_usize(btree_address)?,
|
||||
offset_size,
|
||||
length_size,
|
||||
)?;
|
||||
let node = BTreeV1Node::parse_in(file, btree_address, offset_size, length_size)?;
|
||||
|
||||
if node.node_type != 0 {
|
||||
return Err(FormatError::InvalidBTreeNodeType(node.node_type));
|
||||
@@ -184,7 +203,7 @@ fn collect_symbol_table_nodes_inner(
|
||||
let mut result = Vec::new();
|
||||
for &child_addr in &node.children {
|
||||
let child_snods = collect_symbol_table_nodes_inner(
|
||||
file_data,
|
||||
file,
|
||||
child_addr,
|
||||
offset_size,
|
||||
length_size,
|
||||
@@ -323,4 +342,48 @@ mod tests {
|
||||
assert_eq!(node.entries_used, 1);
|
||||
assert_eq!(node.children, vec![0x50]);
|
||||
}
|
||||
|
||||
/// Nodes and trees, cut at every length, parse identically through a
|
||||
/// `read_at`-only storage.
|
||||
#[test]
|
||||
fn storage_parse_matches_slice_parse() {
|
||||
use crate::storage::CountingStorage;
|
||||
let nodes = [
|
||||
build_btree_node(0, 0, &[0, 5, 10], &[0x100, 0x200], None, None, 8),
|
||||
build_btree_node(0, 0, &[0, 5], &[0x100], Some(0x40), Some(0x80), 4),
|
||||
build_btree_node(1, 2, &[0, 5], &[0x100], None, Some(0x80), 8),
|
||||
];
|
||||
for (n, node) in nodes.iter().enumerate() {
|
||||
let os = if n == 1 { 4 } else { 8 };
|
||||
for cut in 0..=node.len() {
|
||||
let f = &node[..cut];
|
||||
let storage = CountingStorage::new(f.to_vec());
|
||||
let want = BTreeV1Node::parse(f, 0, os, 8);
|
||||
let got = BTreeV1Node::parse_in(&storage, 0, os, 8);
|
||||
assert_eq!(format!("{got:?}"), format!("{want:?}"));
|
||||
}
|
||||
}
|
||||
let leaf1 = build_btree_node(0, 0, &[0, 5], &[0xA00], None, None, 8);
|
||||
let leaf2 = build_btree_node(0, 0, &[5, 10], &[0xB00], None, None, 8);
|
||||
let internal = build_btree_node(0, 1, &[0, 5, 10], &[0, 256], None, None, 8);
|
||||
let mut file = vec![0u8; 512 + internal.len()];
|
||||
file[..leaf1.len()].copy_from_slice(&leaf1);
|
||||
file[256..256 + leaf2.len()].copy_from_slice(&leaf2);
|
||||
file[512..].copy_from_slice(&internal);
|
||||
for cut in [file.len(), 300, 260, 100, 10] {
|
||||
let mut f = file.clone();
|
||||
if cut < 512 {
|
||||
// Truncate the leaves, keep the root.
|
||||
f[cut..512].fill(0);
|
||||
}
|
||||
let storage = CountingStorage::new(f.clone());
|
||||
assert_eq!(
|
||||
collect_symbol_table_nodes_in(&storage, 512, 8, 8),
|
||||
collect_symbol_table_nodes(&f, 512, 8, 8)
|
||||
);
|
||||
}
|
||||
let storage = CountingStorage::new(file);
|
||||
collect_symbol_table_nodes_in(&storage, 512, 8, 8).unwrap();
|
||||
assert_eq!(storage.reads(), 6);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user