Skip to main content

dope_runtime/random/
mod.rs

1//! Runtime-owned randomness bound to one exact generative driver domain.
2
3use std::{hash, marker, mem};
4
5use siphasher::sip;
6
7mod seed;
8
9pub(crate) use seed::Seed;
10
11/// Keyed hash builder issued for one exact generative driver domain.
12///
13/// The driver lifetime is invariant and cannot be widened:
14///
15/// ```compile_fail
16/// use dope_runtime::random::HashState;
17///
18/// fn escape<'d>(state: HashState<'d>) -> HashState<'static> {
19///     state
20/// }
21/// ```
22///
23/// Construction is reserved for a runtime session holding the matching brand:
24///
25/// ```compile_fail,E0624
26/// use dope_runtime::random::HashState;
27///
28/// let _ = HashState::new([1, 2]);
29/// ```
30///
31/// Building a hasher preserves the same invariant driver lifetime:
32///
33/// ```compile_fail
34/// use std::hash::BuildHasher;
35/// use dope_runtime::random::{HashState, Hasher};
36///
37/// fn erase<'d>(state: HashState<'d>) -> Hasher<'static> {
38///     state.build_hasher()
39/// }
40/// ```
41#[derive(Clone, Copy)]
42pub struct HashState<'d> {
43    words: [u64; 2],
44    _driver: marker::PhantomData<fn(&'d ()) -> &'d ()>,
45    _thread: o3::ThreadBound,
46}
47
48impl<'d> HashState<'d> {
49    const fn new(words: [u64; 2]) -> Self {
50        Self {
51            words,
52            _driver: marker::PhantomData,
53            _thread: o3::ThreadBound::NEW,
54        }
55    }
56}
57
58impl<'d> hash::BuildHasher for HashState<'d> {
59    type Hasher = Hasher<'d>;
60
61    fn build_hasher(&self) -> Self::Hasher {
62        Hasher::new(sip::SipHasher13::new_with_keys(
63            self.words[0],
64            self.words[1],
65        ))
66    }
67}
68
69const _: () = assert!(mem::size_of::<HashState<'static>>() == 2 * mem::size_of::<u64>());
70
71/// Hashing state retaining the exact driver brand of its key source.
72#[doc(hidden)]
73pub struct Hasher<'d> {
74    inner: sip::SipHasher13,
75    _driver: marker::PhantomData<fn(&'d ()) -> &'d ()>,
76    _thread: o3::ThreadBound,
77}
78
79impl<'d> Hasher<'d> {
80    const fn new(inner: sip::SipHasher13) -> Self {
81        Self {
82            inner,
83            _driver: marker::PhantomData,
84            _thread: o3::ThreadBound::NEW,
85        }
86    }
87}
88
89impl hash::Hasher for Hasher<'_> {
90    fn finish(&self) -> u64 {
91        self.inner.finish()
92    }
93
94    fn write(&mut self, bytes: &[u8]) {
95        self.inner.write(bytes);
96    }
97
98    fn write_u8(&mut self, value: u8) {
99        self.inner.write_u8(value);
100    }
101
102    fn write_u16(&mut self, value: u16) {
103        self.inner.write_u16(value);
104    }
105
106    fn write_u32(&mut self, value: u32) {
107        self.inner.write_u32(value);
108    }
109
110    fn write_u64(&mut self, value: u64) {
111        self.inner.write_u64(value);
112    }
113
114    fn write_usize(&mut self, value: usize) {
115        self.inner.write_usize(value);
116    }
117}
118
119const _: () = assert!(mem::size_of::<Hasher<'static>>() == mem::size_of::<sip::SipHasher13>());
120
121/// Stable separation between independent uses of one worker seed.
122#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123#[repr(transparent)]
124pub struct Domain(u64);
125
126impl Domain {
127    pub const fn new(value: u64) -> Self {
128        Self(value)
129    }
130
131    pub const fn from_bytes(value: [u8; 8]) -> Self {
132        Self(u64::from_be_bytes(value))
133    }
134
135    pub(super) const fn get(self) -> u64 {
136        self.0
137    }
138}
139
140const _: () = assert!(mem::size_of::<Domain>() == mem::size_of::<u64>());