Skip to main content

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}