use crate::types::{ArchiveLocation, EKey};
use std::collections::BTreeMap;
#[derive(Debug, Clone)]
pub struct SortedIndex {
entries: BTreeMap<EKey, ArchiveLocation>,
sorted_keys: Option<Vec<EKey>>,
}
impl SortedIndex {
pub fn new() -> Self {
Self {
entries: BTreeMap::new(),
sorted_keys: None,
}
}
pub fn with_capacity(_capacity: usize) -> Self {
Self::new()
}
pub fn insert(&mut self, key: EKey, location: ArchiveLocation) {
self.entries.insert(key, location);
self.sorted_keys = None;
}
pub fn lookup(&self, key: &EKey) -> Option<&ArchiveLocation> {
self.entries.get(key)
}
pub fn range(
&self,
start: &EKey,
end: &EKey,
) -> impl Iterator<Item = (&EKey, &ArchiveLocation)> {
self.entries.range(start..=end)
}
pub fn lower_bound(&self, key: &EKey) -> Option<(&EKey, &ArchiveLocation)> {
self.entries.range(key..).next()
}
pub fn upper_bound(&self, key: &EKey) -> Option<(&EKey, &ArchiveLocation)> {
self.entries.range(..=key).next_back()
}
pub fn entries(&self) -> impl Iterator<Item = (&EKey, &ArchiveLocation)> {
self.entries.iter()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
self.sorted_keys = None;
}
pub fn sorted_keys(&mut self) -> &[EKey] {
if self.sorted_keys.is_none() {
self.sorted_keys = Some(self.entries.keys().copied().collect());
}
self.sorted_keys.as_ref().unwrap()
}
pub fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = (EKey, ArchiveLocation)>,
{
self.entries.extend(iter);
self.sorted_keys = None;
}
pub fn from_hashmap(map: std::collections::HashMap<EKey, ArchiveLocation>) -> Self {
let mut index = Self::new();
index.extend(map);
index
}
pub fn into_btree_map(self) -> BTreeMap<EKey, ArchiveLocation> {
self.entries
}
}
impl Default for SortedIndex {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sorted_index_operations() {
let mut index = SortedIndex::new();
let key1 = EKey::new([1; 16]);
let key2 = EKey::new([2; 16]);
let key3 = EKey::new([3; 16]);
let loc1 = ArchiveLocation {
archive_id: 1,
offset: 100,
size: 50,
};
let loc2 = ArchiveLocation {
archive_id: 2,
offset: 200,
size: 60,
};
let loc3 = ArchiveLocation {
archive_id: 3,
offset: 300,
size: 70,
};
index.insert(key2, loc2);
index.insert(key1, loc1);
index.insert(key3, loc3);
assert_eq!(index.lookup(&key1), Some(&loc1));
assert_eq!(index.lookup(&key2), Some(&loc2));
assert_eq!(index.lookup(&key3), Some(&loc3));
let key4 = EKey::new([4; 16]);
assert_eq!(index.lookup(&key4), None);
assert_eq!(index.len(), 3);
let range: Vec<_> = index.range(&key1, &key2).collect();
assert_eq!(range.len(), 2);
let lower = index.lower_bound(&key2);
assert!(lower.is_some());
assert_eq!(lower.unwrap().0, &key2);
}
#[test]
fn test_sorted_order() {
let mut index = SortedIndex::new();
for i in [5u8, 2, 8, 1, 9, 3, 7, 4, 6] {
let mut key_bytes = [0u8; 16];
key_bytes[0] = i;
let key = EKey::new(key_bytes);
let loc = ArchiveLocation {
archive_id: i as u16,
offset: (i as u64) * 100,
size: 50,
};
index.insert(key, loc);
}
let keys: Vec<_> = index.entries().map(|(k, _)| *k).collect();
for i in 1..keys.len() {
assert!(keys[i - 1] < keys[i], "Keys should be in sorted order");
}
}
}