use crate::error::{Error, Result};
use crate::filters;
use crate::io::reader::HdfReader;
use std::io::{Read, Seek};
use std::thread;
use super::chunk_read::{BorrowedChunkPayloadRead, ChunkBTreeRecord, ChunkReadContext};
use super::{u64_from_usize, usize_from_u64, Dataset, DatasetInfo};
const MAX_CHUNK_BTREE_V1_RECURSION: usize = 64;
const MIN_PARALLEL_DEFLATE_CHUNKS_1D: usize = 8;
const MIN_PARALLEL_DEFLATE_BYTES_1D: usize = 64 * 1024;
enum ChunkBTreeNode {
Leaf {
level: u8,
records: Vec<ChunkBTreeRecord>,
},
Internal {
level: u8,
child_addrs: Vec<u64>,
},
}
struct ChunkBTreeKey {
scaled: Vec<u64>,
nbytes: u64,
filter_mask: u32,
}
struct ChunkBTreeChild {
key: ChunkBTreeKey,
child_addr: u64,
}
struct ChunkBTreeLookupNode {
level: u8,
children: Vec<ChunkBTreeChild>,
final_key: ChunkBTreeKey,
}
struct ChunkBTreeKeyDims<'a> {
chunk_dims: &'a [u64],
element_size: usize,
}
impl Dataset {
pub(super) fn lookup_btree_v1_chunk<R: Read + Seek>(
reader: &mut HdfReader<R>,
addr: u64,
data_ndims: usize,
chunk_dims: &[u64],
element_size: usize,
chunk_origin: &[u64],
) -> Result<Option<ChunkBTreeRecord>> {
if data_ndims != chunk_dims.len() || data_ndims != chunk_origin.len() {
return Err(Error::InvalidFormat(
"v1 B-tree lookup rank mismatch".into(),
));
}
if crate::io::reader::is_undef_addr(addr) {
return Ok(None);
}
let mut target_scaled = Vec::with_capacity(data_ndims + 1);
for ((&origin, &chunk_dim), dim_index) in
chunk_origin.iter().zip(chunk_dims).zip(0..data_ndims)
{
if chunk_dim == 0 {
return Err(Error::InvalidFormat("chunk dimension is zero".into()));
}
if origin % chunk_dim != 0 {
return Err(Error::InvalidFormat(format!(
"v1 B-tree lookup coordinate for dimension {dim_index} is not chunk-aligned"
)));
}
target_scaled.push(origin / chunk_dim);
}
target_scaled.push(0);
let mut visited = Vec::new();
let key_dims = ChunkBTreeKeyDims {
chunk_dims,
element_size,
};
let found = Self::lookup_btree_v1_chunk_inner(
reader,
addr,
data_ndims,
&key_dims,
&target_scaled,
0,
None,
&mut visited,
)?;
let (lookup_addr, lookup_nbytes, lookup_mask) = found
.as_ref()
.map(|record| (record.chunk_addr, record.chunk_size, record.filter_mask))
.unwrap_or_else(|| (crate::io::reader::UNDEF_ADDR, 0, 0));
Self::trace_btree1_chunk_lookup(
addr,
&target_scaled[..data_ndims],
lookup_addr,
lookup_nbytes,
lookup_mask,
);
Ok(found)
}
#[allow(clippy::too_many_arguments)]
fn lookup_btree_v1_chunk_inner<R: Read + Seek>(
reader: &mut HdfReader<R>,
addr: u64,
data_ndims: usize,
key_dims: &ChunkBTreeKeyDims<'_>,
target_scaled: &[u64],
depth: usize,
expected_level: Option<u8>,
visited: &mut Vec<u64>,
) -> Result<Option<ChunkBTreeRecord>> {
if depth > MAX_CHUNK_BTREE_V1_RECURSION {
return Err(Error::InvalidFormat(
"v1 chunk B-tree recursion depth exceeded".into(),
));
}
if crate::io::reader::is_undef_addr(addr) {
return Err(Error::InvalidFormat(
"v1 chunk B-tree node address is undefined".into(),
));
}
if visited.contains(&addr) {
return Err(Error::InvalidFormat(
"v1 chunk B-tree traversal cycle detected".into(),
));
}
visited.push(addr);
let node = Self::decode_chunk_btree_lookup_node(reader, addr, data_ndims, key_dims)?;
if let Some(expected_level) = expected_level {
if node.level != expected_level {
visited.pop();
return Err(Error::InvalidFormat(format!(
"v1 chunk B-tree child level {} does not match expected {expected_level}",
node.level
)));
}
}
let mut lt = 0usize;
let mut rt = node.children.len();
let mut idx = 0usize;
let mut cmp = 1i32;
while lt < rt && cmp != 0 {
idx = (lt + rt) / 2;
let right_key = if idx + 1 < node.children.len() {
&node.children[idx + 1].key
} else {
&node.final_key
};
cmp = Self::btree_v1_cmp3(&node.children[idx].key, target_scaled, right_key);
if cmp < 0 {
rt = idx;
} else {
lt = idx + 1;
}
}
let result = if cmp != 0 {
Ok(None)
} else if node.level > 0 {
let child_addr = node.children[idx].child_addr;
if child_addr == addr {
Err(Error::InvalidFormat(
"cyclic v1 chunk B-tree detected".into(),
))
} else {
Self::lookup_btree_v1_chunk_inner(
reader,
child_addr,
data_ndims,
key_dims,
target_scaled,
depth.checked_add(1).ok_or_else(|| {
Error::InvalidFormat("v1 chunk B-tree recursion depth overflow".into())
})?,
Some(node.level - 1),
visited,
)
}
} else {
Self::found_btree_v1_chunk(&node.children[idx], data_ndims, key_dims, target_scaled)
};
visited.pop();
result
}
pub(super) fn read_chunked_btree_v1<R: Read + Seek>(
reader: &mut HdfReader<R>,
info: &DatasetInfo,
chunk_ctx: &ChunkReadContext<'_>,
) -> Result<Vec<u8>> {
let mut output = Self::scratch_output(chunk_ctx.total_bytes);
Self::read_chunked_btree_v1_into(reader, info, chunk_ctx, &mut output)?;
Ok(output)
}
pub(super) fn read_chunked_btree_v1_into<R: Read + Seek>(
reader: &mut HdfReader<R>,
info: &DatasetInfo,
chunk_ctx: &ChunkReadContext<'_>,
output: &mut [u8],
) -> Result<()> {
if output.len() != chunk_ctx.total_bytes {
return Err(Error::InvalidFormat(format!(
"v1 B-tree chunk output buffer has {} bytes, expected {}",
output.len(),
chunk_ctx.total_bytes
)));
}
let ndims = chunk_ctx.data_dims.len();
let chunk_records = Self::collect_btree_v1_chunks(reader, chunk_ctx.idx_addr, ndims)?;
if !Self::has_full_chunk_coverage_1d(&chunk_records, chunk_ctx)? {
Self::filled_data_into(
chunk_ctx.total_bytes / chunk_ctx.element_size,
chunk_ctx.element_size,
info,
output,
)?;
}
let mut compressed_scratch = Vec::new();
let mut shuffle_scratch = Vec::new();
let mut raw_scratch = Vec::new();
let mut scaled_scratch = Vec::new();
let mut handled = if Self::btree_v1_uses_unfiltered_coalescing(info) {
Self::try_read_coalesced_borrowed_unfiltered_chunks_1d(
reader,
info,
chunk_ctx,
chunk_records.len(),
chunk_records.iter().map(|record| {
Ok(BorrowedChunkPayloadRead {
coords: &record.coords,
addr: record.chunk_addr,
read_size: usize_from_u64(record.chunk_size, "v1 B-tree chunk size")?,
filter_mask: record.filter_mask,
})
}),
output,
)?
} else {
Vec::new()
};
if handled.is_empty() {
handled.resize(chunk_records.len(), false);
}
Self::try_read_parallel_deflate_btree_v1_chunks_1d(
reader,
info,
chunk_ctx,
&chunk_records,
output,
&mut handled,
)?;
for (chunk_index, chunk_record) in chunk_records.iter().enumerate() {
if handled.get(chunk_index).copied().unwrap_or(false) {
continue;
}
if Self::try_read_full_chunk_1d_into_output(
reader,
info,
chunk_ctx,
&chunk_record.coords,
chunk_record.chunk_addr,
usize_from_u64(chunk_record.chunk_size, "v1 B-tree chunk size")?,
chunk_record.filter_mask,
output,
&mut compressed_scratch,
&mut shuffle_scratch,
)? {
continue;
}
Self::process_btree_v1_chunk_record(
reader,
chunk_ctx.idx_addr,
chunk_record,
info,
chunk_ctx.data_dims,
chunk_ctx.chunk_dims,
chunk_ctx.chunk_bytes,
chunk_ctx.element_size,
output,
&mut raw_scratch,
&mut scaled_scratch,
)?;
}
Ok(())
}
fn btree_v1_uses_unfiltered_coalescing(info: &DatasetInfo) -> bool {
info.filter_pipeline
.as_ref()
.map(|pipeline| pipeline.filters.is_empty())
.unwrap_or(true)
}
fn try_read_parallel_deflate_btree_v1_chunks_1d<R: Read + Seek>(
reader: &mut HdfReader<R>,
info: &DatasetInfo,
chunk_ctx: &ChunkReadContext<'_>,
chunk_records: &[ChunkBTreeRecord],
output: &mut [u8],
handled: &mut [bool],
) -> Result<()> {
let Some(pipeline) = info.filter_pipeline.as_ref() else {
return Ok(());
};
if !Self::is_deflate_only_pipeline(pipeline) || chunk_ctx.data_dims.len() != 1 {
return Ok(());
}
if chunk_ctx.chunk_dims.len() != 1 || output.len() != chunk_ctx.total_bytes {
return Ok(());
}
let data_size = usize_from_u64(chunk_ctx.data_dims[0], "dataset dimension")?;
let chunk_size = usize_from_u64(chunk_ctx.chunk_dims[0], "chunk dimension")?;
if chunk_size == 0 {
return Ok(());
}
let full_chunk_count = data_size / chunk_size;
if full_chunk_count < 2 || chunk_records.len() < full_chunk_count {
return Ok(());
}
let chunk_bytes = chunk_size
.checked_mul(chunk_ctx.element_size)
.ok_or_else(|| Error::InvalidFormat("chunk byte size overflow".into()))?;
if chunk_bytes == 0 || chunk_bytes != chunk_ctx.chunk_bytes {
return Ok(());
}
let full_output_len = full_chunk_count
.checked_mul(chunk_bytes)
.ok_or_else(|| Error::InvalidFormat("parallel chunk output length overflow".into()))?;
if full_output_len > output.len() {
return Ok(());
}
if full_chunk_count < MIN_PARALLEL_DEFLATE_CHUNKS_1D
|| full_output_len < MIN_PARALLEL_DEFLATE_BYTES_1D
{
return Ok(());
}
let worker_count = super::support::parallel_deflate_worker_count(full_chunk_count);
if worker_count <= 1 {
return Ok(());
}
let mut payloads = Vec::with_capacity(full_chunk_count);
for (chunk_index, record) in chunk_records.iter().take(full_chunk_count).enumerate() {
if handled.get(chunk_index).copied().unwrap_or(false) {
return Ok(());
}
if record.filter_mask != 0
|| record.coords.len() != 1
|| crate::io::reader::is_undef_addr(record.chunk_addr)
{
return Ok(());
}
let expected_start = chunk_index
.checked_mul(chunk_size)
.ok_or_else(|| Error::InvalidFormat("chunk coordinate overflow".into()))?;
if usize_from_u64(record.coords[0], "chunk coordinate")? != expected_start {
return Ok(());
}
let read_size = usize_from_u64(record.chunk_size, "v1 B-tree chunk size")?;
let mut payload = vec![0u8; read_size];
reader.seek(record.chunk_addr).map_err(|err| {
Error::InvalidFormat(format!(
"failed to seek to v1 B-tree chunk {chunk_index} at address {}: {err}",
record.chunk_addr
))
})?;
reader.read_bytes_into(&mut payload).map_err(|err| {
Error::InvalidFormat(format!(
"failed to read v1 B-tree chunk {chunk_index} at address {} with size {}: {err}",
record.chunk_addr, record.chunk_size
))
})?;
payloads.push(payload);
}
let chunks_per_worker = full_chunk_count.div_ceil(worker_count);
let full_output = &mut output[..full_output_len];
let parallel_result: Result<()> = thread::scope(|scope| {
let mut handles = Vec::new();
let mut remaining_output = full_output;
let mut payload_start = 0usize;
while payload_start < full_chunk_count {
let payload_end = (payload_start + chunks_per_worker).min(full_chunk_count);
let group_chunks = payload_end - payload_start;
let group_bytes = group_chunks * chunk_bytes;
let (group_output, next_output) = remaining_output.split_at_mut(group_bytes);
let group_payloads = &payloads[payload_start..payload_end];
handles.push(scope.spawn(move || -> Result<()> {
for (chunk_offset, payload) in group_payloads.iter().enumerate() {
let start = chunk_offset * chunk_bytes;
let end = start + chunk_bytes;
crate::filters::deflate::decompress_exact_into(
payload,
&mut group_output[start..end],
)?;
}
Ok(())
}));
remaining_output = next_output;
payload_start = payload_end;
}
for handle in handles {
handle.join().map_err(|_| {
Error::InvalidFormat("parallel deflate worker panicked".into())
})??;
}
Ok(())
});
parallel_result?;
super::support::record_parallel_deflate_chunks_handled(full_chunk_count);
for flag in handled.iter_mut().take(full_chunk_count) {
*flag = true;
}
Ok(())
}
#[cfg(feature = "tracehash")]
fn trace_btree1_chunk_lookup(
index_addr: u64,
scaled: &[u64],
addr: u64,
nbytes: u64,
filter_mask: u32,
) {
let mut th = tracehash::th_call!("hdf5.chunk_index.btree1.lookup");
th.input_u64(index_addr);
for coord in scaled {
th.input_u64(*coord);
}
th.output_value(&(true));
th.output_u64(addr);
th.output_u64(if crate::io::reader::is_undef_addr(addr) {
0
} else {
nbytes
});
th.output_u64(u64::from(filter_mask));
th.finish();
}
#[cfg(not(feature = "tracehash"))]
fn trace_btree1_chunk_lookup(
_index_addr: u64,
_scaled: &[u64],
_addr: u64,
_nbytes: u64,
_filter_mask: u32,
) {
}
fn scaled_chunk_coords_into(
coords: &[u64],
chunk_dims: &[u64],
out: &mut Vec<u64>,
) -> Result<()> {
if coords.len() != chunk_dims.len() {
return Err(Error::InvalidFormat(
"chunk coordinate rank does not match chunk dimensions".into(),
));
}
let mut next = Vec::new();
next.try_reserve_exact(coords.len()).map_err(|err| {
Error::InvalidFormat(format!("scaled chunk coordinate allocation failed: {err}"))
})?;
for (&coord, &dim) in coords.iter().zip(chunk_dims) {
if dim == 0 {
return Err(Error::InvalidFormat("chunk dimension is zero".into()));
}
if coord % dim != 0 {
return Err(Error::InvalidFormat(
"chunk coordinate is not aligned to chunk dimension".into(),
));
}
next.push(coord / dim);
}
*out = next;
Ok(())
}
fn decode_chunk_btree_lookup_node<R: Read + Seek>(
reader: &mut HdfReader<R>,
addr: u64,
data_ndims: usize,
key_dims: &ChunkBTreeKeyDims<'_>,
) -> Result<ChunkBTreeLookupNode> {
reader.seek(addr)?;
let mut magic = [0u8; 4];
reader.read_bytes_into(&mut magic)?;
if magic != [b'T', b'R', b'E', b'E'] {
return Err(Error::InvalidFormat("invalid chunk B-tree magic".into()));
}
let node_type = reader.read_u8()?;
if node_type != 1 {
return Err(Error::InvalidFormat(format!(
"expected raw data B-tree (type 1), got type {node_type}"
)));
}
let level = reader.read_u8()?;
let entries_used = usize::from(reader.read_u16()?);
let _left_sibling = reader.read_addr()?;
let _right_sibling = reader.read_addr()?;
let mut children = Vec::with_capacity(entries_used);
for _ in 0..entries_used {
let key = Self::decode_chunk_btree_key(reader, data_ndims, key_dims)?;
let child_addr = reader.read_addr()?;
if crate::io::reader::is_undef_addr(child_addr) {
return Err(Error::InvalidFormat(
"v1 chunk B-tree child address is undefined".into(),
));
}
children.push(ChunkBTreeChild { key, child_addr });
}
let final_key = Self::decode_chunk_btree_key(reader, data_ndims, key_dims)?;
Ok(ChunkBTreeLookupNode {
level,
children,
final_key,
})
}
fn decode_chunk_btree_key<R: Read + Seek>(
reader: &mut HdfReader<R>,
data_ndims: usize,
key_dims: &ChunkBTreeKeyDims<'_>,
) -> Result<ChunkBTreeKey> {
if key_dims.chunk_dims.len() != data_ndims {
return Err(Error::InvalidFormat(
"v1 B-tree key dimension mismatch".into(),
));
}
let nbytes = u64::from(reader.read_u32()?);
let filter_mask = reader.read_u32()?;
let mut scaled = Vec::with_capacity(data_ndims + 1);
for dim_index in 0..data_ndims {
let chunk_dim = key_dims.chunk_dims[dim_index];
if chunk_dim == 0 {
return Err(Error::InvalidFormat(format!(
"chunk size must be > 0, dim = {dim_index}"
)));
}
let offset = reader.read_u64()?;
if offset % chunk_dim != 0 {
return Err(Error::InvalidFormat(
"bad v1 B-tree coordinate offset".into(),
));
}
scaled.push(offset / chunk_dim);
}
let element_size = u64_from_usize(key_dims.element_size, "datatype size")?;
if element_size == 0 {
return Err(Error::InvalidFormat("datatype size is zero".into()));
}
let extra_offset = reader.read_u64()?;
if extra_offset % element_size != 0 {
return Err(Error::InvalidFormat(
"bad v1 B-tree datatype-size coordinate offset".into(),
));
}
scaled.push(extra_offset / element_size);
Ok(ChunkBTreeKey {
scaled,
nbytes,
filter_mask,
})
}
fn btree_v1_cmp3(lt_key: &ChunkBTreeKey, target_scaled: &[u64], rt_key: &ChunkBTreeKey) -> i32 {
if target_scaled.len() == 2 {
if target_scaled[0] > rt_key.scaled[0] {
1
} else if target_scaled[0] == rt_key.scaled[0] && target_scaled[1] >= rt_key.scaled[1] {
1
} else if target_scaled[0] < lt_key.scaled[0] {
-1
} else {
0
}
} else if vector_ge(target_scaled, &rt_key.scaled) {
1
} else if vector_lt(target_scaled, <_key.scaled) {
-1
} else {
0
}
}
fn found_btree_v1_chunk(
child: &ChunkBTreeChild,
data_ndims: usize,
key_dims: &ChunkBTreeKeyDims<'_>,
target_scaled: &[u64],
) -> Result<Option<ChunkBTreeRecord>> {
for (target, left) in target_scaled.iter().zip(&child.key.scaled) {
if *target >= left.saturating_add(1) {
return Ok(None);
}
}
if child.key.nbytes == 0 {
return Err(Error::InvalidFormat(
"v1 B-tree chunk has zero byte size".into(),
));
}
let mut coords = Vec::with_capacity(data_ndims);
for dim_index in 0..data_ndims {
coords.push(
child.key.scaled[dim_index]
.checked_mul(key_dims.chunk_dims[dim_index])
.ok_or_else(|| {
Error::InvalidFormat("v1 B-tree chunk coordinate overflow".into())
})?,
);
}
Ok(Some(ChunkBTreeRecord {
coords,
chunk_addr: child.child_addr,
chunk_size: child.key.nbytes,
filter_mask: child.key.filter_mask,
}))
}
fn decode_chunk_btree_node<R: Read + Seek>(
reader: &mut HdfReader<R>,
addr: u64,
ndims: usize,
) -> Result<ChunkBTreeNode> {
reader.seek(addr)?;
let mut magic = [0u8; 4];
reader.read_bytes_into(&mut magic)?;
if magic != [b'T', b'R', b'E', b'E'] {
return Err(Error::InvalidFormat("invalid chunk B-tree magic".into()));
}
let node_type = reader.read_u8()?;
if node_type != 1 {
return Err(Error::InvalidFormat(format!(
"expected raw data B-tree (type 1), got type {node_type}"
)));
}
let level = reader.read_u8()?;
let entries_used = usize::from(reader.read_u16()?);
let _left_sibling = reader.read_addr()?;
let _right_sibling = reader.read_addr()?;
if level == 0 {
let mut records = Vec::with_capacity(entries_used);
for _ in 0..entries_used {
records.push(Self::decode_chunk_btree_leaf_record(reader, ndims)?);
}
Self::skip_chunk_btree_final_key(reader, ndims)?;
Ok(ChunkBTreeNode::Leaf { level, records })
} else {
let mut child_addrs = Vec::with_capacity(entries_used);
for _ in 0..entries_used {
child_addrs.push(Self::decode_chunk_btree_child_addr(reader, ndims)?);
}
Self::skip_chunk_btree_final_key(reader, ndims)?;
Ok(ChunkBTreeNode::Internal { level, child_addrs })
}
}
pub(super) fn collect_btree_v1_chunks<R: Read + Seek>(
reader: &mut HdfReader<R>,
addr: u64,
ndims: usize,
) -> Result<Vec<ChunkBTreeRecord>> {
let mut visited = Vec::new();
Self::collect_btree_v1_chunks_inner(reader, addr, ndims, 0, None, &mut visited)
}
fn collect_btree_v1_chunks_inner<R: Read + Seek>(
reader: &mut HdfReader<R>,
addr: u64,
ndims: usize,
depth: usize,
expected_level: Option<u8>,
visited: &mut Vec<u64>,
) -> Result<Vec<ChunkBTreeRecord>> {
if depth > MAX_CHUNK_BTREE_V1_RECURSION {
return Err(Error::InvalidFormat(
"v1 chunk B-tree recursion depth exceeded".into(),
));
}
if crate::io::reader::is_undef_addr(addr) {
return Err(Error::InvalidFormat(
"v1 chunk B-tree node address is undefined".into(),
));
}
if visited.contains(&addr) {
return Err(Error::InvalidFormat(
"v1 chunk B-tree traversal cycle detected".into(),
));
}
visited.push(addr);
let node = Self::decode_chunk_btree_node(reader, addr, ndims)?;
let node_level = match &node {
ChunkBTreeNode::Leaf { level, .. } | ChunkBTreeNode::Internal { level, .. } => *level,
};
if let Some(expected_level) = expected_level {
if node_level != expected_level {
visited.pop();
return Err(Error::InvalidFormat(format!(
"v1 chunk B-tree child level {node_level} does not match expected {expected_level}"
)));
}
}
let result = match node {
ChunkBTreeNode::Leaf { records, .. } => Ok(records),
ChunkBTreeNode::Internal { level, child_addrs } => {
let mut all_chunks = Vec::new();
for child_addr in child_addrs {
let mut child_chunks = Self::collect_btree_v1_chunks_inner(
reader,
child_addr,
ndims,
depth.checked_add(1).ok_or_else(|| {
Error::InvalidFormat("v1 chunk B-tree recursion depth overflow".into())
})?,
Some(level - 1),
visited,
)?;
all_chunks.append(&mut child_chunks);
}
Ok(all_chunks)
}
};
visited.pop();
result
}
fn decode_chunk_btree_leaf_record<R: Read + Seek>(
reader: &mut HdfReader<R>,
ndims: usize,
) -> Result<ChunkBTreeRecord> {
let chunk_size = u64::from(reader.read_u32()?);
let filter_mask = reader.read_u32()?;
let mut coords = Vec::with_capacity(ndims);
for _ in 0..ndims {
coords.push(reader.read_u64()?);
}
let _extra = reader.read_u64()?;
let chunk_addr = reader.read_addr()?;
Ok(ChunkBTreeRecord {
coords,
chunk_addr,
chunk_size,
filter_mask,
})
}
fn decode_chunk_btree_child_addr<R: Read + Seek>(
reader: &mut HdfReader<R>,
ndims: usize,
) -> Result<u64> {
let _chunk_size = reader.read_u32()?;
let _filter_mask = reader.read_u32()?;
for _ in 0..=ndims {
let _ = reader.read_u64()?;
}
let child_addr = reader.read_addr()?;
if crate::io::reader::is_undef_addr(child_addr) {
return Err(Error::InvalidFormat(
"v1 chunk B-tree child address is undefined".into(),
));
}
Ok(child_addr)
}
fn skip_chunk_btree_final_key<R: Read + Seek>(
reader: &mut HdfReader<R>,
ndims: usize,
) -> Result<()> {
let _final_chunk_size = reader.read_u32()?;
let _final_filter_mask = reader.read_u32()?;
for _ in 0..=ndims {
let _ = reader.read_u64()?;
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn process_btree_v1_chunk_record<R: Read + Seek>(
reader: &mut HdfReader<R>,
btree_addr: u64,
chunk_record: &ChunkBTreeRecord,
info: &DatasetInfo,
data_dims: &[u64],
chunk_dims: &[u64],
chunk_bytes: usize,
element_size: usize,
output: &mut [u8],
raw_scratch: &mut Vec<u8>,
scaled_scratch: &mut Vec<u64>,
) -> Result<()> {
Self::scaled_chunk_coords_into(&chunk_record.coords, chunk_dims, scaled_scratch)?;
Self::trace_btree1_chunk_lookup(
btree_addr,
scaled_scratch,
chunk_record.chunk_addr,
chunk_record.chunk_size,
chunk_record.filter_mask,
);
if crate::io::reader::is_undef_addr(chunk_record.chunk_addr) {
return Ok(());
}
let raw = Self::read_btree_v1_chunk_payload_into_scratch(
reader,
chunk_record,
info,
chunk_bytes,
element_size,
raw_scratch,
)?;
Self::copy_chunk_to_output(
raw,
&chunk_record.coords,
data_dims,
chunk_dims,
element_size,
output,
)
}
fn read_btree_v1_chunk_payload_into_scratch<'a, R: Read + Seek>(
reader: &mut HdfReader<R>,
chunk_record: &ChunkBTreeRecord,
info: &DatasetInfo,
chunk_bytes: usize,
element_size: usize,
raw_scratch: &'a mut Vec<u8>,
) -> Result<&'a [u8]> {
reader.seek(chunk_record.chunk_addr)?;
let read_size = usize_from_u64(chunk_record.chunk_size, "v1 B-tree chunk size")?;
raw_scratch.resize(read_size, 0);
reader.read_bytes_into(raw_scratch)?;
if let Some(ref pipeline) = info.filter_pipeline {
if !pipeline.filters.is_empty() {
*raw_scratch = filters::apply_pipeline_reverse_with_mask_expected(
raw_scratch,
pipeline,
element_size,
chunk_record.filter_mask,
chunk_bytes,
)?;
}
}
Ok(raw_scratch)
}
}
fn vector_ge(left: &[u64], right: &[u64]) -> bool {
for (&left_value, &right_value) in left.iter().zip(right) {
if left_value > right_value {
return true;
}
if left_value < right_value {
return false;
}
}
left.len() >= right.len()
}
fn vector_lt(left: &[u64], right: &[u64]) -> bool {
for (&left_value, &right_value) in left.iter().zip(right) {
if left_value < right_value {
return true;
}
if left_value > right_value {
return false;
}
}
left.len() < right.len()
}