|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
|
use crate::core::{Blob, Id, PlacedPoint, Point}; |
|
|
use crate::ports::{Place, PlaceError, PlaceResult}; |
|
|
|
|
|
|
|
|
pub struct MemoryStorage { |
|
|
|
|
|
points: HashMap<Id, PlacedPoint>, |
|
|
|
|
|
|
|
|
dimensionality: usize, |
|
|
|
|
|
|
|
|
capacity: usize, |
|
|
|
|
|
|
|
|
current_size: usize, |
|
|
} |
|
|
|
|
|
impl MemoryStorage { |
|
|
|
|
|
pub fn new(dimensionality: usize) -> Self { |
|
|
Self { |
|
|
points: HashMap::new(), |
|
|
dimensionality, |
|
|
capacity: 0, |
|
|
current_size: 0, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
pub fn with_capacity(dimensionality: usize, capacity: usize) -> Self { |
|
|
Self { |
|
|
points: HashMap::new(), |
|
|
dimensionality, |
|
|
capacity, |
|
|
current_size: 0, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
fn point_size(point: &PlacedPoint) -> usize { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 + (point.point.dimensionality() * 4) + point.blob.size() + 48 |
|
|
} |
|
|
} |
|
|
|
|
|
impl Place for MemoryStorage { |
|
|
fn place(&mut self, point: Point, blob: Blob) -> PlaceResult<Id> { |
|
|
|
|
|
if point.dimensionality() != self.dimensionality { |
|
|
return Err(PlaceError::DimensionalityMismatch { |
|
|
expected: self.dimensionality, |
|
|
got: point.dimensionality(), |
|
|
}); |
|
|
} |
|
|
|
|
|
let id = Id::now(); |
|
|
let placed = PlacedPoint::new(id, point, blob); |
|
|
|
|
|
|
|
|
let size = Self::point_size(&placed); |
|
|
if self.capacity > 0 && self.current_size + size > self.capacity { |
|
|
return Err(PlaceError::CapacityExceeded); |
|
|
} |
|
|
|
|
|
self.current_size += size; |
|
|
self.points.insert(id, placed); |
|
|
|
|
|
Ok(id) |
|
|
} |
|
|
|
|
|
fn place_with_id(&mut self, id: Id, point: Point, blob: Blob) -> PlaceResult<()> { |
|
|
|
|
|
if point.dimensionality() != self.dimensionality { |
|
|
return Err(PlaceError::DimensionalityMismatch { |
|
|
expected: self.dimensionality, |
|
|
got: point.dimensionality(), |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if self.points.contains_key(&id) { |
|
|
return Err(PlaceError::DuplicateId(id)); |
|
|
} |
|
|
|
|
|
let placed = PlacedPoint::new(id, point, blob); |
|
|
|
|
|
|
|
|
let size = Self::point_size(&placed); |
|
|
if self.capacity > 0 && self.current_size + size > self.capacity { |
|
|
return Err(PlaceError::CapacityExceeded); |
|
|
} |
|
|
|
|
|
self.current_size += size; |
|
|
self.points.insert(id, placed); |
|
|
|
|
|
Ok(()) |
|
|
} |
|
|
|
|
|
fn remove(&mut self, id: Id) -> Option<PlacedPoint> { |
|
|
if let Some(placed) = self.points.remove(&id) { |
|
|
self.current_size -= Self::point_size(&placed); |
|
|
Some(placed) |
|
|
} else { |
|
|
None |
|
|
} |
|
|
} |
|
|
|
|
|
fn get(&self, id: Id) -> Option<&PlacedPoint> { |
|
|
self.points.get(&id) |
|
|
} |
|
|
|
|
|
fn len(&self) -> usize { |
|
|
self.points.len() |
|
|
} |
|
|
|
|
|
fn iter(&self) -> Box<dyn Iterator<Item = &PlacedPoint> + '_> { |
|
|
Box::new(self.points.values()) |
|
|
} |
|
|
|
|
|
fn size_bytes(&self) -> usize { |
|
|
self.current_size |
|
|
} |
|
|
|
|
|
fn clear(&mut self) { |
|
|
self.points.clear(); |
|
|
self.current_size = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
#[cfg(test)] |
|
|
mod tests { |
|
|
use super::*; |
|
|
|
|
|
#[test] |
|
|
fn test_memory_storage_place() { |
|
|
let mut storage = MemoryStorage::new(3); |
|
|
|
|
|
let point = Point::new(vec![1.0, 2.0, 3.0]); |
|
|
let blob = Blob::from_str("test"); |
|
|
|
|
|
let id = storage.place(point, blob).unwrap(); |
|
|
|
|
|
assert_eq!(storage.len(), 1); |
|
|
assert!(storage.contains(id)); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_memory_storage_get() { |
|
|
let mut storage = MemoryStorage::new(3); |
|
|
|
|
|
let point = Point::new(vec![1.0, 2.0, 3.0]); |
|
|
let blob = Blob::from_str("hello"); |
|
|
|
|
|
let id = storage.place(point, blob).unwrap(); |
|
|
|
|
|
let retrieved = storage.get(id).unwrap(); |
|
|
assert_eq!(retrieved.blob.as_str(), Some("hello")); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_memory_storage_remove() { |
|
|
let mut storage = MemoryStorage::new(3); |
|
|
|
|
|
let point = Point::new(vec![1.0, 2.0, 3.0]); |
|
|
let id = storage.place(point, Blob::empty()).unwrap(); |
|
|
|
|
|
assert_eq!(storage.len(), 1); |
|
|
|
|
|
let removed = storage.remove(id); |
|
|
assert!(removed.is_some()); |
|
|
assert_eq!(storage.len(), 0); |
|
|
assert!(!storage.contains(id)); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_memory_storage_dimensionality_check() { |
|
|
let mut storage = MemoryStorage::new(3); |
|
|
|
|
|
let wrong_dims = Point::new(vec![1.0, 2.0]); |
|
|
|
|
|
let result = storage.place(wrong_dims, Blob::empty()); |
|
|
|
|
|
match result { |
|
|
Err(PlaceError::DimensionalityMismatch { expected, got }) => { |
|
|
assert_eq!(expected, 3); |
|
|
assert_eq!(got, 2); |
|
|
} |
|
|
_ => panic!("Expected DimensionalityMismatch error"), |
|
|
} |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_memory_storage_capacity() { |
|
|
|
|
|
|
|
|
let mut storage = MemoryStorage::with_capacity(3, 150); |
|
|
|
|
|
let point = Point::new(vec![1.0, 2.0, 3.0]); |
|
|
let blob = Blob::new(vec![0u8; 10]); |
|
|
|
|
|
|
|
|
storage.place(point.clone(), blob.clone()).unwrap(); |
|
|
|
|
|
|
|
|
let result = storage.place(point, blob); |
|
|
assert!(matches!(result, Err(PlaceError::CapacityExceeded))); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_memory_storage_clear() { |
|
|
let mut storage = MemoryStorage::new(3); |
|
|
|
|
|
for i in 0..10 { |
|
|
let point = Point::new(vec![i as f32, 0.0, 0.0]); |
|
|
storage.place(point, Blob::empty()).unwrap(); |
|
|
} |
|
|
|
|
|
assert_eq!(storage.len(), 10); |
|
|
assert!(storage.size_bytes() > 0); |
|
|
|
|
|
storage.clear(); |
|
|
|
|
|
assert_eq!(storage.len(), 0); |
|
|
assert_eq!(storage.size_bytes(), 0); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_memory_storage_iter() { |
|
|
let mut storage = MemoryStorage::new(2); |
|
|
|
|
|
storage.place(Point::new(vec![1.0, 0.0]), Blob::empty()).unwrap(); |
|
|
storage.place(Point::new(vec![0.0, 1.0]), Blob::empty()).unwrap(); |
|
|
|
|
|
let points: Vec<_> = storage.iter().collect(); |
|
|
assert_eq!(points.len(), 2); |
|
|
} |
|
|
} |
|
|
|