referrerpolicy=no-referrer-when-downgrade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

use crate::MAX_XCM_DECODE_DEPTH;
use alloc::vec::Vec;
use codec::{Decode, DecodeLimit, Encode};

/// Wrapper around the encoded and decoded versions of a value.
/// Caches the decoded value once computed.
#[derive(Encode, Decode, scale_info::TypeInfo)]
#[codec(encode_bound())]
#[codec(decode_bound())]
#[scale_info(bounds(), skip_type_params(T))]
#[scale_info(replace_segment("staging_xcm", "xcm"))]
#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
pub struct DoubleEncoded<T> {
	encoded: Vec<u8>,
	#[codec(skip)]
	decoded: Option<T>,
}

impl<T> Clone for DoubleEncoded<T> {
	fn clone(&self) -> Self {
		Self { encoded: self.encoded.clone(), decoded: None }
	}
}

impl<T> PartialEq for DoubleEncoded<T> {
	fn eq(&self, other: &Self) -> bool {
		self.encoded.eq(&other.encoded)
	}
}
impl<T> Eq for DoubleEncoded<T> {}

impl<T> core::fmt::Debug for DoubleEncoded<T> {
	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
		array_bytes::bytes2hex("0x", &self.encoded).fmt(f)
	}
}

impl<T> From<Vec<u8>> for DoubleEncoded<T> {
	fn from(encoded: Vec<u8>) -> Self {
		Self { encoded, decoded: None }
	}
}

impl<T> DoubleEncoded<T> {
	pub fn into<S>(self) -> DoubleEncoded<S> {
		DoubleEncoded::from(self)
	}

	pub fn from<S>(e: DoubleEncoded<S>) -> Self {
		Self { encoded: e.encoded, decoded: None }
	}

	/// Provides an API similar to `AsRef` that provides access to the inner value.
	/// `AsRef` implementation would expect an `&Option<T>` return type.
	pub fn as_ref(&self) -> Option<&T> {
		self.decoded.as_ref()
	}

	/// Access the encoded data.
	pub fn into_encoded(self) -> Vec<u8> {
		self.encoded
	}
}

impl<T: Decode> DoubleEncoded<T> {
	/// Decode the inner encoded value and store it.
	/// Returns a reference to the value in case of success and `Err(())` in case the decoding
	/// fails.
	pub fn ensure_decoded(&mut self) -> Result<&T, ()> {
		if self.decoded.is_none() {
			self.decoded =
				T::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut &self.encoded[..]).ok();
		}
		self.decoded.as_ref().ok_or(())
	}

	/// Move the decoded value out or (if not present) decode `encoded`.
	pub fn take_decoded(&mut self) -> Result<T, ()> {
		self.decoded
			.take()
			.or_else(|| {
				T::decode_all_with_depth_limit(MAX_XCM_DECODE_DEPTH, &mut &self.encoded[..]).ok()
			})
			.ok_or(())
	}

	/// Provides an API similar to `TryInto` that allows fallible conversion to the inner value
	/// type. `TryInto` implementation would collide with std blanket implementation based on
	/// `TryFrom`.
	pub fn try_into(mut self) -> Result<T, ()> {
		self.ensure_decoded()?;
		self.decoded.ok_or(())
	}
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn ensure_decoded_works() {
		let val: u64 = 42;
		let mut encoded: DoubleEncoded<_> = Encode::encode(&val).into();
		assert_eq!(encoded.ensure_decoded(), Ok(&val));
	}

	#[test]
	fn take_decoded_works() {
		let val: u64 = 42;
		let mut encoded: DoubleEncoded<_> = Encode::encode(&val).into();
		assert_eq!(encoded.take_decoded(), Ok(val));
	}

	#[test]
	fn try_into_works() {
		let val: u64 = 42;
		let encoded: DoubleEncoded<_> = Encode::encode(&val).into();
		assert_eq!(encoded.try_into(), Ok(val));
	}
}