use crate::blocks::CompressionType;
#[derive(Debug, Clone)]
pub struct DataBlockInfo {
pub offset: u64,
pub block_type: DataBlockType,
pub original_size: u64,
pub compressed_size: u64,
pub compression: Option<CompressionInfo>,
pub invalidation_block: Option<Box<DataBlockInfo>>,
pub data_bytes: u32,
pub inval_bytes: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DataBlockType {
Data,
SortedData,
Compressed,
Reduction,
DataValues,
DataInvalidation,
}
impl DataBlockType {
pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
match bytes {
b"DT" => Some(DataBlockType::Data),
b"SD" => Some(DataBlockType::SortedData),
b"DZ" => Some(DataBlockType::Compressed),
b"RD" => Some(DataBlockType::Reduction),
b"DV" => Some(DataBlockType::DataValues),
b"DI" => Some(DataBlockType::DataInvalidation),
_ => None,
}
}
pub fn as_bytes(&self) -> &'static [u8; 2] {
match self {
DataBlockType::Data => b"DT",
DataBlockType::SortedData => b"SD",
DataBlockType::Compressed => b"DZ",
DataBlockType::Reduction => b"RD",
DataBlockType::DataValues => b"DV",
DataBlockType::DataInvalidation => b"DI",
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct CompressionInfo {
pub algorithm: CompressionType,
pub parameter: u32,
pub data_offset: u64,
}
impl DataBlockInfo {
pub fn uncompressed(offset: u64, block_type: DataBlockType, size: u64) -> Self {
Self {
offset,
block_type,
original_size: size,
compressed_size: size,
compression: None,
invalidation_block: None,
data_bytes: 0,
inval_bytes: 0,
}
}
pub fn compressed(
offset: u64,
original_size: u64,
compressed_size: u64,
compression: CompressionInfo,
) -> Self {
Self {
offset,
block_type: DataBlockType::Compressed,
original_size,
compressed_size,
compression: Some(compression),
invalidation_block: None,
data_bytes: 0,
inval_bytes: 0,
}
}
pub fn effective_size(&self) -> u64 {
if let Some(ref inval) = self.invalidation_block {
self.original_size + inval.original_size
} else {
self.original_size
}
}
pub fn is_compressed(&self) -> bool {
self.compression.is_some()
}
pub fn compression_ratio(&self) -> f64 {
if self.original_size == 0 {
1.0
} else {
self.compressed_size as f64 / self.original_size as f64
}
}
}
#[derive(Debug, Clone, Default)]
pub struct DataBlockIndex {
blocks: Vec<DataBlockInfo>,
cumulative_offsets: Vec<u64>,
total_size: u64,
}
impl DataBlockIndex {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(block_count: usize) -> Self {
Self {
blocks: Vec::with_capacity(block_count),
cumulative_offsets: Vec::with_capacity(block_count),
total_size: 0,
}
}
pub fn push(&mut self, info: DataBlockInfo) {
let size = info.effective_size();
self.cumulative_offsets.push(self.total_size);
self.total_size += size;
self.blocks.push(info);
}
pub fn block_count(&self) -> usize {
self.blocks.len()
}
pub fn is_empty(&self) -> bool {
self.blocks.is_empty()
}
pub fn total_size(&self) -> u64 {
self.total_size
}
pub fn blocks(&self) -> &[DataBlockInfo] {
&self.blocks
}
pub fn block_for_offset(&self, global_offset: u64) -> Option<(usize, &DataBlockInfo, u64)> {
if global_offset >= self.total_size {
return None;
}
let block_idx = match self.cumulative_offsets.binary_search(&global_offset) {
Ok(idx) => idx,
Err(idx) => idx.saturating_sub(1),
};
let block = &self.blocks[block_idx];
let local_offset = global_offset - self.cumulative_offsets[block_idx];
Some((block_idx, block, local_offset))
}
pub fn iter(&self) -> impl Iterator<Item = (u64, &DataBlockInfo)> {
self.cumulative_offsets
.iter()
.zip(self.blocks.iter())
.map(|(&offset, info)| (offset, info))
}
pub fn average_compression_ratio(&self) -> f64 {
if self.blocks.is_empty() {
return 1.0;
}
let total_compressed: u64 = self.blocks.iter().map(|b| b.compressed_size).sum();
let total_original: u64 = self.blocks.iter().map(|b| b.original_size).sum();
if total_original == 0 {
1.0
} else {
total_compressed as f64 / total_original as f64
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ChannelLayout {
pub byte_offset: usize,
pub byte_size: usize,
pub bit_offset: u8,
pub bit_count: u32,
pub is_signed: bool,
pub is_float: bool,
pub little_endian: bool,
}
impl ChannelLayout {
pub fn new(
byte_offset: u32,
bit_offset: u8,
bit_count: u32,
is_signed: bool,
is_float: bool,
little_endian: bool,
) -> Self {
Self {
byte_offset: byte_offset as usize,
byte_size: (bit_offset as u32 + bit_count).div_ceil(8) as usize,
bit_offset,
bit_count,
is_signed,
is_float,
little_endian,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_data_block_info_uncompressed() {
let info = DataBlockInfo::uncompressed(1000, DataBlockType::Data, 4096);
assert_eq!(info.offset, 1000);
assert_eq!(info.original_size, 4096);
assert_eq!(info.compressed_size, 4096);
assert!(!info.is_compressed());
assert_eq!(info.compression_ratio(), 1.0);
}
#[test]
fn test_data_block_info_compressed() {
let compression = CompressionInfo {
algorithm: CompressionType::Deflate,
parameter: 0,
data_offset: 1024,
};
let info = DataBlockInfo::compressed(1000, 4096, 1024, compression);
assert!(info.is_compressed());
assert_eq!(info.compression_ratio(), 0.25);
}
#[test]
fn test_data_block_index() {
let mut index = DataBlockIndex::new();
index.push(DataBlockInfo::uncompressed(100, DataBlockType::Data, 1000));
index.push(DataBlockInfo::uncompressed(200, DataBlockType::Data, 500));
index.push(DataBlockInfo::uncompressed(300, DataBlockType::Data, 500));
assert_eq!(index.block_count(), 3);
assert_eq!(index.total_size(), 2000);
let (idx, _, local) = index.block_for_offset(0).unwrap();
assert_eq!(idx, 0);
assert_eq!(local, 0);
let (idx, _, local) = index.block_for_offset(1000).unwrap();
assert_eq!(idx, 1);
assert_eq!(local, 0);
let (idx, _, local) = index.block_for_offset(1200).unwrap();
assert_eq!(idx, 1);
assert_eq!(local, 200);
let (idx, _, local) = index.block_for_offset(1500).unwrap();
assert_eq!(idx, 2);
assert_eq!(local, 0);
assert!(index.block_for_offset(2000).is_none());
}
#[test]
fn test_data_block_type() {
assert_eq!(DataBlockType::from_bytes(b"DT"), Some(DataBlockType::Data));
assert_eq!(
DataBlockType::from_bytes(b"SD"),
Some(DataBlockType::SortedData)
);
assert_eq!(
DataBlockType::from_bytes(b"DZ"),
Some(DataBlockType::Compressed)
);
assert_eq!(
DataBlockType::from_bytes(b"RD"),
Some(DataBlockType::Reduction)
);
assert_eq!(
DataBlockType::from_bytes(b"DV"),
Some(DataBlockType::DataValues)
);
assert_eq!(
DataBlockType::from_bytes(b"DI"),
Some(DataBlockType::DataInvalidation)
);
assert_eq!(DataBlockType::from_bytes(b"XX"), None);
}
}
#[derive(Debug, Clone, Default)]
pub struct RecordIndex {
per_group: Vec<Vec<u64>>,
}
impl RecordIndex {
pub fn with_groups(group_count: usize) -> Self {
Self {
per_group: vec![Vec::new(); group_count],
}
}
pub fn push(&mut self, group: usize, offset: u64) {
if let Some(slot) = self.per_group.get_mut(group) {
slot.push(offset);
}
}
pub fn offsets(&self, group: usize) -> &[u64] {
self.per_group.get(group).map_or(&[], |v| v.as_slice())
}
pub fn count(&self, group: usize) -> usize {
self.per_group.get(group).map_or(0, |v| v.len())
}
pub fn group_count(&self) -> usize {
self.per_group.len()
}
}