use std::hash::Hash; use std::{collections::HashMap, mem::size_of}; use nix::errno::Errno; use nix::Result; use rshm::shm::{OwnedShmMap, ShmMap}; /// The client of a shared memory dictionary. pub struct ShmDictionaryClient> { _map: ShmMap, written_records_ptr: *const usize, end_ptr: *const R, next_read: usize, index: HashMap, } impl> ShmDictionaryClient { pub fn new(map: ShmMap) -> Self { // We keep the number of written bytes of the beginning let written_records_ptr = map.head() as *const usize; // Ensure Alignment let end_ptr = unsafe { (map.head() as *const R).add(1) }; Self { _map: map, written_records_ptr: written_records_ptr, end_ptr: end_ptr, next_read: 0, index: HashMap::new(), } } pub fn get(&mut self, key: &K) -> Result { let records_count = unsafe { self.written_records_ptr.read_volatile() }; if !self.index.contains_key(key) { while self.next_read < records_count { let record = unsafe { self.end_ptr.read_volatile() }; self.index.insert(record.key().clone(), self.next_read); self.next_read += 1; unsafe { self.end_ptr = self.end_ptr.add(1); } } }; self.index .get(key) .ok_or(Errno::ENOKEY) .and_then(|i| unsafe { Ok(self.end_ptr.sub(records_count - i).read_volatile()) }) } } /// The owner of a shared memory dictionary pub struct ShmDictionaryOwner> { _map: OwnedShmMap, written_records_ptr: *mut usize, end_ptr: *mut R, available: usize, index: HashMap, } impl> ShmDictionaryOwner { pub fn new(map: OwnedShmMap) -> Self { let size = map.definition.size; // We keep the number of written bytes of the beginning let written_records_ptr = map.head() as *mut usize; // Ensure Alignment let end_ptr = unsafe { (map.head() as *mut R).add(1) }; unsafe { *written_records_ptr = 0 }; Self { _map: map, written_records_ptr: written_records_ptr, end_ptr: end_ptr, available: (size - size_of::()) / size_of::(), index: HashMap::new(), } } pub fn put(&mut self, record: R) -> Result<()> { if self.available > 0 { let key = record.key(); let written_records = unsafe { self.written_records_ptr.read_volatile() }; match self.index.get(&key) { Some(i) => { unsafe { self.end_ptr .sub(written_records - i + 1) .write_volatile(record) }; } None => { unsafe { self.end_ptr.write(record); self.written_records_ptr.write_volatile(written_records + 1); self.end_ptr = self.end_ptr.add(1); self.index.insert(key, *self.written_records_ptr as usize); }; self.available -= 1; } }; Ok(()) } else { Err(Errno::ENOMEM) } } } /// Definition of a record's Key. pub trait Key: Eq + Hash + Clone {} /// Records stored in the dictionary need a key. pub trait Record: Copy { fn key(&self) -> K; } #[cfg(test)] mod tests { use rshm::shm::ShmDefinition; use crate::{Record, ShmDictionaryClient, ShmDictionaryOwner}; #[derive(Clone, Copy)] pub struct TestRecord { pub value: (i32, i32), } impl Record for TestRecord { fn key(&self) -> i32 { println!("{:?}", self.value); self.value.0.clone() } } #[test] fn store_insertion_is_read_by_the_client() { let owner_definition = ShmDefinition { path: "test_store".to_string(), size: 1024, }; let owner_shared_memory = owner_definition.create().unwrap(); let mut owner_store: ShmDictionaryOwner = ShmDictionaryOwner::new(owner_shared_memory); let client_definition = ShmDefinition { path: "test_store".to_string(), size: 1024, }; let client_shared_memory = client_definition.open().unwrap(); let mut client_store: ShmDictionaryClient = ShmDictionaryClient::new(client_shared_memory); owner_store.put(TestRecord { value: (1, 11) }).unwrap(); assert_eq!(client_store.get(&1).unwrap().value, (1, 11)); } }