Skip to content

Commit

Permalink
get chunks number from unbounded map
Browse files Browse the repository at this point in the history
  • Loading branch information
F3kilo committed Jan 26, 2024
1 parent af983b4 commit e803c95
Showing 1 changed file with 62 additions and 0 deletions.
62 changes: 62 additions & 0 deletions ic-stable-structures/src/structure/stable_storage/unbounded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ where
StableUnboundedIter(self.inner.iter().peekable())
}

/// Returns number of chunks, used to store the entity.
pub fn chunks_number_of(&self, key: &K) -> Option<ChunkIndex> {
let first_chunk_key = Key::new(key);
let max_chunk_key = first_chunk_key.clone().with_max_chunk_index();

// first item in this iter will be the last chunk of the key.
self.inner
.iter_upper_bound(&max_chunk_key)
.filter_map(|(k, _)| {
(k.prefix() == max_chunk_key.prefix()).then_some(k.chunk_index() + 1)
})
.next()
}

/// Returns an iterator pointing to the first element below the given bound.
/// Returns an empty iterator if there are no keys below the given bound.
pub fn iter_upper_bound(&self, bound: &K) -> StableUnboundedIter<'_, K, V, M> {
Expand Down Expand Up @@ -306,6 +320,19 @@ impl<K: Storable> Key<K> {
pub fn key_data(&self) -> &[u8] {
&self.data[Self::BOUNDS.size_prefix_len..self.data.len() - CHUNK_INDEX_LEN]
}

/// Chunk index of the key.
pub fn chunk_index(&self) -> ChunkIndex {
// last `CHUNK_INDEX_LEN` bytes is chunk index
let chunk_index_bytes = &self.data[(self.data.len() - CHUNK_INDEX_LEN)..];

let chunk_index_arr = chunk_index_bytes
.try_into()
.expect("the slice is always CHUNK_INDEX_LEN length");

// store chunk index in big-endian format to preserve order of chunks in BTreeMap
ChunkIndex::from_be_bytes(chunk_index_arr)
}
}

impl<K: Storable> Storable for Key<K> {
Expand Down Expand Up @@ -614,4 +641,39 @@ mod tests {
assert_eq!(map.last_key(), Some(4u32));
assert_eq!(map.last_key_value(), Some((4u32, str_4)));
}

#[test]
fn test_chunks_number_calculation() {
let mut map = StableUnboundedMap::new(VectorMemory::default());

// No chunks if there is no key.
assert!(map.chunks_number_of(&42).is_none());

// Exact number of chunks.
let expected_chunks_number = 42;
let val = str_val(StringValue::CHUNK_SIZE as usize * expected_chunks_number);
map.insert(&10_u64, &val);
assert_eq!(map.chunks_number_of(&10), Some(expected_chunks_number as _));

// One more partially filled chunk.
let expected_chunks_number = 42;
let val = str_val(StringValue::CHUNK_SIZE as usize * expected_chunks_number + 5);
map.insert(&10_u64, &val);
assert_eq!(
map.chunks_number_of(&10),
Some(expected_chunks_number as u16 + 1)
);

// Make the key to be between other keys.
map.insert(&5_u64, &val);
map.insert(&15_u64, &val);
assert_eq!(
map.chunks_number_of(&10),
Some(expected_chunks_number as u16 + 1)
);

// No chunks if there is no key.
map.remove(&10_u64);
assert!(map.chunks_number_of(&10).is_none());
}
}

0 comments on commit e803c95

Please sign in to comment.