use crate::storage::FileSize;
use crate::storage::Node;
use crate::storage::types::FileChunkIndex;
use crate::storage::types::FileChunkPtr;
use ic_stable_structures;
use ic_stable_structures::BTreeMap;
use ic_stable_structures::Memory;
use ic_stable_structures::memory_manager::VirtualMemory;
use super::ptr_cache::CachedChunkPtr;
use super::ptr_cache::PtrCache;
pub(crate) struct ChunkV2Iterator<'a, M: Memory> {
node: Node,
last_index_excluded: FileChunkIndex,
cur_index: FileChunkIndex,
ptr_cache: &'a mut PtrCache,
v2_chunk_ptr: &'a mut BTreeMap<(Node, FileChunkIndex), FileChunkPtr, VirtualMemory<M>>,
}
impl<'a, M: Memory> ChunkV2Iterator<'a, M> {
pub fn new(
node: Node,
offset: FileSize,
last_address: FileSize,
chunk_size: FileSize,
ptr_cache: &'a mut PtrCache,
v2_chunk_ptr: &'a mut BTreeMap<(Node, FileChunkIndex), FileChunkPtr, VirtualMemory<M>>,
) -> Self {
let cur_index = (offset / chunk_size) as FileChunkIndex;
let last_index_excluded = (last_address / chunk_size + 1) as FileChunkIndex;
Self {
node,
last_index_excluded,
cur_index,
ptr_cache,
v2_chunk_ptr,
}
}
}
impl<M: Memory> Iterator for ChunkV2Iterator<'_, M> {
type Item = ((Node, FileChunkIndex), CachedChunkPtr);
fn next(&mut self) -> Option<Self::Item> {
if self.cur_index >= self.last_index_excluded {
return None;
}
let ptr = self.ptr_cache.get((self.node, self.cur_index));
if let Some(chunk_ptr) = ptr {
let ret = Some(((self.node, self.cur_index), chunk_ptr));
self.cur_index += 1;
return ret;
}
self.ptr_cache.add_range(
self.node,
self.cur_index,
self.last_index_excluded,
self.v2_chunk_ptr,
);
let found: CachedChunkPtr = self.ptr_cache.get((self.node, self.cur_index)).unwrap();
let res = Some(((self.node, self.cur_index), found));
self.cur_index += 1;
res
}
}
#[cfg(test)]
mod tests {
use crate::fs::FileSize;
use crate::storage::Storage;
use crate::storage::chunk_iterator::ChunkV2Iterator;
use crate::storage::ptr_cache::CachedChunkPtr;
use crate::storage::stable::StableStorage;
use crate::storage::types::{FileType, Metadata, Node, Times};
use crate::test_utils::new_vector_memory;
use ic_stable_structures::Memory;
fn create_file_with_size<M: Memory>(size: FileSize, storage: &mut StableStorage<M>) -> Node {
let node = storage.new_node();
storage
.put_metadata(
node,
&Metadata {
node,
file_type: FileType::RegularFile,
link_count: 1,
size,
times: Times::default(),
chunk_type: Some(storage.chunk_type()),
maximum_size_allowed: None,
first_dir_entry: None,
last_dir_entry: None,
},
)
.unwrap();
node
}
#[test]
fn iterate_short_file() {
let mut storage = StableStorage::new(new_vector_memory());
let node = create_file_with_size(0, &mut storage);
let write_size = storage.chunk_size() * 3 - 100;
let buf = vec![142u8; write_size];
storage.write(node, 0, &buf).unwrap();
let meta = storage.get_metadata(node).unwrap();
let file_size = meta.size;
let iterator = ChunkV2Iterator::new(
node,
30,
file_size,
storage.chunk_size() as FileSize,
&mut storage.ptr_cache,
&mut storage.v2_filechunk.v2_chunk_ptr,
);
let res_vec: Vec<_> = iterator.collect();
assert!(res_vec[0].1 != CachedChunkPtr::ChunkMissing);
assert!(res_vec[1].1 != CachedChunkPtr::ChunkMissing);
assert!(res_vec[2].1 != CachedChunkPtr::ChunkMissing);
}
#[test]
fn iterate_file_with_size_and_no_stored_chunks() {
let mut storage = StableStorage::new(new_vector_memory());
let write_size = (storage.chunk_size() * 3 - 100) as FileSize;
let node = create_file_with_size(write_size, &mut storage);
let meta = storage.get_metadata(node).unwrap();
let file_size = meta.size;
let iterator = ChunkV2Iterator::new(
node,
30,
file_size,
storage.chunk_size() as FileSize,
&mut storage.ptr_cache,
&mut storage.v2_filechunk.v2_chunk_ptr,
);
let res_vec: Vec<_> = iterator.collect();
assert!(res_vec[0].1 == CachedChunkPtr::ChunkMissing);
assert!(res_vec[1].1 == CachedChunkPtr::ChunkMissing);
assert!(res_vec[2].1 == CachedChunkPtr::ChunkMissing);
}
#[test]
fn iterate_file_missing_chunk_in_the_middle() {
let mut storage = StableStorage::new(new_vector_memory());
let node = create_file_with_size(0, &mut storage);
let write_size = (storage.chunk_size() * 3 - 200) as FileSize;
storage.write(node, 10, &[142u8; 100]).unwrap();
storage.write(node, write_size, &[142u8; 100]).unwrap();
let meta = storage.get_metadata(node).unwrap();
let file_size = meta.size;
let iterator = ChunkV2Iterator::new(
node,
30,
file_size,
storage.chunk_size() as FileSize,
&mut storage.ptr_cache,
&mut storage.v2_filechunk.v2_chunk_ptr,
);
let res_vec: Vec<_> = iterator.collect();
assert!(res_vec[0].1 != CachedChunkPtr::ChunkMissing);
assert!(res_vec[1].1 == CachedChunkPtr::ChunkMissing);
assert!(res_vec[2].1 != CachedChunkPtr::ChunkMissing);
}
#[test]
fn iterate_file_only_middle_chunk_is_present() {
let mut storage = StableStorage::new(new_vector_memory());
let file_size = (storage.chunk_size() * 3 - 200) as FileSize;
let node = create_file_with_size(file_size, &mut storage);
let write_size = (storage.chunk_size() * 2 - 200) as FileSize;
storage.write(node, write_size, &[142u8; 102]).unwrap();
let meta = storage.get_metadata(node).unwrap();
let file_size = meta.size;
let iterator = ChunkV2Iterator::new(
node,
30,
file_size,
storage.chunk_size() as FileSize,
&mut storage.ptr_cache,
&mut storage.v2_filechunk.v2_chunk_ptr,
);
let res_vec: Vec<_> = iterator.collect();
assert!(res_vec[0].1 == CachedChunkPtr::ChunkMissing);
assert!(res_vec[1].1 != CachedChunkPtr::ChunkMissing);
assert!(res_vec[2].1 == CachedChunkPtr::ChunkMissing);
}
}