substrate_stellar_sdk/xdr/
streams.rs

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