use serde::{Deserialize, de::DeserializeOwned};
use std::borrow::Cow;
use std::collections::HashMap;
use std::hash::Hash;
use std::io::Cursor;
use std::marker::PhantomData;
use std::mem::{ManuallyDrop, MaybeUninit};
use std::ops::Deref;
use std::ptr;
use std::slice;
use std::sync::Arc;
#[cfg(all(feature = "mmap", not(target_arch = "wasm32")))]
use memmap2::Mmap;
#[cfg(all(feature = "parallel", not(target_arch = "wasm32")))]
use rayon::prelude::*;
#[cfg(not(target_arch = "wasm32"))]
use std::fs::File;
#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;
use crate::compression::CompressorRegistry;
use crate::error::{ParcodeError, Result};
use crate::format::{ChildRef, GLOBAL_HEADER_SIZE, GlobalHeader, MAGIC_BYTES, MetaByte};
use crate::rt::ParcodeLazyRef;
#[derive(Debug, Clone)]
pub enum DataSource {
#[cfg(all(feature = "mmap", not(target_arch = "wasm32")))]
Mmap(Arc<Mmap>),
Memory(Arc<Vec<u8>>),
}
impl Deref for DataSource {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
match self {
#[cfg(all(feature = "mmap", not(target_arch = "wasm32")))]
Self::Mmap(mmap) => mmap.as_ref(),
Self::Memory(vec) => vec.as_slice(),
}
}
}
pub trait ParcodeNative: Sized {
fn from_node(node: &ChunkNode<'_>) -> Result<Self>;
}
pub trait ParcodeItem: Sized + Send + Sync + 'static {
fn read_from_shard(
reader: &mut std::io::Cursor<&[u8]>,
children: &mut std::vec::IntoIter<ChunkNode<'_>>,
) -> Result<Self>;
fn read_slice_from_shard(
reader: &mut std::io::Cursor<&[u8]>,
children: &mut std::vec::IntoIter<ChunkNode<'_>>,
) -> Result<Vec<Self>> {
let start_pos = reader.position();
let len: u64 = bincode::serde::decode_from_std_read(reader, bincode::config::standard())
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
let count = usize::try_from(len)
.map_err(|_| ParcodeError::Serialization("Vector length exceeds usize".into()))?;
let mut uninit_vec: Vec<MaybeUninit<Self>> = Vec::with_capacity(count);
#[allow(unsafe_code)]
unsafe {
uninit_vec.set_len(count);
}
reader.set_position(start_pos);
let read_count = Self::read_into_slice(reader, children, &mut uninit_vec)?;
if read_count != count {
return Err(ParcodeError::Format("Mismatch in read count".into()));
}
#[allow(unsafe_code)]
let final_vec = unsafe {
let mut manual = ManuallyDrop::new(uninit_vec);
Vec::from_raw_parts(manual.as_mut_ptr() as *mut Self, count, count)
};
Ok(final_vec)
}
fn read_into_slice(
reader: &mut std::io::Cursor<&[u8]>,
children: &mut std::vec::IntoIter<ChunkNode<'_>>,
destination: &mut [MaybeUninit<Self>],
) -> Result<usize> {
let len: u64 = bincode::serde::decode_from_std_read(reader, bincode::config::standard())
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
let count = usize::try_from(len)
.map_err(|_| ParcodeError::Serialization("Shard length exceeds usize".into()))?;
if destination.len() < count {
return Err(ParcodeError::Format(format!(
"Destination buffer too small: expected {}, got {}",
count,
destination.len()
)));
}
for slot in destination.iter_mut().take(count) {
let item = Self::read_from_shard(reader, children)?;
slot.write(item);
}
Ok(count)
}
}
macro_rules! impl_primitive_parcode_item {
($($t:ty),*) => {
$(
impl ParcodeItem for $t {
fn read_from_shard(
reader: &mut std::io::Cursor<&[u8]>,
_children: &mut std::vec::IntoIter<ChunkNode<'_>>,
) -> Result<Self> {
bincode::serde::decode_from_std_read(reader, bincode::config::standard())
.map_err(|e| ParcodeError::Serialization(e.to_string()))
}
fn read_into_slice(
reader: &mut std::io::Cursor<&[u8]>,
_children: &mut std::vec::IntoIter<ChunkNode<'_>>,
destination: &mut [MaybeUninit<Self>],
) -> Result<usize> {
let len: u64 = bincode::serde::decode_from_std_read(reader, bincode::config::standard())
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
let count = usize::try_from(len).map_err(|_| ParcodeError::Format("Len overflow".into()))?;
if destination.len() < count {
return Err(ParcodeError::Format("Buffer too small".into()));
}
if std::any::TypeId::of::<Self>() == std::any::TypeId::of::<u8>() {
let pos = usize::try_from(reader.position())
.map_err(|_| ParcodeError::Format("Position overflow".into()))?;
let inner = reader.get_ref();
let src_slice = inner.get(pos..pos + count)
.ok_or_else(|| ParcodeError::Format("Unexpected EOF reading u8 blob".into()))?;
let dest_ptr = destination.as_mut_ptr() as *mut u8;
#[allow(unsafe_code)]
unsafe {
ptr::copy_nonoverlapping(src_slice.as_ptr(), dest_ptr, count);
}
reader.set_position((pos + count) as u64);
return Ok(count);
}
for slot in destination.iter_mut().take(count) {
let item: Self = bincode::serde::decode_from_std_read(reader, bincode::config::standard())
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
slot.write(item);
}
Ok(count)
}
}
)*
}
}
impl_primitive_parcode_item!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool, String
);
macro_rules! impl_primitive_parcode_native {
($($t:ty),*) => {
$(
impl ParcodeNative for $t {
fn from_node(node: &ChunkNode<'_>) -> Result<Self> {
node.decode()
}
}
)*
}
}
impl_primitive_parcode_native!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, bool, String
);
impl<T> ParcodeNative for Vec<T>
where
T: ParcodeItem,
{
fn from_node(node: &ChunkNode<'_>) -> Result<Self> {
node.decode_parallel_collection()
}
}
impl<K, V> ParcodeNative for HashMap<K, V>
where
K: DeserializeOwned + Eq + Hash + Send + Sync,
V: DeserializeOwned + Send + Sync,
{
fn from_node(node: &ChunkNode<'_>) -> Result<Self> {
let container_payload = node.read_raw()?;
if container_payload.len() < 4 {
return Ok(Self::new());
}
if node.child_count == 0 {
return node.decode();
}
let shards = node.children()?;
let mut map = Self::new();
for shard in shards {
let payload = shard.read_raw()?;
if payload.len() < 8 {
continue;
}
let count = u32::from_le_bytes(
payload
.get(0..4)
.ok_or_else(|| ParcodeError::Format("Payload too short for count".into()))?
.try_into()
.map_err(|_| ParcodeError::Format("Failed to parse count".into()))?,
) as usize;
let offsets_start = 8 + (count * 8);
let data_start = offsets_start + (count * 4);
let offsets_bytes = payload
.get(offsets_start..data_start)
.ok_or_else(|| ParcodeError::Format("Offsets out of bounds".into()))?;
for off_bytes in offsets_bytes.chunks_exact(4).take(count) {
let offset = u32::from_le_bytes(
off_bytes
.try_into()
.map_err(|_| ParcodeError::Format("Failed to parse offset".into()))?,
) as usize;
let data_slice = payload
.get(data_start + offset..)
.ok_or_else(|| ParcodeError::Format("Data slice out of bounds".into()))?;
let (k, v) =
bincode::serde::decode_from_slice(data_slice, bincode::config::standard())
.map_err(|e| ParcodeError::Serialization(e.to_string()))?
.0;
map.insert(k, v);
}
}
Ok(map)
}
}
#[derive(Debug)]
pub struct ParcodeFile {
source: DataSource,
header: GlobalHeader,
file_size: u64,
registry: CompressorRegistry,
}
impl ParcodeFile {
#[cfg(not(target_arch = "wasm32"))]
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
#[cfg(feature = "mmap")]
{
let file = File::open(path)?;
#[allow(unsafe_code)]
let mmap = unsafe { Mmap::map(&file)? };
Self::init(DataSource::Mmap(Arc::new(mmap)), file.metadata()?.len())
}
#[cfg(not(feature = "mmap"))]
{
use std::io::Read;
let mut file = File::open(path)?;
let file_size = file.metadata()?.len();
let mut buffer =
Vec::with_capacity(usize::try_from(file_size).expect("File size too large"));
file.read_to_end(&mut buffer)?;
Self::init(DataSource::Memory(Arc::new(buffer)), file_size)
}
}
pub fn from_bytes(data: impl Into<Arc<Vec<u8>>>) -> Result<Self> {
let data_arc = data.into();
let size = data_arc.len() as u64;
Self::init(DataSource::Memory(data_arc), size)
}
fn init(source: DataSource, file_size: u64) -> Result<Self> {
if file_size < GLOBAL_HEADER_SIZE as u64 {
return Err(ParcodeError::Format("File smaller than header".into()));
}
let header_start = usize::try_from(file_size - GLOBAL_HEADER_SIZE as u64)
.map_err(|_| ParcodeError::Format("File size too large for usize".into()))?;
let header_bytes = source
.get(header_start..)
.ok_or_else(|| ParcodeError::Format("Failed to access global header".into()))?;
if header_bytes.get(0..4) != Some(&MAGIC_BYTES) {
return Err(ParcodeError::Format("Invalid Magic Bytes".into()));
}
let version = u16::from_le_bytes(
header_bytes
.get(4..6)
.ok_or_else(|| ParcodeError::Format("Version out of bounds".into()))?
.try_into()
.map_err(|_| ParcodeError::Format("Failed to read version".into()))?,
);
if version != 4 {
return Err(ParcodeError::Format(format!(
"Unsupported version: {version}. Expected V4."
)));
}
let root_offset = u64::from_le_bytes(
header_bytes
.get(6..14)
.ok_or_else(|| ParcodeError::Format("Root offset out of bounds".into()))?
.try_into()
.map_err(|_| ParcodeError::Format("Failed to read root offset".into()))?,
);
let root_length = u64::from_le_bytes(
header_bytes
.get(14..22)
.ok_or_else(|| ParcodeError::Format("Root length out of bounds".into()))?
.try_into()
.map_err(|_| ParcodeError::Format("Failed to read root length".into()))?,
);
let checksum = u32::from_le_bytes(
header_bytes
.get(22..26)
.ok_or_else(|| ParcodeError::Format("Checksum out of bounds".into()))?
.try_into()
.map_err(|_| ParcodeError::Format("Failed to read checksum".into()))?,
);
let header = GlobalHeader {
magic: MAGIC_BYTES,
version,
root_offset,
root_length,
checksum,
};
Ok(Self {
source,
header,
file_size,
registry: CompressorRegistry::new(),
})
}
pub fn load<T: ParcodeNative>(&self) -> Result<T> {
let root = self.root_node()?;
T::from_node(&root)
}
pub fn root<'a, T>(&'a self) -> Result<T::Lazy>
where
T: ParcodeLazyRef<'a>,
{
let root = self.root_node()?;
T::create_lazy(root)
}
#[inline]
pub fn load_lazy<'a, T>(&'a self) -> Result<T::Lazy>
where
T: ParcodeLazyRef<'a>,
{
self.root::<T>()
}
#[inline]
pub fn read_lazy<'a, T>(&'a self) -> Result<T::Lazy>
where
T: ParcodeLazyRef<'a>,
{
self.root::<T>()
}
pub fn inspect(&self) -> Result<crate::inspector::DebugReport> {
crate::inspector::ParcodeInspector::inspect_file(self)
}
pub fn file_size(&self) -> u64 {
self.file_size
}
pub fn root_node(&self) -> Result<ChunkNode<'_>> {
self.get_chunk(self.header.root_offset, self.header.root_length)
}
fn read_u32(slice: &[u8]) -> Result<u32> {
slice
.try_into()
.map(u32::from_le_bytes)
.map_err(|_| ParcodeError::Format("Failed to read u32".into()))
}
fn get_chunk(&self, offset: u64, length: u64) -> Result<ChunkNode<'_>> {
if offset + length > self.file_size {
return Err(ParcodeError::Format(format!(
"Chunk out of bounds: {} + {}",
offset, length
)));
}
let chunk_end = usize::try_from(offset + length)
.map_err(|_| ParcodeError::Format("Chunk end exceeds address space".into()))?;
let meta_byte = self
.source
.get(chunk_end - 1)
.ok_or_else(|| ParcodeError::Format("Failed to read chunk meta byte".into()))?;
let meta = MetaByte::from_byte(*meta_byte);
let mut child_count = 0;
let mut payload_end = chunk_end - 1;
if meta.is_chunkable() {
if length < 5 {
return Err(ParcodeError::Format("Chunk too small for metadata".into()));
}
let count_start = chunk_end - 5;
let count_bytes = self
.source
.get(count_start..count_start + 4)
.ok_or_else(|| ParcodeError::Format("Failed to read child count".into()))?;
child_count = Self::read_u32(count_bytes)?;
let footer_size = child_count as usize * ChildRef::SIZE;
let total_meta_size = 1 + 4 + footer_size;
if length < total_meta_size as u64 {
return Err(ParcodeError::Format("Invalid footer size".into()));
}
payload_end = chunk_end - total_meta_size;
}
Ok(ChunkNode {
reader: self,
offset,
length,
meta,
child_count,
payload_end_offset: offset + (payload_end as u64 - offset),
})
}
}
#[derive(Debug, Clone)]
pub struct ChunkNode<'a> {
reader: &'a ParcodeFile,
offset: u64,
#[allow(dead_code)]
length: u64,
meta: MetaByte,
child_count: u32,
payload_end_offset: u64,
}
#[derive(Deserialize, Debug, Clone)]
struct ShardRun {
item_count: u32,
repeat: u32,
}
impl<'a> ChunkNode<'a> {
pub fn read_raw(&self) -> Result<Cow<'a, [u8]>> {
let start = usize::try_from(self.offset)
.map_err(|_| ParcodeError::Format("Offset exceeds address space".into()))?;
let end = usize::try_from(self.payload_end_offset)
.map_err(|_| ParcodeError::Format("End offset exceeds address space".into()))?;
let raw = self
.reader
.source
.get(start..end)
.ok_or_else(|| ParcodeError::Format("Payload out of bounds".into()))?;
let method_id = self.meta.compression_method();
self.reader.registry.get(method_id)?.decompress(raw)
}
pub fn children(&self) -> Result<Vec<Self>> {
let mut list = Vec::with_capacity(self.child_count as usize);
for i in 0..self.child_count {
list.push(self.get_child_by_index(i as usize)?);
}
Ok(list)
}
pub fn decode<T: DeserializeOwned>(&self) -> Result<T> {
let payload = self.read_raw()?;
bincode::serde::decode_from_slice(&payload, bincode::config::standard())
.map(|(obj, _)| obj)
.map_err(|e| ParcodeError::Serialization(e.to_string()))
}
#[allow(unsafe_code)]
pub fn decode_parallel_collection<T>(&self) -> Result<Vec<T>>
where
T: ParcodeItem,
{
let payload = self.read_raw()?;
if payload.is_empty() {
return Ok(Vec::new());
}
if payload.len() < 8 {
let mut cursor = std::io::Cursor::new(payload.as_ref());
let children = self.children()?;
let mut child_iter = children.into_iter();
return T::read_slice_from_shard(&mut cursor, &mut child_iter);
}
let total_items = usize::try_from(u64::from_le_bytes(
payload
.get(0..8)
.ok_or_else(|| ParcodeError::Format("Payload too short".into()))?
.try_into()
.map_err(|_| ParcodeError::Format("Failed to parse total items".into()))?,
))
.map_err(|_| ParcodeError::Format("total_items exceeds usize".into()))?;
let runs_data = payload.get(8..).unwrap_or(&[]);
let shard_runs: Vec<ShardRun> =
bincode::serde::decode_from_slice(runs_data, bincode::config::standard())
.map(|(obj, _)| obj)
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
let mut shard_jobs = Vec::with_capacity(self.child_count as usize);
let mut current_shard_idx = 0;
let mut current_global_idx: usize = 0;
for run in shard_runs {
let items_per_shard = run.item_count as usize;
for _ in 0..run.repeat {
if current_global_idx.checked_add(items_per_shard).is_none() {
return Err(ParcodeError::Format("Integer overflow in RLE".into()));
}
shard_jobs.push((current_shard_idx, current_global_idx, items_per_shard));
current_shard_idx += 1;
current_global_idx += items_per_shard;
}
}
if current_global_idx != total_items {
return Err(ParcodeError::Format(format!(
"Metadata mismatch: Header={}, RLE={}",
total_items, current_global_idx
)));
}
if shard_jobs.is_empty() {
return Ok(Vec::new());
}
let mut result_buffer: Vec<MaybeUninit<T>> = Vec::with_capacity(total_items);
unsafe {
result_buffer.set_len(total_items);
}
let buffer_base_addr = result_buffer.as_mut_ptr() as usize;
#[cfg(all(feature = "parallel", not(target_arch = "wasm32")))]
{
shard_jobs.into_par_iter().try_for_each(
|(shard_idx, start_idx, expected_count)| -> Result<()> {
self.decode_shard_into_buffer::<T>(
shard_idx,
start_idx,
expected_count,
buffer_base_addr,
)
},
)?;
}
#[cfg(not(feature = "parallel"))]
{
for (shard_idx, start_idx, expected_count) in shard_jobs {
self.decode_shard_into_buffer::<T>(
shard_idx,
start_idx,
expected_count,
buffer_base_addr,
)?;
}
}
unsafe {
let mut manual = ManuallyDrop::new(result_buffer);
Ok(Vec::from_raw_parts(
manual.as_mut_ptr() as *mut T,
manual.len(),
manual.capacity(),
))
}
}
fn decode_shard_into_buffer<T: ParcodeItem>(
&self,
shard_idx: usize,
start_idx: usize,
expected_count: usize,
buffer_base_addr: usize,
) -> Result<()> {
let shard_node = self.get_child_by_index(shard_idx)?;
let payload = shard_node.read_raw()?;
let mut cursor = Cursor::new(payload.as_ref());
let children = shard_node.children()?;
let mut child_iter = children.into_iter();
#[allow(unsafe_code)]
let dest_slice = unsafe {
let ptr = (buffer_base_addr as *mut MaybeUninit<T>).add(start_idx);
slice::from_raw_parts_mut(ptr, expected_count)
};
let items_read = T::read_into_slice(&mut cursor, &mut child_iter, dest_slice)?;
if items_read != expected_count {
return Err(ParcodeError::Format(format!(
"Shard {} items mismatch: expected {}, got {}",
shard_idx, expected_count, items_read
)));
}
Ok(())
}
pub fn len(&self) -> u64 {
if let Ok(payload) = self.read_raw()
&& payload.len() >= 8
&& let Some(bytes) = payload.get(0..8).and_then(|s| s.try_into().ok())
{
return u64::from_le_bytes(bytes);
}
0
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub(crate) fn locate_shard_item(&self, index: usize) -> Result<(Self, usize)> {
let payload = self.read_raw()?;
if payload.len() < 8 {
return Err(ParcodeError::Format("Invalid container payload".into()));
}
let runs_data = payload.get(8..).unwrap_or(&[]);
let shard_runs: Vec<ShardRun> =
bincode::serde::decode_from_slice(runs_data, bincode::config::standard())
.map(|(obj, _)| obj)
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
let (shard_idx, local_idx) = self.resolve_rle_index(index, &shard_runs)?;
let shard = self.get_child_by_index(shard_idx)?;
Ok((shard, local_idx))
}
pub fn get<T: ParcodeItem>(&self, index: usize) -> Result<T> {
let payload = self.read_raw()?;
if payload.len() < 8 {
return Err(ParcodeError::Format("Not a valid container".into()));
}
let runs_data = payload.get(8..).unwrap_or(&[]);
let shard_runs: Vec<ShardRun> =
bincode::serde::decode_from_slice(runs_data, bincode::config::standard())
.map(|(obj, _)| obj)
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
let (target_shard_idx, index_in_shard) = self.resolve_rle_index(index, &shard_runs)?;
let shard_node = self.get_child_by_index(target_shard_idx)?;
let payload = shard_node.read_raw()?;
let mut cursor = std::io::Cursor::new(payload.as_ref());
let children = shard_node.children()?;
let mut child_iter = children.into_iter();
let shard_data: Vec<T> = T::read_slice_from_shard(&mut cursor, &mut child_iter)?;
shard_data
.into_iter()
.nth(index_in_shard)
.ok_or(ParcodeError::Internal("Shard index mismatch".into()))
}
pub fn iter<T: ParcodeItem>(self) -> Result<ChunkIterator<'a, T>> {
let payload = self.read_raw()?;
if payload.is_empty() && self.child_count == 0 {
return Ok(ChunkIterator::empty(self));
}
if payload.len() < 8 {
return Err(ParcodeError::Format("Not a valid container".into()));
}
let total_len = usize::try_from(u64::from_le_bytes(
payload
.get(0..8)
.ok_or_else(|| ParcodeError::Format("Payload too short".into()))?
.try_into()
.map_err(|_| ParcodeError::Format("Failed to read total_len".into()))?,
))
.map_err(|_| ParcodeError::Format("total_len exceeds usize range".into()))?;
let runs_data = payload.get(8..).unwrap_or(&[]);
let shard_runs: Vec<ShardRun> =
bincode::serde::decode_from_slice(runs_data, bincode::config::standard())
.map(|(obj, _)| obj)
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
Ok(ChunkIterator {
container: self,
shard_runs,
total_items: total_len,
current_global_idx: 0,
current_shard_idx: 0,
current_items_in_shard: Vec::new().into_iter(),
_marker: PhantomData,
})
}
pub fn get_child_by_index(&self, index: usize) -> Result<Self> {
if index >= self.child_count as usize {
return Err(ParcodeError::Format("Child index out of bounds".into()));
}
let footer_start = usize::try_from(self.payload_end_offset)
.map_err(|_| ParcodeError::Format("Offset exceeds usize range".into()))?;
let entry_start = footer_start + (index * ChildRef::SIZE);
let bytes = self
.reader
.source
.get(entry_start..entry_start + ChildRef::SIZE)
.ok_or_else(|| ParcodeError::Format("Child reference out of bounds".into()))?;
let r = ChildRef::from_bytes(bytes)?;
self.reader.get_chunk(r.offset, r.length)
}
fn resolve_rle_index(&self, global_index: usize, runs: &[ShardRun]) -> Result<(usize, usize)> {
let mut current_base = 0;
let mut shard_base = 0;
for run in runs {
let count = run.item_count as usize;
let total_run = count * run.repeat as usize;
if global_index < current_base + total_run {
let offset = global_index - current_base;
return Ok((shard_base + (offset / count), offset % count));
}
current_base += total_run;
shard_base += run.repeat as usize;
}
Err(ParcodeError::Format("Index out of bounds".into()))
}
pub fn offset(&self) -> u64 {
self.offset
}
pub fn length(&self) -> u64 {
self.length
}
pub fn child_count(&self) -> u32 {
self.child_count
}
pub fn meta(&self) -> crate::format::MetaByte {
self.meta
}
pub fn payload_len(&self) -> u64 {
self.payload_end_offset - self.offset
}
}
#[derive(Debug)]
pub struct ChunkIterator<'a, T> {
container: ChunkNode<'a>,
#[allow(dead_code)]
shard_runs: Vec<ShardRun>, total_items: usize,
current_global_idx: usize,
current_shard_idx: usize,
current_items_in_shard: std::vec::IntoIter<T>,
_marker: PhantomData<T>,
}
impl<'a, T> ChunkIterator<'a, T> {
fn empty(node: ChunkNode<'a>) -> Self {
Self {
container: node,
shard_runs: vec![],
total_items: 0,
current_global_idx: 0,
current_shard_idx: 0,
current_items_in_shard: Vec::new().into_iter(),
_marker: PhantomData,
}
}
}
impl<'a, T: ParcodeItem> Iterator for ChunkIterator<'a, T> {
type Item = Result<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_global_idx >= self.total_items {
return None;
}
if let Some(item) = self.current_items_in_shard.next() {
self.current_global_idx += 1;
return Some(Ok(item));
}
if self.current_shard_idx >= self.container.child_count as usize {
return Some(Err(ParcodeError::Internal(
"Iterator mismatch: runs out of shards".into(),
)));
}
let next_shard_res = self
.container
.get_child_by_index(self.current_shard_idx)
.and_then(|node| {
let payload = node.read_raw()?;
let mut cursor = std::io::Cursor::new(payload.as_ref());
let children = node.children()?;
let mut child_iter = children.into_iter();
T::read_slice_from_shard(&mut cursor, &mut child_iter)
});
match next_shard_res {
Ok(items) => {
self.current_items_in_shard = items.into_iter();
self.current_shard_idx += 1;
self.next()
}
Err(e) => Some(Err(e)),
}
}
}