Skip to main content

jam_std_common/util/
immutable.rs

1use crate::{BytesCursor, RecordedInput, hash_raw};
2use bytes::Bytes;
3use castaway::cast;
4use codec::{Decode, Encode, Input, MaxEncodedLen};
5use jam_types::{Hash, Wrap};
6use std::{borrow::Borrow, ops::Deref};
7
8/// `HashOrNoHash` is implemented for hash types (eg `WorkPackageHash`) and also `NoHash`.
9///
10/// `NoHash` is a unit type, used when we don't care about the hash in question.
11pub trait HashOrNoHash {
12	/// Returns the hash of `data`.
13	fn hash(data: &[u8]) -> Self;
14}
15
16#[derive(Clone, Copy, Debug)]
17pub struct NoHash;
18
19impl HashOrNoHash for NoHash {
20	fn hash(_data: &[u8]) -> Self {
21		Self
22	}
23}
24
25impl<T: From<Hash>> HashOrNoHash for T {
26	fn hash(data: &[u8]) -> Self {
27		hash_raw(data).into()
28	}
29}
30
31/// An `Immutable<T, H>` stores an _immutable_ `T` plus various derived values.
32///
33/// One of the derived values is the hash of the encoded `T`, which has type `H`. `H` may be
34/// `NoHash` if we do not care about this hash.
35#[derive(Clone, Debug)]
36pub struct Immutable<T, H = NoHash> {
37	value: T,
38	/// `value.encode()`.
39	encoded: Bytes,
40	/// Hash of `encoded`. This may have type `NoHash`.
41	hash: H,
42}
43
44impl<T, H: HashOrNoHash> Immutable<T, H> {
45	/// Construct an `Immutable<T, H>` from a `T`, its hash and its encoding.
46	pub fn from_parts(value: T, hash: H, encoded: Bytes) -> Self {
47		Self { value, encoded, hash }
48	}
49
50	/// Construct an `Immutable<T, H>` from a `T` and its encoding.
51	pub fn from_value_and_encoded(value: T, encoded: Bytes) -> Self {
52		let hash = H::hash(&encoded);
53		Self { value, encoded, hash }
54	}
55
56	/// Returns the encoding of the wrapped value.
57	pub fn encoded(&self) -> &Bytes {
58		&self.encoded
59	}
60
61	/// Returns the length of the encoding of the wrapped value.
62	pub fn encoded_len(&self) -> usize {
63		self.encoded.len()
64	}
65
66	/// Returns the wrapped value.
67	pub fn into_value(self) -> T {
68		self.value
69	}
70
71	/// Returns the encoding of the wrapped value.
72	#[allow(dead_code)]
73	pub fn into_encoded(self) -> Bytes {
74		self.encoded
75	}
76
77	/// Returns the wrapped value and its encoding.
78	pub fn into_value_and_encoded(self) -> (T, Bytes) {
79		(self.value, self.encoded)
80	}
81
82	/// Returns the wrapped value, hash and its encoding.
83	pub fn into_parts(self) -> (T, Bytes, H) {
84		(self.value, self.encoded, self.hash)
85	}
86}
87
88// The Into<Hash> bound is to avoid exposing the hash() method when H is NoHash
89impl<T, H: Into<Hash> + Copy> Immutable<T, H> {
90	/// Returns the hash of the encoding of the wrapped value.
91	pub fn hash(&self) -> H {
92		self.hash
93	}
94}
95
96// This is provided primarily for tests...
97impl<T: Encode, H: HashOrNoHash> From<T> for Immutable<T, H> {
98	fn from(value: T) -> Self {
99		let encoded = value.encode().into();
100		Self::from_value_and_encoded(value, encoded)
101	}
102}
103
104impl<T: Encode, H: HashOrNoHash> Immutable<T, H> {
105	pub fn modify_and_encode(&mut self, f: impl FnOnce(&mut T)) {
106		f(&mut self.value);
107		self.encoded = self.value.encode().into();
108		self.hash = H::hash(&self.encoded);
109	}
110}
111
112impl<T, H> Deref for Immutable<T, H> {
113	type Target = T;
114
115	fn deref(&self) -> &T {
116		&self.value
117	}
118}
119
120impl<T, H> Borrow<T> for Immutable<T, H> {
121	fn borrow(&self) -> &T {
122		&self.value
123	}
124}
125
126impl<T: Decode, H: HashOrNoHash> Decode for Immutable<T, H> {
127	fn decode<I: Input>(input: &mut I) -> Result<Self, codec::Error> {
128		let (value, encoded) = match cast!(input, &mut BytesCursor) {
129			Ok(input) => {
130				let mut encoded = input.remaining().clone();
131				let value = T::decode(input)?;
132				encoded.truncate(encoded.len() - input.infallible_remaining_len());
133				(value, encoded)
134			},
135			Err(input) => {
136				let mut input = RecordedInput::new(input);
137				let value = T::decode(&mut input)?;
138				debug_assert!(!input.failed());
139				let encoded: Vec<_> = input.into_record();
140				(value, encoded.into())
141			},
142		};
143		let hash = H::hash(&encoded);
144		Ok(Self { value, encoded, hash })
145	}
146
147	fn skip<I: Input>(input: &mut I) -> Result<(), codec::Error> {
148		T::skip(input)
149	}
150
151	fn encoded_fixed_size() -> Option<usize> {
152		T::encoded_fixed_size()
153	}
154}
155
156impl<T, H> Encode for Immutable<T, H> {
157	fn size_hint(&self) -> usize {
158		self.encoded.len()
159	}
160
161	fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
162		f(&self.encoded)
163	}
164
165	fn encoded_size(&self) -> usize {
166		self.encoded.len()
167	}
168}
169
170impl<T: MaxEncodedLen, H> MaxEncodedLen for Immutable<T, H> {
171	fn max_encoded_len() -> usize {
172		T::max_encoded_len()
173	}
174}
175
176// These traits are derived to avoid running into issues with Rust's poor handling of derive with
177// generic parameters; see https://github.com/rust-lang/rust/issues/26925
178#[derive(Clone, Debug)]
179pub struct ImmutableWrap;
180
181impl Wrap for ImmutableWrap {
182	type Wrap<T> = Immutable<T>;
183}
184
185#[cfg(test)]
186mod tests {
187	use super::*;
188	use codec::DecodeAll;
189	use jam_types::NoWrap;
190
191	#[derive(Encode, Decode, Debug, PartialEq, Eq)]
192	struct Inner(Bytes);
193
194	#[derive(Encode, Decode, Debug, PartialEq, Eq)]
195	struct WrappedOuter<W: Wrap> {
196		a: W::Wrap<Inner>,
197		b: W::Wrap<Inner>,
198	}
199
200	type Outer = WrappedOuter<NoWrap>;
201	type ImmutableOuter = Immutable<WrappedOuter<ImmutableWrap>, Hash>;
202
203	fn contains(outer: &[u8], inner: &[u8]) -> bool {
204		let outer = outer.as_ptr_range();
205		let inner = inner.as_ptr_range();
206		(outer.start <= inner.start) && (inner.end <= outer.end)
207	}
208
209	#[test]
210	fn basic_operation() {
211		let outer =
212			Outer { a: Inner(Bytes::from_static(b"polka")), b: Inner(Bytes::from_static(b"jam!")) };
213		let encoded_outer = outer.encode();
214		assert_eq!(outer, Outer::decode_all(&mut encoded_outer.as_slice()).unwrap());
215
216		let encoded_outer = Bytes::from(encoded_outer);
217		let immutable_outer: ImmutableOuter =
218			BytesCursor::from(encoded_outer.clone()).decode_all().unwrap();
219		assert_eq!(immutable_outer.a.0, outer.a.0);
220		assert_eq!(immutable_outer.b.0, outer.b.0);
221		assert_eq!(immutable_outer.encoded(), &encoded_outer);
222		assert_eq!(immutable_outer.hash(), hash_raw(&encoded_outer));
223		assert_eq!(immutable_outer.a.encoded(), &outer.a.encode());
224		assert_eq!(immutable_outer.b.encoded(), &outer.b.encode());
225		assert!(contains(&encoded_outer, immutable_outer.encoded()));
226		assert!(contains(&encoded_outer, immutable_outer.a.encoded()));
227		assert!(contains(&encoded_outer, immutable_outer.b.encoded()));
228		assert!(contains(&encoded_outer, &immutable_outer.a.0));
229		assert!(contains(&encoded_outer, &immutable_outer.b.0));
230	}
231}