use std::mem::size_of;
use crate::bounds::{checked_add, checked_capacity, checked_mul, decoded_len};
use crate::error::CodecError;
use super::block::{decode_block, skip_block};
use super::header::{GLOBAL_HEADER_SIZE, expected_block_count, parse_header, validate_frame};
pub fn block_byte_offsets(data: &[u8]) -> Result<Vec<usize>, CodecError> {
if data.len() < GLOBAL_HEADER_SIZE {
return Err(CodecError::Truncated {
expected: GLOBAL_HEADER_SIZE,
actual: data.len(),
});
}
let (total_count, num_blocks) = parse_header(data)?;
let offset_capacity = checked_capacity(num_blocks, size_of::<usize>(), "FastLanes offsets")?;
let mut offsets = Vec::with_capacity(offset_capacity);
let mut pos = GLOBAL_HEADER_SIZE;
for i in 0..num_blocks {
offsets.push(pos);
pos = skip_block(data, pos, i, expected_block_count(total_count, i)?)?;
}
if pos != data.len() {
return Err(CodecError::Corrupt {
detail: "trailing bytes after FastLanes frame".into(),
});
}
Ok(offsets)
}
pub fn decode_block_range(
data: &[u8],
start_block: usize,
end_block: usize,
) -> Result<Vec<i64>, CodecError> {
if data.len() < GLOBAL_HEADER_SIZE {
return Err(CodecError::Truncated {
expected: GLOBAL_HEADER_SIZE,
actual: data.len(),
});
}
let (total_count, num_blocks) = parse_header(data)?;
validate_frame(data, total_count, num_blocks)?;
if start_block >= num_blocks || end_block > num_blocks || start_block >= end_block {
return Ok(Vec::new());
}
let mut offset = GLOBAL_HEADER_SIZE;
for i in 0..start_block {
offset = skip_block(data, offset, i, expected_block_count(total_count, i)?)?;
}
let selected_count = (start_block..end_block).try_fold(0usize, |count, index| {
checked_add(
count,
expected_block_count(total_count, index)?,
"FastLanes range count",
)
})?;
let selected_bytes = checked_mul(selected_count, size_of::<i64>(), "FastLanes range bytes")?;
decoded_len(selected_bytes, "FastLanes range")?;
let selected_capacity = checked_capacity(selected_count, size_of::<i64>(), "FastLanes range")?;
let mut values = Vec::with_capacity(selected_capacity);
for i in start_block..end_block {
offset = decode_block(
data,
offset,
&mut values,
i,
expected_block_count(total_count, i)?,
)?;
}
Ok(values)
}
pub fn block_count(data: &[u8]) -> Result<usize, CodecError> {
if data.len() < GLOBAL_HEADER_SIZE {
return Err(CodecError::Truncated {
expected: GLOBAL_HEADER_SIZE,
actual: data.len(),
});
}
Ok(parse_header(data)?.1)
}
pub fn decode_single_block(data: &[u8], block_idx: usize) -> Result<Vec<i64>, CodecError> {
if data.len() < GLOBAL_HEADER_SIZE {
return Err(CodecError::Truncated {
expected: GLOBAL_HEADER_SIZE,
actual: data.len(),
});
}
let (total_count, num_blocks) = parse_header(data)?;
validate_frame(data, total_count, num_blocks)?;
if block_idx >= num_blocks {
return Err(CodecError::Corrupt {
detail: format!("block_idx {block_idx} >= block_count {num_blocks}"),
});
}
let mut offset = GLOBAL_HEADER_SIZE;
for i in 0..block_idx {
offset = skip_block(data, offset, i, expected_block_count(total_count, i)?)?;
}
let expected_count = expected_block_count(total_count, block_idx)?;
let value_capacity = checked_capacity(expected_count, size_of::<i64>(), "FastLanes block")?;
let mut values = Vec::with_capacity(value_capacity);
decode_block(data, offset, &mut values, block_idx, expected_count)?;
Ok(values)
}