substrate_stellar_xdr/
streams.rs

1//! Streams for efficient encoding and decoding
2
3use core::convert::{AsRef, TryInto};
4use core::iter;
5
6use sp_std::vec::Vec;
7
8fn extend_to_multiple_of_4(value: usize) -> usize {
9    (value + 3) & !3
10}
11
12/// An error type for decoding XDR data
13#[derive(Debug)]
14pub enum DecodeError {
15    /// The XDR data ends too early.
16    ///
17    /// The decoder expects more bytes to decode the data successfully
18    /// The actual length and the expected length are given by `actual_length` and
19    /// `expected_length`
20    SuddenEnd {
21        actual_length: usize,
22        expected_length: usize,
23    },
24
25    /// There binary data is longer than expected
26    ///
27    /// The XDR is self delimiting and would end earlier than the length of the provided
28    /// binary data. The number of remaining bytes is given by `remaining_no_of_bytes`
29    TypeEndsTooEarly { remaining_no_of_bytes: isize },
30
31    /// The XDR contains an invalid boolean
32    ///
33    /// The boolean is neither encoded as 0 or 1. The value found is given by `found_integer`.
34    InvalidBoolean {
35        found_integer: i32,
36        at_position: usize,
37    },
38
39    /// The XDR contains a "Var Opaque" whose length exceeds the specified maximal length
40    VarOpaqueExceedsMaxLength {
41        at_position: usize,
42        max_length: i32,
43        actual_length: i32,
44    },
45
46    /// The XDR contains a string whose length exceeds the specified maximal length
47    StringExceedsMaxLength {
48        at_position: usize,
49        max_length: i32,
50        actual_length: i32,
51    },
52
53    /// The XDR contains a "Var Array" whose length exceeds the specified maximal length
54    VarArrayExceedsMaxLength {
55        at_position: usize,
56        max_length: i32,
57        actual_length: i32,
58    },
59
60    /// The XDR contains an in invalid "Optional"
61    ///
62    /// The "optional" is neither encoded as 0 or 1. The value found is given by `has_code`.
63    InvalidOptional { at_position: usize, has_code: u32 },
64
65    /// The XDR contains an enum with an invalid discriminator
66    ///
67    /// The discriminator does not have one of the allowed values
68    InvalidEnumDiscriminator { at_position: usize },
69
70    /// The base64 encoding of the binary XDR is invalid
71    InvalidBase64,
72}
73
74/// An helper structure for efficiently decoding XDR data
75pub struct ReadStream<T: AsRef<[u8]>> {
76    read_index: usize,
77    source: T,
78}
79
80impl<T: AsRef<[u8]>> ReadStream<T> {
81    /// Create a new `ReadStream` from a reference to a byte slice
82    pub fn new(source: T) -> ReadStream<T> {
83        ReadStream {
84            read_index: 0,
85            source,
86        }
87    }
88
89    fn ensure_size(&self, no_of_bytes_to_read: usize) -> Result<(), DecodeError> {
90        if no_of_bytes_to_read + self.read_index > self.source.as_ref().len() {
91            return Err(self.generate_sudden_end_error(no_of_bytes_to_read));
92        }
93        Ok(())
94    }
95
96    fn generate_sudden_end_error(&self, no_of_bytes_to_read: usize) -> DecodeError {
97        DecodeError::SuddenEnd {
98            actual_length: self.source.as_ref().len(),
99            expected_length: no_of_bytes_to_read + self.read_index,
100        }
101    }
102
103    fn read_next_byte_array<const N: usize>(&mut self) -> Result<&[u8; N], DecodeError> {
104        let array: Result<&[u8; N], _> =
105            (self.source.as_ref()[self.read_index..self.read_index + N]).try_into();
106
107        match array {
108            Ok(array) => {
109                self.read_index += N;
110                Ok(array)
111            }
112            Err(_) => Err(self.generate_sudden_end_error(N)),
113        }
114    }
115
116    /// Read the next big endian u32 from the stream
117    pub fn read_next_u32(&mut self) -> Result<u32, DecodeError> {
118        let array: &[u8; 4] = self.read_next_byte_array()?;
119        Ok(u32::from_be_bytes(*array))
120    }
121
122    /// Read the next big endian i32 from the stream
123    pub fn read_next_i32(&mut self) -> Result<i32, DecodeError> {
124        let array: &[u8; 4] = self.read_next_byte_array()?;
125        Ok(i32::from_be_bytes(*array))
126    }
127
128    /// Read the next big endian u64 from the stream
129    pub fn read_next_u64(&mut self) -> Result<u64, DecodeError> {
130        let array: &[u8; 8] = self.read_next_byte_array()?;
131        Ok(u64::from_be_bytes(*array))
132    }
133
134    /// Read the next big endian i64 from the stream
135    pub fn read_next_i64(&mut self) -> Result<i64, DecodeError> {
136        let array: &[u8; 8] = self.read_next_byte_array()?;
137        Ok(i64::from_be_bytes(*array))
138    }
139
140    /// Read the next array of binary data from the stream
141    ///
142    /// The no of bytes to read are given by `no_of_bytes`. The internal pointer
143    /// of the `ReadStream` is advanced by a multiple of 4.
144    pub fn read_next_binary_data(&mut self, no_of_bytes: usize) -> Result<Vec<u8>, DecodeError> {
145        self.ensure_size(extend_to_multiple_of_4(no_of_bytes))?;
146        let result = self.source.as_ref()[self.read_index..self.read_index + no_of_bytes].to_vec();
147        self.read_index += extend_to_multiple_of_4(no_of_bytes);
148        Ok(result)
149    }
150
151    /// Determine the number of bytes left to be read from the stream
152    pub fn no_of_bytes_left_to_read(&self) -> isize {
153        self.source.as_ref().len() as isize - self.read_index as isize
154    }
155
156    /// Get the current pointer position of the `ReadStream`
157    pub fn get_position(&self) -> usize {
158        self.read_index
159    }
160}
161
162/// An helper structure for efficiently encoding XDR data
163pub struct WriteStream {
164    result: Vec<u8>,
165}
166
167impl WriteStream {
168    /// Construct a new `WriteStream`
169    pub fn new() -> WriteStream {
170        WriteStream {
171            result: Vec::with_capacity(128),
172        }
173    }
174
175    /// Append a new big endian u32 to the stream
176    pub fn write_next_u32(&mut self, value: u32) {
177        self.result.extend(value.to_be_bytes().iter());
178    }
179
180    /// Append a new big endian i32 to the stream
181    pub fn write_next_i32(&mut self, value: i32) {
182        self.result.extend(value.to_be_bytes().iter());
183    }
184
185    /// Append a new big endian u64 to the stream
186    pub fn write_next_u64(&mut self, value: u64) {
187        self.result.extend(value.to_be_bytes().iter());
188    }
189
190    /// Append a new big endian i64 to the stream
191    pub fn write_next_i64(&mut self, value: i64) {
192        self.result.extend(value.to_be_bytes().iter());
193    }
194
195    /// Append an array of binary data to the stream
196    pub fn write_next_binary_data(&mut self, value: &[u8]) {
197        self.result.extend_from_slice(value);
198        let length = value.len();
199        let no_of_padding_bytes = extend_to_multiple_of_4(length) - length;
200        self.result
201            .extend(iter::repeat(0).take(no_of_padding_bytes));
202    }
203
204    /// Get the result written to the stream
205    pub fn get_result(self) -> Vec<u8> {
206        self.result
207    }
208}