/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2026 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::{ borrow::Borrow, collections::{ hash_map::{Iter, Keys, Values}, HashMap, }, hash::Hash, }; use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable}; use crate::{net, net::P2pPtr, Result}; /// A general networked hashmap. Propagates changes over P2P. #[derive(Clone)] pub struct NetHashMap { /// The internal [`HashMap`] that represents the actual state hashmap: HashMap, /// Pointer to the P2P network p2p: P2pPtr, } impl NetHashMap { /// Instantiate a new [`NetHashMap`] with the given [`P2pPtr`] pub fn new(p2p: P2pPtr) -> Self { let hashmap = HashMap::new(); Self { hashmap, p2p } } } impl NetHashMap where K: Eq + Hash + Send + Sync + Encodable + Decodable + Clone + 'static, V: Send + Sync + Encodable + Decodable + Clone + 'static, { /// Returns `true` if the map contains a value for the specified key. #[allow(dead_code)] pub fn contains_key(&self, k: &Q) -> bool where K: Borrow, Q: Hash + Eq, { self.hashmap.contains_key(k) } /// Returns `true` if the map contains no elements. #[allow(dead_code)] pub fn is_empty(&self) -> bool { self.hashmap.is_empty() } /// Returns the number of elements in the map. #[allow(dead_code)] pub fn len(&self) -> usize { self.hashmap.len() } /// Insert a key-value pair into the map. /// /// If the map did not have this key present, `None` is returned. /// /// If the map did have this key present, the value is updated, and /// the old value is returned. /// /// Additionally, this change will be broadcasted to the P2P network. pub async fn insert(&mut self, k: K, v: V) -> Result> { let message = NetHashMapInsert { k: k.clone(), v: v.clone() }; self.p2p.broadcast(&message).await; Ok(self.hashmap.insert(k, v)) } /// Removes a key from the map, returning the value at the key if the key /// was previously in the map. /// /// Additionally, this change will be broadcasted to the P2P network. pub async fn remove( &mut self, k: Q, ) -> Result> where K: Borrow, Q: Hash + Eq + Send + Sync + Encodable + Decodable + 'static, { let message = NetHashMapRemove { k: k.clone() }; self.p2p.broadcast(&message).await; Ok(self.hashmap.remove(&k)) } /// An iterator visiting all key-value pairs in arbitrary order. /// The iterator element type is `(&'a K, &'a V)`. #[allow(dead_code)] pub fn iter(&self) -> Iter<'_, K, V> { self.hashmap.iter() } /// An iterator visiting all keys in arbitrary order. /// The iterator element type is `&'a K`. #[allow(dead_code)] pub fn keys(&self) -> Keys<'_, K, V> { self.hashmap.keys() } /// An iterator visiting all values in arbitrary order. /// The iterator element type is `&'a V`. #[allow(dead_code)] pub fn values(&self) -> Values<'_, K, V> { self.hashmap.values() } } #[derive(Debug, Clone, SerialDecodable, SerialEncodable)] pub struct NetHashMapInsert { pub k: K, pub v: V, } impl net::Message for NetHashMapInsert where K: Encodable + Decodable + Send + Sync + 'static, V: Encodable + Decodable + Send + Sync + 'static, { const NAME: &'static str = "nethashmap_insert"; } #[derive(Debug, Clone, SerialDecodable, SerialEncodable)] pub struct NetHashMapRemove { pub k: K, } impl net::Message for NetHashMapRemove where K: Encodable + Decodable + Send + Sync + 'static, { const NAME: &'static str = "nethashmap_remove"; }