Rename our custom `HashMap` to `RandomMap`

This makes it easier to distinguish with the stdlib.

Also, we add an env var that can control the RNG seed, for testing
purposes.
This commit is contained in:
cloudhead 2023-08-09 10:50:49 +02:00
parent d13f47b2a4
commit efb8de2728
No known key found for this signature in database
9 changed files with 43 additions and 29 deletions

View File

@ -56,6 +56,7 @@ fn formula(root: &Path, test: impl AsRef<Path>) -> Result<TestFormula, Box<dyn s
.env("GIT_COMMITTER_NAME", "radicle") .env("GIT_COMMITTER_NAME", "radicle")
.env("RAD_PASSPHRASE", "radicle") .env("RAD_PASSPHRASE", "radicle")
.env("RAD_SEED", RAD_SEED) .env("RAD_SEED", RAD_SEED)
.env("RAD_RNG_SEED", "0")
.env("EDITOR", "true") .env("EDITOR", "true")
.env("TZ", "UTC") .env("TZ", "UTC")
.env("LANG", "C") .env("LANG", "C")

View File

@ -12,7 +12,7 @@ use radicle::node::routing::Store as _;
use radicle::node::ConnectOptions; use radicle::node::ConnectOptions;
use radicle::storage::ReadRepository; use radicle::storage::ReadRepository;
use crate::collections::{HashMap, HashSet}; use crate::collections::{RandomMap, RandomSet};
use crate::crypto::test::signer::MockSigner; use crate::crypto::test::signer::MockSigner;
use crate::identity::Id; use crate::identity::Id;
use crate::node; use crate::node;
@ -1375,7 +1375,7 @@ fn prop_inventory_exchange_dense() {
eve_inv.clone(), eve_inv.clone(),
peer::Config::default(), peer::Config::default(),
); );
let mut routing = HashMap::with_hasher(rng.clone().into()); let mut routing = RandomMap::with_hasher(rng.clone().into());
for (inv, peer) in &[ for (inv, peer) in &[
(alice_inv.inventory, alice.node_id()), (alice_inv.inventory, alice.node_id()),
@ -1385,7 +1385,7 @@ fn prop_inventory_exchange_dense() {
for id in inv.keys() { for id in inv.keys() {
routing routing
.entry(*id) .entry(*id)
.or_insert_with(|| HashSet::with_hasher(rng.clone().into())) .or_insert_with(|| RandomSet::with_hasher(rng.clone().into()))
.insert(*peer); .insert(*peer);
} }
} }
@ -1407,7 +1407,7 @@ fn prop_inventory_exchange_dense() {
ConnectOptions::default(), ConnectOptions::default(),
)); ));
let mut peers: HashMap<_, _> = [ let mut peers: RandomMap<_, _> = [
(alice.node_id(), alice), (alice.node_id(), alice),
(bob.node_id(), bob), (bob.node_id(), bob),
(eve.node_id(), eve), (eve.node_id(), eve),
@ -1439,7 +1439,7 @@ fn prop_inventory_exchange_dense() {
"There are remote locations for the project" "There are remote locations for the project"
); );
assert_eq!( assert_eq!(
&lookup.remote.into_iter().collect::<HashSet<_>>(), &lookup.remote.into_iter().collect::<RandomSet<_>>(),
remotes, remotes,
"The remotes match the global routing table" "The remotes match the global routing table"
); );

View File

@ -21,7 +21,7 @@ use netservices::session::{ProtocolArtifact, Socks5Session};
use netservices::{NetConnection, NetProtocol, NetReader, NetWriter}; use netservices::{NetConnection, NetProtocol, NetReader, NetWriter};
use reactor::Timestamp; use reactor::Timestamp;
use radicle::collections::HashMap; use radicle::collections::RandomMap;
use radicle::node::{address, routing, NodeId}; use radicle::node::{address, routing, NodeId};
use radicle::storage::WriteStorage; use radicle::storage::WriteStorage;
@ -74,7 +74,7 @@ struct Streams {
/// Active streams and their associated worker channels. /// Active streams and their associated worker channels.
/// Note that the gossip and control streams are not included here as they are always /// Note that the gossip and control streams are not included here as they are always
/// implied to exist. /// implied to exist.
streams: HashMap<StreamId, worker::Channels>, streams: RandomMap<StreamId, worker::Channels>,
/// Connection direction. /// Connection direction.
link: Link, link: Link,
/// Sequence number used to compute the next stream id. /// Sequence number used to compute the next stream id.
@ -85,7 +85,7 @@ impl Streams {
/// Create a new [`Streams`] object, passing the connection link. /// Create a new [`Streams`] object, passing the connection link.
fn new(link: Link) -> Self { fn new(link: Link) -> Self {
Self { Self {
streams: HashMap::default(), streams: RandomMap::default(),
link, link,
seq: 0, seq: 0,
} }
@ -257,7 +257,7 @@ impl Peer {
} }
} }
struct Peers(HashMap<RawFd, Peer>); struct Peers(RandomMap<RawFd, Peer>);
impl Peers { impl Peers {
fn get_mut(&mut self, fd: &RawFd) -> Option<&mut Peer> { fn get_mut(&mut self, fd: &RawFd) -> Option<&mut Peer> {
@ -352,7 +352,7 @@ where
signer, signer,
proxy, proxy,
actions: VecDeque::new(), actions: VecDeque::new(),
peers: Peers(HashMap::default()), peers: Peers(RandomMap::default()),
} }
} }

View File

@ -2,10 +2,10 @@
use siphasher::sip::SipHasher13; use siphasher::sip::SipHasher13;
/// A `HashMap` which uses [`fastrand::Rng`] for its random state. /// A `HashMap` which uses [`fastrand::Rng`] for its random state.
pub type HashMap<K, V> = std::collections::HashMap<K, V, RandomState>; pub type RandomMap<K, V> = std::collections::HashMap<K, V, RandomState>;
/// A `HashSet` which uses [`fastrand::Rng`] for its random state. /// A `HashSet` which uses [`fastrand::Rng`] for its random state.
pub type HashSet<K> = std::collections::HashSet<K, RandomState>; pub type RandomSet<K> = std::collections::HashSet<K, RandomState>;
/// Random hasher state. /// Random hasher state.
#[derive(Clone)] #[derive(Clone)]
@ -16,7 +16,7 @@ pub struct RandomState {
impl Default for RandomState { impl Default for RandomState {
fn default() -> Self { fn default() -> Self {
Self::new(fastrand::Rng::new()) Self::new(crate::profile::env::rng())
} }
} }

View File

@ -6,7 +6,7 @@ use std::str::FromStr;
use git_ext::ref_format as format; use git_ext::ref_format as format;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use crate::collections::HashMap; use crate::collections::RandomMap;
use crate::crypto::PublicKey; use crate::crypto::PublicKey;
use crate::storage; use crate::storage;
use crate::storage::refs::Refs; use crate::storage::refs::Refs;
@ -367,9 +367,9 @@ pub mod refs {
} }
/// List remote refs of a project, given the remote URL. /// List remote refs of a project, given the remote URL.
pub fn remote_refs(url: &Url) -> Result<HashMap<RemoteId, Refs>, ListRefsError> { pub fn remote_refs(url: &Url) -> Result<RandomMap<RemoteId, Refs>, ListRefsError> {
let url = url.to_string(); let url = url.to_string();
let mut remotes = HashMap::default(); let mut remotes = RandomMap::default();
let mut remote = git2::Remote::create_detached(url)?; let mut remote = git2::Remote::create_detached(url)?;
remote.connect(git2::Direction::Fetch)?; remote.connect(git2::Direction::Fetch)?;

View File

@ -3,7 +3,7 @@ use std::ops::{Deref, DerefMut};
use localtime::LocalTime; use localtime::LocalTime;
use nonempty::NonEmpty; use nonempty::NonEmpty;
use crate::collections::HashMap; use crate::collections::RandomMap;
use crate::node; use crate::node;
use crate::node::{Address, Alias}; use crate::node::{Address, Alias};
use crate::prelude::Timestamp; use crate::prelude::Timestamp;
@ -11,7 +11,7 @@ use crate::prelude::Timestamp;
/// A map with the ability to randomly select values. /// A map with the ability to randomly select values.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AddressBook<K, V> { pub struct AddressBook<K, V> {
inner: HashMap<K, V>, inner: RandomMap<K, V>,
rng: fastrand::Rng, rng: fastrand::Rng,
} }
@ -19,7 +19,7 @@ impl<K, V> AddressBook<K, V> {
/// Create a new address book. /// Create a new address book.
pub fn new(rng: fastrand::Rng) -> Self { pub fn new(rng: fastrand::Rng) -> Self {
Self { Self {
inner: HashMap::with_hasher(rng.clone().into()), inner: RandomMap::with_hasher(rng.clone().into()),
rng, rng,
} }
} }
@ -61,7 +61,7 @@ impl<K, V> AddressBook<K, V> {
} }
impl<K, V> Deref for AddressBook<K, V> { impl<K, V> Deref for AddressBook<K, V> {
type Target = HashMap<K, V>; type Target = RandomMap<K, V>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.inner &self.inner

View File

@ -37,6 +37,8 @@ pub mod env {
pub const RAD_SOCKET: &str = "RAD_SOCKET"; pub const RAD_SOCKET: &str = "RAD_SOCKET";
/// Passphrase for the encrypted radicle secret key. /// Passphrase for the encrypted radicle secret key.
pub const RAD_PASSPHRASE: &str = "RAD_PASSPHRASE"; pub const RAD_PASSPHRASE: &str = "RAD_PASSPHRASE";
/// RNG seed. Must be convertible to a `u64`.
pub const RAD_RNG_SEED: &str = "RAD_RNG_SEED";
/// Get the radicle passphrase from the environment. /// Get the radicle passphrase from the environment.
pub fn passphrase() -> Option<super::Passphrase> { pub fn passphrase() -> Option<super::Passphrase> {
@ -45,6 +47,17 @@ pub mod env {
}; };
Some(super::Passphrase::from(passphrase)) Some(super::Passphrase::from(passphrase))
} }
/// Get a random number generator from the environment.
pub fn rng() -> fastrand::Rng {
let Ok(seed) = std::env::var(RAD_RNG_SEED) else {
return fastrand::Rng::new();
};
fastrand::Rng::with_seed(
seed.parse()
.expect("env::rng: invalid seed specified in `RAD_RNG_SEED`"),
)
}
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]

View File

@ -14,7 +14,7 @@ use crypto::{PublicKey, Signer, Unverified, Verified};
pub use git::VerifyError; pub use git::VerifyError;
pub use radicle_git_ext::Oid; pub use radicle_git_ext::Oid;
use crate::collections::HashMap; use crate::collections::RandomMap;
use crate::git::ext as git_ext; use crate::git::ext as git_ext;
use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefString}; use crate::git::{refspec::Refspec, PatternString, Qualified, RefError, RefString};
use crate::identity; use crate::identity;
@ -168,7 +168,7 @@ impl fmt::Display for RefUpdate {
/// Project remotes. Tracks the git state of a project. /// Project remotes. Tracks the git state of a project.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Remotes<V>(HashMap<RemoteId, Remote<V>>); pub struct Remotes<V>(RandomMap<RemoteId, Remote<V>>);
impl<V> FromIterator<(RemoteId, Remote<V>)> for Remotes<V> { impl<V> FromIterator<(RemoteId, Remote<V>)> for Remotes<V> {
fn from_iter<T: IntoIterator<Item = (RemoteId, Remote<V>)>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = (RemoteId, Remote<V>)>>(iter: T) -> Self {
@ -177,7 +177,7 @@ impl<V> FromIterator<(RemoteId, Remote<V>)> for Remotes<V> {
} }
impl<V> Deref for Remotes<V> { impl<V> Deref for Remotes<V> {
type Target = HashMap<RemoteId, Remote<V>>; type Target = RandomMap<RemoteId, Remote<V>>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
@ -185,7 +185,7 @@ impl<V> Deref for Remotes<V> {
} }
impl<V> Remotes<V> { impl<V> Remotes<V> {
pub fn new(remotes: HashMap<RemoteId, Remote<V>>) -> Self { pub fn new(remotes: RandomMap<RemoteId, Remote<V>>) -> Self {
Self(remotes) Self(remotes)
} }
} }
@ -202,7 +202,7 @@ impl Remotes<Verified> {
impl<V> Default for Remotes<V> { impl<V> Default for Remotes<V> {
fn default() -> Self { fn default() -> Self {
Self(HashMap::default()) Self(RandomMap::default())
} }
} }
@ -215,9 +215,9 @@ impl<V> IntoIterator for Remotes<V> {
} }
} }
impl<V> From<Remotes<V>> for HashMap<RemoteId, Refs> { impl<V> From<Remotes<V>> for RandomMap<RemoteId, Refs> {
fn from(other: Remotes<V>) -> Self { fn from(other: Remotes<V>) -> Self {
let mut remotes = HashMap::with_hasher(fastrand::Rng::new().into()); let mut remotes = RandomMap::with_hasher(fastrand::Rng::new().into());
for (k, v) in other.into_iter() { for (k, v) in other.into_iter() {
remotes.insert(k, v.refs.into()); remotes.insert(k, v.refs.into());

View File

@ -9,7 +9,7 @@ use crypto::{PublicKey, Unverified, Verified};
use nonempty::NonEmpty; use nonempty::NonEmpty;
use qcheck::Arbitrary; use qcheck::Arbitrary;
use crate::collections::HashMap; use crate::collections::RandomMap;
use crate::identity::{ use crate::identity::{
doc::{Doc, Id}, doc::{Doc, Id},
project::Project, project::Project,
@ -82,7 +82,7 @@ pub fn gen<T: Arbitrary>(size: usize) -> T {
impl Arbitrary for storage::Remotes<crypto::Verified> { impl Arbitrary for storage::Remotes<crypto::Verified> {
fn arbitrary(g: &mut qcheck::Gen) -> Self { fn arbitrary(g: &mut qcheck::Gen) -> Self {
let remotes: HashMap<storage::RemoteId, storage::Remote<crypto::Verified>> = let remotes: RandomMap<storage::RemoteId, storage::Remote<crypto::Verified>> =
Arbitrary::arbitrary(g); Arbitrary::arbitrary(g);
storage::Remotes::new(remotes) storage::Remotes::new(remotes)