Skip to main content

xdrs/txs/
buf_reader.rs

1//! TXS buffer reader implementation.
2//!
3//! This module provides [`BufReader`], which implements [`Reader`]
4//! for reading TXS-encoded XDR data from a byte slice.
5
6use crate::serialize::reader::*;
7
8/// A reader for TXS-encoded XDR data from a byte slice.
9///
10/// This reader implements the [`Reader`] trait for the TXS (Traditional XDR
11/// Serialization) format. It reads data from a byte slice and tracks the
12/// current position.
13pub struct BufReader<'r> {
14    data: &'r [u8],
15    pos: usize,
16}
17
18macro_rules! read_int {
19    [ $($read:ident($nbytes:expr): $ty:ty),* $(,)? ] => { $(
20        #[inline]
21        fn $read(&mut self) -> ReadResult<$ty> {
22            let input = self.get_input($nbytes)?;
23            let mut bytes = [0u8; $nbytes];
24            // SAFETY: get_input($nbytes) returns exactly $nbytes bytes.
25            unsafe { core::ptr::copy_nonoverlapping(input.as_ptr(), bytes.as_mut_ptr(), $nbytes) };
26            let n = <$ty>::from_be_bytes(bytes);
27            Ok(n)
28        }
29    )* };
30}
31
32impl<'a> BufReader<'a> {
33    /// Creates a new `BufReader` that reads from the given byte slice.
34    ///
35    /// The reader starts at position 0.
36    pub fn new(data: &'a [u8]) -> Self {
37        let pos = 0;
38        Self { data, pos }
39    }
40
41    /// Returns the current read position in the buffer.
42    pub fn position(&self) -> usize {
43        self.pos
44    }
45
46    /// Returns the number of bytes remaining to be read.
47    pub fn remaining(&self) -> usize {
48        self.data.len() - self.pos
49    }
50
51    /// Returns a slice of the remaining unread bytes.
52    pub fn remaining_slice(&self) -> &'a [u8] {
53        &self.data[self.pos..]
54    }
55
56    #[inline]
57    fn get_input(&mut self, requested: usize) -> ReadResult<&'a [u8]> {
58        let Some(extent) = self
59            .pos
60            .checked_add(requested)
61            .filter(|&e| e <= self.data.len())
62        else {
63            let remaining: usize = self.data.len() - self.pos;
64            return Err(ReadError::source_empty(requested, remaining));
65        };
66
67        // SAFETY: we just verified extent <= self.data.len() and
68        // self.pos <= extent (since requested >= 0)
69        let buf = unsafe { self.data.get_unchecked(self.pos..extent) };
70
71        self.pos = extent;
72
73        Ok(buf)
74    }
75}
76
77// SAFETY: read_opaque_fixed returns exactly `len` bytes (or an error).
78unsafe impl<'r> Reader<'r> for BufReader<'r> {
79    #[inline]
80    fn read_opaque_fixed(&mut self, len: usize) -> ReadResult<&'r [u8]> {
81        let rem = len % 4;
82        if rem == 0 {
83            return self.get_input(len);
84        }
85
86        let padlen = 4 - rem;
87
88        // SAFETY: padlen <= 3 and get_input(len + padlen) returns exactly
89        // len + padlen initialized bytes. The first `len` bytes are
90        // payload, the trailing `padlen` bytes are padding.
91        let combined = len.checked_add(padlen).ok_or_else(|| {
92            // TODO(geoff): The actual requested amount if this overflow fails
93            // is 2^32, i.e. one more than u32::MAX, on 32-bit targets
94            // This check overflows for i.e. len = u32::MAX, padding=1 on 32-bit targets,
95            // but on such targets we cannot have a buffer of size usize::MAX anyways.
96            ReadError::source_empty(usize::MAX, self.data.len() - self.pos)
97        })?;
98        let buf = self.get_input(combined)?;
99        let payload = unsafe { buf.get_unchecked(..len) };
100
101        #[cfg(not(feature = "permissive"))]
102        if unsafe { buf.get_unchecked(len..) }.iter().any(|&b| b != 0) {
103            return Err(ReadError::non_zero_padding());
104        }
105
106        Ok(payload)
107    }
108
109    #[inline]
110    fn read_opaque_variable(&mut self, max: u32) -> ReadResult<&'r [u8]> {
111        let len = self.read_u32()?;
112
113        if len > max {
114            return Err(ReadError::max_length_exceeded());
115        }
116
117        self.read_opaque_fixed(len as usize)
118    }
119
120    #[inline]
121    fn read_i16(&mut self) -> ReadResult<i16> {
122        // i16 is encoded as a 4-byte big-endian i32. The upper two bytes are
123        // padding: they must be zero (or 0xFF for sign-extension) by default,
124        // but in permissive mode non-zero padding is accepted and the value is
125        // truncated to 16 bits.
126        let n = self.read_i32()?;
127        #[cfg(not(feature = "permissive"))]
128        if n < i16::MIN as i32 || n > i16::MAX as i32 {
129            return Err(ReadError::integer_out_of_range());
130        }
131        Ok(n as i16)
132    }
133
134    #[inline]
135    fn read_u16(&mut self) -> ReadResult<u16> {
136        // u16 is encoded as a 4-byte big-endian u32. The upper two bytes are
137        // padding: they must be zero by default, but in permissive mode
138        // non-zero padding is accepted and the value is truncated to 16 bits.
139        let n = self.read_u32()?;
140        #[cfg(not(feature = "permissive"))]
141        if n < u16::MIN as u32 || n > u16::MAX as u32 {
142            return Err(ReadError::integer_out_of_range());
143        }
144        Ok(n as u16)
145    }
146
147    read_int! [
148        read_u64(8): u64,
149        read_u32(4): u32,
150
151        read_i64(8): i64,
152        read_i32(4): i32,
153    ];
154
155    #[inline]
156    fn read_f32(&mut self) -> ReadResult<f32> {
157        let input = self.get_input(4)?;
158        let mut bytes = [0u8; 4];
159        // SAFETY: get_input(4) returns exactly 4 bytes.
160        unsafe { core::ptr::copy_nonoverlapping(input.as_ptr(), bytes.as_mut_ptr(), 4) };
161        Ok(f32::from_be_bytes(bytes))
162    }
163
164    #[inline]
165    fn read_f64(&mut self) -> ReadResult<f64> {
166        let input = self.get_input(8)?;
167        let mut bytes = [0u8; 8];
168        // SAFETY: get_input(8) returns exactly 8 bytes.
169        unsafe { core::ptr::copy_nonoverlapping(input.as_ptr(), bytes.as_mut_ptr(), 8) };
170        Ok(f64::from_be_bytes(bytes))
171    }
172}