concordium_contracts_common/traits.rs
1use crate::types::*;
2#[cfg(not(feature = "std"))]
3use alloc::vec::Vec;
4use core::{default::Default, mem::MaybeUninit, slice};
5
6/// This is essentially equivalent to the
7/// [SeekFrom](https://doc.rust-lang.org/std/io/enum.SeekFrom.html) type from
8/// the rust standard library, but reproduced here to avoid dependency on
9/// `std::io`, as well as to use 32-bit integers to specify positions. This
10/// saves some computation and space, and is adequate for the kind of data sizes
11/// that are possible in smart contracts.
12pub enum SeekFrom {
13 Start(u32),
14 End(i32),
15 Current(i32),
16}
17
18/// The `Seek` trait provides a cursor which can be moved within a stream of
19/// bytes. This is essentially a copy of
20/// [std::io::Seek](https://doc.rust-lang.org/std/io/trait.Seek.html), but
21/// avoiding its dependency on `std::io::Error`, and the associated code size
22/// increase. Additionally, the positions are expressed in terms of 32-bit
23/// integers since this is adequate for the sizes of data in smart contracts.
24pub trait Seek {
25 type Err;
26 /// Seek to the new position. If successful, return the new position from
27 /// the beginning of the stream.
28 fn seek(&mut self, pos: SeekFrom) -> Result<u32, Self::Err>;
29
30 /// Get the cursor position counted from the beginning of the stream.
31 fn cursor_position(&self) -> u32;
32}
33
34/// The `HasSize` trait provides a function for getting the current byte size.
35pub trait HasSize {
36 /// Get the current byte size.
37 fn size(&self) -> u32;
38}
39
40/// Reads `n` bytes from a given `source` without initializing the byte array
41/// beforehand using MaybeUninit.
42macro_rules! read_n_bytes {
43 ($n:expr, $source:tt) => {{
44 let mut bytes: MaybeUninit<[u8; $n]> = MaybeUninit::uninit();
45 let write_bytes = unsafe { slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u8, $n) };
46 $source.read_exact(write_bytes)?;
47 unsafe { bytes.assume_init() }
48 }};
49}
50
51/// The `Read` trait provides a means of reading from byte streams.
52pub trait Read {
53 /// Read a number of bytes into the provided buffer. The returned value is
54 /// `Ok(n)` if a read was successful, and `n` bytes were read (`n` could be
55 /// 0).
56 fn read(&mut self, buf: &mut [u8]) -> ParseResult<usize>;
57
58 /// Read exactly the required number of bytes. If not enough bytes could be
59 /// read the function returns `Err(_)`, and the contents of the given buffer
60 /// is unspecified.
61 fn read_exact(&mut self, buf: &mut [u8]) -> ParseResult<()> {
62 let mut start = 0;
63 while start < buf.len() {
64 match self.read(&mut buf[start..]) {
65 Ok(0) => break,
66 Ok(n) => {
67 start += n;
68 }
69 Err(_e) => return Err(Default::default()),
70 }
71 }
72 if start == buf.len() {
73 Ok(())
74 } else {
75 Err(Default::default())
76 }
77 }
78
79 /// Read a `u64` in little-endian format.
80 fn read_u64(&mut self) -> ParseResult<u64> {
81 let bytes = read_n_bytes!(8, self);
82 Ok(u64::from_le_bytes(bytes))
83 }
84
85 /// Read a `u32` in little-endian format.
86 fn read_u32(&mut self) -> ParseResult<u32> {
87 let bytes = read_n_bytes!(4, self);
88 Ok(u32::from_le_bytes(bytes))
89 }
90
91 /// Read a `u16` in little-endian format.
92 fn read_u16(&mut self) -> ParseResult<u16> {
93 let bytes = read_n_bytes!(2, self);
94 Ok(u16::from_le_bytes(bytes))
95 }
96
97 /// Read a `u8`.
98 fn read_u8(&mut self) -> ParseResult<u8> {
99 let bytes = read_n_bytes!(1, self);
100 Ok(u8::from_le_bytes(bytes))
101 }
102
103 /// Read a `i64` in little-endian format.
104 fn read_i64(&mut self) -> ParseResult<i64> {
105 let bytes = read_n_bytes!(8, self);
106 Ok(i64::from_le_bytes(bytes))
107 }
108
109 /// Read a `i32` in little-endian format.
110 fn read_i32(&mut self) -> ParseResult<i32> {
111 let bytes = read_n_bytes!(4, self);
112 Ok(i32::from_le_bytes(bytes))
113 }
114
115 /// Read a `i16` in little-endian format.
116 fn read_i16(&mut self) -> ParseResult<i16> {
117 let bytes = read_n_bytes!(2, self);
118 Ok(i16::from_le_bytes(bytes))
119 }
120
121 /// Read a `i32` in little-endian format.
122 fn read_i8(&mut self) -> ParseResult<i8> {
123 let bytes = read_n_bytes!(1, self);
124 Ok(i8::from_le_bytes(bytes))
125 }
126
127 /// Load an array of the given size.
128 fn read_array<const N: usize>(&mut self) -> ParseResult<[u8; N]> { Ok(read_n_bytes!(N, self)) }
129}
130
131/// The `Write` trait provides functionality for writing to byte streams.
132pub trait Write {
133 type Err: Default;
134 /// Try to write the given buffer into the output stream. If writes are
135 /// successful returns the number of bytes written.
136 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Err>;
137
138 /// Attempt to write the entirety of the buffer to the output by repeatedly
139 /// calling `write` until either no more output can written, or writing
140 /// fails.
141 fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Err> {
142 let mut start = 0;
143 while start < buf.len() {
144 match self.write(&buf[start..]) {
145 Ok(n) if n > 0 => start += n,
146 _ => return Err(Default::default()),
147 }
148 }
149 Ok(())
150 }
151
152 /// Write a single byte to the output.
153 fn write_u8(&mut self, x: u8) -> Result<(), Self::Err> { self.write_all(&x.to_le_bytes()) }
154
155 /// Write a `u16` in little endian.
156 fn write_u16(&mut self, x: u16) -> Result<(), Self::Err> { self.write_all(&x.to_le_bytes()) }
157
158 /// Write a `u32` in little endian.
159 fn write_u32(&mut self, x: u32) -> Result<(), Self::Err> { self.write_all(&x.to_le_bytes()) }
160
161 /// Write a `u64` in little endian.
162 fn write_u64(&mut self, x: u64) -> Result<(), Self::Err> { self.write_all(&x.to_le_bytes()) }
163
164 /// Write a `i8` to the output.
165 fn write_i8(&mut self, x: i8) -> Result<(), Self::Err> { self.write_all(&x.to_le_bytes()) }
166
167 /// Write a `i16` in little endian.
168 fn write_i16(&mut self, x: i16) -> Result<(), Self::Err> { self.write_all(&x.to_le_bytes()) }
169
170 /// Write a `i32` in little endian.
171 fn write_i32(&mut self, x: i32) -> Result<(), Self::Err> { self.write_all(&x.to_le_bytes()) }
172
173 /// Write a `i64` in little endian.
174 fn write_i64(&mut self, x: i64) -> Result<(), Self::Err> { self.write_all(&x.to_le_bytes()) }
175}
176
177/// The `write` method always appends data to the end of the vector.
178impl Write for Vec<u8> {
179 type Err = ();
180
181 #[inline]
182 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Err> {
183 self.extend_from_slice(buf);
184 Ok(buf.len())
185 }
186}
187
188/// This implementation overwrite the contents of the slice and updates the
189/// reference to point to the unwritten part. The slice is (by necessity) never
190/// extended.
191/// This is in contrast to the `Vec<u8>` implementation which always extends the
192/// vector with the data that is written.
193impl Write for &mut [u8] {
194 type Err = ();
195
196 #[inline]
197 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Err> {
198 let to_write = core::cmp::min(buf.len(), self.len());
199 #[allow(clippy::mem_replace_with_default)]
200 let (overwrite, rest) = core::mem::replace(self, &mut []).split_at_mut(to_write);
201 overwrite.copy_from_slice(&buf[..to_write]);
202 *self = rest;
203 Ok(to_write)
204 }
205}
206
207/// The `Serial` trait provides a means of writing structures into byte-sinks
208/// (`Write`).
209///
210/// Can be derived using `#[derive(Serial)]` for most cases.
211pub trait Serial {
212 /// Attempt to write the structure into the provided writer, failing if
213 /// only part of the structure could be written.
214 ///
215 /// NB: We use Result instead of Option for better composability with other
216 /// constructs.
217 fn serial<W: Write>(&self, _out: &mut W) -> Result<(), W::Err>;
218}
219
220/// The `Deserial` trait provides a means of reading structures from
221/// byte-sources (`Read`).
222///
223/// Can be derived using `#[derive(Deserial)]` for most cases.
224pub trait Deserial: Sized {
225 /// Attempt to read a structure from a given source, failing if an error
226 /// occurs during deserialization or reading.
227 fn deserial<R: Read>(_source: &mut R) -> ParseResult<Self>;
228}
229
230/// The `Serialize` trait provides a means of writing structures into byte-sinks
231/// (`Write`) or reading structures from byte sources (`Read`).
232///
233/// Can be derived using `#[derive(Serialized)]` for most cases.
234pub trait Serialize: Serial + Deserial {}
235
236/// Generic instance deriving Serialize for any type that implements both Serial
237/// and Deserial.
238impl<A: Deserial + Serial> Serialize for A {}
239
240/// The `SerialCtx` trait provides a means of writing structures into byte-sinks
241/// (`Write`) using contextual information.
242/// The contextual information is:
243///
244/// - `size_length`: The number of bytes used to record the length of the
245/// data.
246pub trait SerialCtx {
247 /// Attempt to write the structure into the provided writer, failing if
248 /// if the length cannot be represented in the provided `size_length` or
249 /// only part of the structure could be written.
250 ///
251 /// NB: We use Result instead of Option for better composability with other
252 /// constructs.
253 fn serial_ctx<W: Write>(
254 &self,
255 size_length: crate::schema::SizeLength,
256 out: &mut W,
257 ) -> Result<(), W::Err>;
258}
259
260/// The `DeserialCtx` trait provides a means of reading structures from
261/// byte-sources (`Read`) using contextual information.
262/// The contextual information is:
263///
264/// - `size_length`: The expected number of bytes used for the length of the
265/// data.
266/// - `ensure_ordered`: Whether the ordering should be ensured, for example
267/// that keys in `BTreeMap` and `BTreeSet` are in strictly increasing order.
268pub trait DeserialCtx: Sized {
269 /// Attempt to read a structure from a given source and context, failing if
270 /// an error occurs during deserialization or reading.
271 fn deserial_ctx<R: Read>(
272 size_length: crate::schema::SizeLength,
273 ensure_ordered: bool,
274 source: &mut R,
275 ) -> ParseResult<Self>;
276}
277
278/// A more convenient wrapper around `Deserial` that makes it easier to write
279/// deserialization code. It has a blanked implementation for any read and
280/// serialize pair. The key idea is that the type to deserialize is inferred
281/// from the context, enabling one to write, for example,
282///
283/// ```rust
284/// # fn deserial<R: concordium_contracts_common::Read>(source: &mut R) -> concordium_contracts_common::ParseResult<(u8, u8)> {
285/// # use crate::concordium_contracts_common::Get;
286/// let x = source.get()?;
287/// let y = source.get()?;
288/// # Ok((x,y))
289/// # }
290/// ```
291/// where `source` is any type that implements `Read`.
292pub trait Get<T> {
293 fn get(&mut self) -> ParseResult<T>;
294}
295
296impl<R: Read, T: Deserial> Get<T> for R {
297 #[inline(always)]
298 fn get(&mut self) -> ParseResult<T> { T::deserial(self) }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304 #[test]
305 fn write_u8_slice() {
306 let mut xs = [0u8; 10];
307 let mut slice: &mut [u8] = &mut xs;
308 assert!(0xAAAAAAAAu32.serial(&mut slice).is_ok(), "Writing u32 should succeed.");
309 assert_eq!(slice.len(), 6, "The new slice should be of length 6 (= 10 - 4)");
310 assert!(0xBBBBBBBBu32.serial(&mut slice).is_ok(), "Writing the second u32 should succeed.");
311 assert_eq!(slice.len(), 2, "The new slice should be of length 2 (= 10 - 4 - 4)");
312 assert!(0xCCCCu16.serial(&mut slice).is_ok(), "Writing the final u16 should succeed.");
313 assert_eq!(slice.len(), 0, "The new slice should be of length 0 (= 10 - 4 - 4 - 2)");
314 assert!(0u8.serial(&mut slice).is_err(), "Writing past the end should fail.");
315 assert_eq!(
316 xs,
317 [0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB, 0xCC, 0xCC],
318 "The original array has incorrect content."
319 );
320 }
321}