Range reads M0/M1 (indexed lookups, Storage trait), ZFP, in-place editing #17
@@ -4,6 +4,7 @@
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use crate::error::FormatError;
|
use crate::error::FormatError;
|
||||||
|
use crate::storage::{Storage, len_usize, read_exact_at};
|
||||||
|
|
||||||
/// Symbol Table message (type 0x0011) found in v1 group object headers.
|
/// Symbol Table message (type 0x0011) found in v1 group object headers.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -79,48 +80,53 @@ impl SymbolTableNode {
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
offset_size: u8,
|
offset_size: u8,
|
||||||
) -> Result<SymbolTableNode, FormatError> {
|
) -> Result<SymbolTableNode, FormatError> {
|
||||||
// signature(4) + version(1) + reserved(1) + number_of_symbols(2) = 8
|
Self::parse_in(&file_data, offset as u64, offset_size)
|
||||||
if offset
|
}
|
||||||
.checked_add(8)
|
|
||||||
.is_none_or(|end| end > file_data.len())
|
|
||||||
{
|
|
||||||
return Err(FormatError::UnexpectedEof {
|
|
||||||
expected: offset.saturating_add(8),
|
|
||||||
available: file_data.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if &file_data[offset..offset + 4] != b"SNOD" {
|
/// [`Self::parse`] over any [`Storage`]: one read of the node's header,
|
||||||
|
/// one of its entries.
|
||||||
|
pub fn parse_in(
|
||||||
|
file: &dyn Storage,
|
||||||
|
offset: u64,
|
||||||
|
offset_size: u8,
|
||||||
|
) -> Result<SymbolTableNode, FormatError> {
|
||||||
|
let file_len = len_usize(file);
|
||||||
|
// signature(4) + version(1) + reserved(1) + number_of_symbols(2) = 8
|
||||||
|
let header = read_exact_at(file, offset, 8)?;
|
||||||
|
|
||||||
|
if &header[..4] != b"SNOD" {
|
||||||
return Err(FormatError::InvalidSymbolTableNodeSignature);
|
return Err(FormatError::InvalidSymbolTableNodeSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = file_data[offset + 4];
|
let version = header[4];
|
||||||
if version != 1 {
|
if version != 1 {
|
||||||
return Err(FormatError::InvalidSymbolTableNodeVersion(version));
|
return Err(FormatError::InvalidSymbolTableNodeVersion(version));
|
||||||
}
|
}
|
||||||
|
|
||||||
let num_symbols =
|
let num_symbols = u16::from_le_bytes([header[6], header[7]]) as usize;
|
||||||
u16::from_le_bytes([file_data[offset + 6], file_data[offset + 7]]) as usize;
|
|
||||||
|
|
||||||
let os = offset_size as usize;
|
let os = offset_size as usize;
|
||||||
// Each entry: link_name_offset(os) + obj_hdr_addr(os) + cache_type(4) + reserved(4) + scratch(16)
|
// Each entry: link_name_offset(os) + obj_hdr_addr(os) + cache_type(4) + reserved(4) + scratch(16)
|
||||||
let entry_size = os + os + 4 + 4 + 16;
|
let entry_size = os + os + 4 + 4 + 16;
|
||||||
let entries_start = offset + 8;
|
// `offset + 8` fits: the header's read checked it.
|
||||||
|
let entries_start = offset as usize + 8;
|
||||||
let needed = entries_start.checked_add(num_symbols * entry_size).ok_or(
|
let needed = entries_start.checked_add(num_symbols * entry_size).ok_or(
|
||||||
FormatError::UnexpectedEof {
|
FormatError::UnexpectedEof {
|
||||||
expected: usize::MAX,
|
expected: usize::MAX,
|
||||||
available: file_data.len(),
|
available: file_len,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
if needed > file_data.len() {
|
if needed > file_len {
|
||||||
return Err(FormatError::UnexpectedEof {
|
return Err(FormatError::UnexpectedEof {
|
||||||
expected: needed,
|
expected: needed,
|
||||||
available: file_data.len(),
|
available: file_len,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let body = read_exact_at(file, entries_start as u64, num_symbols * entry_size)?;
|
||||||
|
let file_data: &[u8] = &body;
|
||||||
|
|
||||||
let mut entries = Vec::with_capacity(num_symbols);
|
let mut entries = Vec::with_capacity(num_symbols);
|
||||||
let mut pos = entries_start;
|
let mut pos = 0usize;
|
||||||
for _ in 0..num_symbols {
|
for _ in 0..num_symbols {
|
||||||
let link_name_offset = read_offset(file_data, pos, offset_size)?;
|
let link_name_offset = read_offset(file_data, pos, offset_size)?;
|
||||||
pos += os;
|
pos += os;
|
||||||
@@ -256,4 +262,28 @@ mod tests {
|
|||||||
let result = SymbolTableNode::parse(&data, usize::MAX / 2, 8);
|
let result = SymbolTableNode::parse(&data, usize::MAX / 2, 8);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Nodes, cut at every length and at an offset, parse identically
|
||||||
|
/// through a `read_at`-only storage.
|
||||||
|
#[test]
|
||||||
|
fn storage_parse_matches_slice_parse() {
|
||||||
|
use crate::storage::CountingStorage;
|
||||||
|
for os in [4u8, 8] {
|
||||||
|
let node = build_snod(&[(0, 0x100, 0), (8, 0x200, 1), (16, 0x300, 2)], os);
|
||||||
|
let mut bad = node.clone();
|
||||||
|
bad[4] = 2;
|
||||||
|
for full in [node, bad] {
|
||||||
|
for at in [0usize, 7] {
|
||||||
|
for cut in 0..=full.len() {
|
||||||
|
let mut f = vec![0u8; at];
|
||||||
|
f.extend_from_slice(&full[..cut]);
|
||||||
|
let storage = CountingStorage::new(f.clone());
|
||||||
|
let want = SymbolTableNode::parse(&f, at, os);
|
||||||
|
let got = SymbolTableNode::parse_in(&storage, at as u64, os);
|
||||||
|
assert_eq!(format!("{got:?}"), format!("{want:?}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user