use crate::utils_common::bitmanip::{BitManip as _, UBitManip as _};
use core::ops;
#[derive(Debug)]
pub enum ChunkedIoRegionError {
ChunkSizeOverflow,
InvalidBounds,
ChunkIndexOverflow,
RegionUnaligned,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct ChunkedIoRegion {
physical_begin_128b: u64,
physical_end_128b: u64,
chunk_size_128b_log2: u32,
chunk_index_offset: usize,
}
impl ChunkedIoRegion {
pub fn new(
physical_begin_128b: u64,
physical_end_128b: u64,
chunk_size_128b_log2: u32,
) -> Result<Self, ChunkedIoRegionError> {
if chunk_size_128b_log2 >= usize::BITS - 7 || chunk_size_128b_log2 >= u64::BITS {
Err(ChunkedIoRegionError::ChunkSizeOverflow)
} else if physical_end_128b < physical_begin_128b {
Err(ChunkedIoRegionError::InvalidBounds)
} else if usize::try_from((physical_end_128b - physical_begin_128b) >> chunk_size_128b_log2).is_err() {
Err(ChunkedIoRegionError::ChunkIndexOverflow)
} else if !(physical_end_128b - physical_begin_128b).is_aligned_pow2(chunk_size_128b_log2) {
Err(ChunkedIoRegionError::RegionUnaligned)
} else {
Ok(Self {
physical_begin_128b,
physical_end_128b,
chunk_size_128b_log2,
chunk_index_offset: 0,
})
}
}
pub fn is_aligned(&self, alignment_128b_log2: u32) -> bool {
if alignment_128b_log2 >= u64::BITS {
false
} else {
self.physical_begin_128b.is_aligned_pow2(alignment_128b_log2)
&& self.physical_end_128b.is_aligned_pow2(alignment_128b_log2)
}
}
pub fn max_aligned_128b_log2(&self) -> u32 {
(self.physical_begin_128b | self.physical_end_128b)
.trailing_zeros()
.min(u64::BITS - 1)
}
pub fn align_to(&self, alignment_128b_log2: u32) -> (Self, Option<(Self, Self)>) {
if alignment_128b_log2 >= u64::BITS {
return (self.clone(), None);
}
let aligned_physical_end_128b = self.physical_end_128b.round_down_pow2(alignment_128b_log2);
let aligned_physical_begin_128b = self
.physical_begin_128b
.round_up_pow2(alignment_128b_log2)
.unwrap_or(aligned_physical_end_128b);
if aligned_physical_end_128b <= aligned_physical_begin_128b {
return (self.clone(), None);
}
let chunk_index_offset = self.chunk_index_offset;
let unaligned_head = Self {
physical_begin_128b: self.physical_begin_128b,
physical_end_128b: aligned_physical_begin_128b,
chunk_size_128b_log2: self.chunk_size_128b_log2,
chunk_index_offset,
};
let chunk_index_offset = chunk_index_offset + unaligned_head.chunks_count();
let aligned = Self {
physical_begin_128b: aligned_physical_begin_128b,
physical_end_128b: aligned_physical_end_128b,
chunk_size_128b_log2: self.chunk_size_128b_log2,
chunk_index_offset,
};
let chunk_index_offset = chunk_index_offset + aligned.chunks_count();
let unaligned_tail = Self {
physical_begin_128b: aligned_physical_end_128b,
physical_end_128b: self.physical_end_128b,
chunk_size_128b_log2: self.chunk_size_128b_log2,
chunk_index_offset,
};
(unaligned_head, Some((aligned, unaligned_tail)))
}
pub fn split_at(&self, split_pos_chunk_offset: usize) -> Result<(Self, Self), ChunkedIoRegionError> {
let chunk_index_offset = self.chunk_index_offset;
if split_pos_chunk_offset < chunk_index_offset
|| split_pos_chunk_offset - chunk_index_offset > self.chunks_count()
{
return Err(ChunkedIoRegionError::InvalidBounds);
}
let physical_split_pos_128b = self.physical_begin_128b
+ (((split_pos_chunk_offset - chunk_index_offset) as u64) << self.chunk_size_128b_log2);
let head = Self {
physical_begin_128b: self.physical_begin_128b,
physical_end_128b: physical_split_pos_128b,
chunk_size_128b_log2: self.chunk_size_128b_log2,
chunk_index_offset,
};
debug_assert_eq!(head.chunks_count(), split_pos_chunk_offset - chunk_index_offset);
let tail = Self {
physical_begin_128b: physical_split_pos_128b,
physical_end_128b: self.physical_end_128b,
chunk_size_128b_log2: self.chunk_size_128b_log2,
chunk_index_offset: split_pos_chunk_offset,
};
Ok((head, tail))
}
pub fn is_empty(&self) -> bool {
self.physical_end_128b == self.physical_begin_128b
}
pub fn chunks_index_offset(&self) -> usize {
self.chunk_index_offset
}
pub fn chunks_count(&self) -> usize {
let chunks = (self.physical_end_128b - self.physical_begin_128b) >> self.chunk_size_128b_log2;
usize::try_from(chunks).unwrap()
}
pub fn chunk_size_128b_log2(&self) -> u32 {
self.chunk_size_128b_log2
}
pub fn aligned_blocks_iter(
&self,
block_size_128b_log2: u32,
) -> Result<ChunkedIoRegionAlignedBlocksIterator<'_>, ChunkedIoRegionError> {
if !self.is_aligned(block_size_128b_log2) {
return Err(ChunkedIoRegionError::RegionUnaligned);
}
Ok(ChunkedIoRegionAlignedBlocksIterator {
region: self,
block_size_128b_log2,
physical_pos_128b: self.physical_begin_128b,
})
}
}
#[derive(Clone, Copy)]
pub struct ChunkedIoRegionChunkIndex {
chunk_index: usize,
}
impl ChunkedIoRegionChunkIndex {
pub fn decompose_to_hierarchic_indices<const N: usize>(
&self,
hierarchy_children_log2: [u32; N],
) -> (usize, [usize; N]) {
let mut hierarchic_indices = [0usize; N];
let mut index = self.chunk_index;
for i in (0..N).rev() {
let children_log2 = hierarchy_children_log2[i];
if children_log2 >= usize::BITS {
hierarchic_indices[i] = index;
return (0, hierarchic_indices);
}
let mask = usize::trailing_bits_mask(children_log2);
hierarchic_indices[i] = index & mask;
index >>= children_log2;
}
(index, hierarchic_indices)
}
}
#[derive(Clone)]
pub struct ChunkedIoRegionChunkRange {
chunk: ChunkedIoRegionChunkIndex,
range: ops::Range<usize>,
}
impl ChunkedIoRegionChunkRange {
pub fn chunk(&self) -> ChunkedIoRegionChunkIndex {
self.chunk
}
pub fn range_in_chunk(&self) -> &ops::Range<usize> {
&self.range
}
}
pub struct ChunkedIoRegionAlignedBlocksIterator<'a> {
region: &'a ChunkedIoRegion,
block_size_128b_log2: u32,
physical_pos_128b: u64,
}
impl<'a> Iterator for ChunkedIoRegionAlignedBlocksIterator<'a> {
type Item = (u64, ChunkedIoRegionAlignedBlockChunksRangesIterator<'a>);
fn next(&mut self) -> Option<Self::Item> {
if self.physical_pos_128b == self.region.physical_end_128b {
None
} else {
let cur_physical_pos_128b = self.physical_pos_128b;
self.physical_pos_128b += u64::exp2(self.block_size_128b_log2);
let physical_block_index = cur_physical_pos_128b >> self.block_size_128b_log2;
Some((
physical_block_index,
ChunkedIoRegionAlignedBlockChunksRangesIterator {
region: self.region,
block_size_128b_log2: self.block_size_128b_log2,
physical_pos_128b: Some(cur_physical_pos_128b),
},
))
}
}
}
pub struct ChunkedIoRegionAlignedBlockChunksRangesIterator<'a> {
region: &'a ChunkedIoRegion,
block_size_128b_log2: u32,
physical_pos_128b: Option<u64>,
}
impl<'a> Iterator for ChunkedIoRegionAlignedBlockChunksRangesIterator<'a> {
type Item = (u64, ChunkedIoRegionChunkRange);
fn next(&mut self) -> Option<Self::Item> {
let cur_physical_pos_128b = self.physical_pos_128b?;
let block_chunk_range_size_128b_log2 = self.block_size_128b_log2.min(self.region.chunk_size_128b_log2);
let block_chunk_range_size_128b = u64::exp2(block_chunk_range_size_128b_log2);
debug_assert!(self.block_size_128b_log2 < u64::BITS);
let next_pos_128b = cur_physical_pos_128b + block_chunk_range_size_128b;
let offset_in_block_mask_128b = u64::trailing_bits_mask(self.block_size_128b_log2);
self.physical_pos_128b = if next_pos_128b & offset_in_block_mask_128b != 0 {
Some(next_pos_128b)
} else {
None
};
let offset_in_block_128b = cur_physical_pos_128b & offset_in_block_mask_128b;
let chunk_index = (cur_physical_pos_128b - self.region.physical_begin_128b) >> self.region.chunk_size_128b_log2;
let chunk_index = usize::try_from(chunk_index).unwrap() + self.region.chunk_index_offset;
let block_chunk_range_begin_128b = (cur_physical_pos_128b - self.region.physical_begin_128b)
& u64::trailing_bits_mask(self.region.chunk_size_128b_log2);
let block_chunk_range_end_128b = block_chunk_range_begin_128b + block_chunk_range_size_128b;
debug_assert!(block_chunk_range_end_128b <= u64::exp2(self.region.chunk_size_128b_log2));
let block_chunk_range_begin_128b = usize::try_from(block_chunk_range_begin_128b).unwrap();
let block_chunk_range_end_128b = usize::try_from(block_chunk_range_end_128b).unwrap();
let block_chunk_range_begin = block_chunk_range_begin_128b.checked_shl(7).unwrap();
let block_chunk_range_end = block_chunk_range_end_128b.checked_shl(7).unwrap();
let block_chunk_range = block_chunk_range_begin..block_chunk_range_end;
Some((
offset_in_block_128b,
ChunkedIoRegionChunkRange {
chunk: ChunkedIoRegionChunkIndex { chunk_index },
range: block_chunk_range,
},
))
}
}