| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| 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); |
| } |
| } |
|
|