precompile_utils/solidity/codec/
bytes.rs

1// This file is part of Frontier.
2
3// Copyright (c) Moonsong Labs.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0
6
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// 	http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18
19use super::*;
20use alloc::borrow::ToOwned;
21use sp_core::{ConstU32, Get};
22
23type ConstU32Max = ConstU32<{ u32::MAX }>;
24
25pub type UnboundedBytes = BoundedBytesString<BytesKind, ConstU32Max>;
26pub type BoundedBytes<S> = BoundedBytesString<BytesKind, S>;
27
28pub type UnboundedString = BoundedBytesString<StringKind, ConstU32Max>;
29pub type BoundedString<S> = BoundedBytesString<StringKind, S>;
30
31trait Kind {
32	fn signature() -> String;
33}
34
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub struct BytesKind;
37
38impl Kind for BytesKind {
39	fn signature() -> String {
40		String::from("bytes")
41	}
42}
43
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct StringKind;
46
47impl Kind for StringKind {
48	fn signature() -> String {
49		String::from("string")
50	}
51}
52
53/// The `bytes/string` type of Solidity.
54/// It is different from `Vec<u8>` which will be serialized with padding for each `u8` element
55/// of the array, while `Bytes` is tightly packed.
56#[derive(Debug)]
57pub struct BoundedBytesString<K, S> {
58	data: Vec<u8>,
59	_phantom: PhantomData<(K, S)>,
60}
61
62impl<K: Kind, S: Get<u32>> Clone for BoundedBytesString<K, S> {
63	fn clone(&self) -> Self {
64		Self {
65			data: self.data.clone(),
66			_phantom: PhantomData,
67		}
68	}
69}
70
71impl<K1, S1, K2, S2> PartialEq<BoundedBytesString<K2, S2>> for BoundedBytesString<K1, S1> {
72	fn eq(&self, other: &BoundedBytesString<K2, S2>) -> bool {
73		self.data.eq(&other.data)
74	}
75}
76
77impl<K, S> Eq for BoundedBytesString<K, S> {}
78
79impl<K, S> Default for BoundedBytesString<K, S> {
80	fn default() -> Self {
81		Vec::default().into()
82	}
83}
84
85impl<K, S: Get<u32>> BoundedBytesString<K, S> {
86	pub fn as_bytes(&self) -> &[u8] {
87		&self.data
88	}
89
90	pub fn as_str(&self) -> Result<&str, core::str::Utf8Error> {
91		core::str::from_utf8(&self.data)
92	}
93}
94
95impl<K: Kind, S: Get<u32>> Codec for BoundedBytesString<K, S> {
96	fn read(reader: &mut Reader) -> MayRevert<Self> {
97		let mut inner_reader = reader.read_pointer()?;
98
99		// Read bytes/string size.
100		let array_size: usize = inner_reader
101			.read::<U256>()
102			.map_err(|_| RevertReason::read_out_of_bounds("length"))?
103			.try_into()
104			.map_err(|_| RevertReason::value_is_too_large("length"))?;
105
106		if array_size > S::get() as usize {
107			return Err(RevertReason::value_is_too_large("length").into());
108		}
109
110		// Get valid range over the bytes data.
111		let range = inner_reader.move_cursor(array_size)?;
112
113		let data = inner_reader
114			.input
115			.get(range)
116			.ok_or_else(|| RevertReason::read_out_of_bounds(K::signature()))?;
117
118		let bytes = Self {
119			data: data.to_owned(),
120			_phantom: PhantomData,
121		};
122
123		Ok(bytes)
124	}
125
126	fn write(writer: &mut Writer, value: Self) {
127		let value: Vec<_> = value.into();
128		let length = value.len();
129
130		// Pad the data.
131		// Leave it as is if a multiple of 32, otherwise pad to next
132		// multiple or 32.
133		let chunks = length / 32;
134		let padded_size = match length % 32 {
135			0 => chunks * 32,
136			_ => (chunks + 1) * 32,
137		};
138
139		let mut value = value.to_vec();
140		value.resize(padded_size, 0);
141
142		writer.write_pointer(
143			Writer::new()
144				.write(U256::from(length))
145				.write_raw_bytes(&value)
146				.build(),
147		);
148	}
149
150	fn has_static_size() -> bool {
151		false
152	}
153
154	fn signature() -> String {
155		K::signature()
156	}
157}
158
159// BytesString <=> Vec/&[u8]
160
161impl<K, S> From<BoundedBytesString<K, S>> for Vec<u8> {
162	fn from(value: BoundedBytesString<K, S>) -> Self {
163		value.data
164	}
165}
166
167impl<K, S> From<Vec<u8>> for BoundedBytesString<K, S> {
168	fn from(value: Vec<u8>) -> Self {
169		Self {
170			data: value,
171			_phantom: PhantomData,
172		}
173	}
174}
175
176impl<K, S> From<&[u8]> for BoundedBytesString<K, S> {
177	fn from(value: &[u8]) -> Self {
178		Self {
179			data: value.to_vec(),
180			_phantom: PhantomData,
181		}
182	}
183}
184
185impl<K, S, const N: usize> From<[u8; N]> for BoundedBytesString<K, S> {
186	fn from(value: [u8; N]) -> Self {
187		Self {
188			data: value.to_vec(),
189			_phantom: PhantomData,
190		}
191	}
192}
193
194impl<K, S, const N: usize> From<&[u8; N]> for BoundedBytesString<K, S> {
195	fn from(value: &[u8; N]) -> Self {
196		Self {
197			data: value.to_vec(),
198			_phantom: PhantomData,
199		}
200	}
201}
202
203// BytesString <=> String/str
204
205impl<K, S> TryFrom<BoundedBytesString<K, S>> for String {
206	type Error = alloc::string::FromUtf8Error;
207
208	fn try_from(value: BoundedBytesString<K, S>) -> Result<Self, Self::Error> {
209		alloc::string::String::from_utf8(value.data)
210	}
211}
212
213impl<K, S> From<&str> for BoundedBytesString<K, S> {
214	fn from(value: &str) -> Self {
215		Self {
216			data: value.as_bytes().into(),
217			_phantom: PhantomData,
218		}
219	}
220}
221
222impl<K, S> From<String> for BoundedBytesString<K, S> {
223	fn from(value: String) -> Self {
224		Self {
225			data: value.as_bytes().into(),
226			_phantom: PhantomData,
227		}
228	}
229}