Skip to main content

der/reader/
position.rs

1//! Position tracking for processing nested input messages using only the stack.
2
3use crate::{Error, ErrorKind, Length, Result};
4
5/// State tracker for the current position in the input.
6#[derive(Clone, Debug)]
7pub(super) struct Position {
8    /// Input length (in bytes after Base64 decoding).
9    input_len: Length,
10
11    /// Position in the input buffer (in bytes after Base64 decoding).
12    position: Length,
13
14    /// Number of nested productions encountered (i.e. depth in the call stack).
15    depth: usize,
16}
17
18impl Position {
19    /// Maximum number of nested messages we tolerate (prevents stack exhaustion).
20    const MAX_DEPTH: usize = 64;
21
22    /// Create a new position tracker with the given overall length.
23    pub(super) fn new(input_len: Length) -> Self {
24        Self {
25            input_len,
26            position: Length::ZERO,
27            depth: 0,
28        }
29    }
30
31    /// Advance the current position by the given amount.
32    ///
33    /// Returns the new current position.
34    pub(super) fn advance(&mut self, amount: Length) -> Result<Length> {
35        let new_position = (self.position + amount)?;
36
37        if new_position > self.input_len {
38            return Err(ErrorKind::Incomplete {
39                expected_len: new_position,
40                actual_len: self.input_len,
41            }
42            .at(self.position));
43        }
44
45        self.position = new_position;
46        Ok(new_position)
47    }
48
49    /// Get the current position.
50    pub(super) fn current(&self) -> Length {
51        self.position
52    }
53
54    /// Create new [`Error`] from the given [`ErrorKind`] which includes the current position.
55    pub(super) fn error(&mut self, kind: ErrorKind) -> Error {
56        kind.at(self.position)
57    }
58
59    /// Get the input length.
60    pub(super) fn input_len(&self) -> Length {
61        self.input_len
62    }
63
64    /// Get the remaining length.
65    pub(super) fn remaining_len(&self) -> Length {
66        debug_assert!(self.position <= self.input_len());
67        self.input_len.saturating_sub(self.position)
68    }
69
70    /// Split a nested position tracker of the given size.
71    ///
72    /// # Returns
73    ///
74    /// A [`Resumption`] value which can be used to continue parsing the outer message.
75    pub(super) fn split_nested(&mut self, len: Length) -> Result<Resumption> {
76        match self.depth.checked_add(1) {
77            Some(depth) if depth < Self::MAX_DEPTH => self.depth = depth,
78            _ => return Err(self.error(ErrorKind::NestingDepth)),
79        }
80
81        let nested_input_len = (self.position + len)?;
82
83        if nested_input_len > self.input_len {
84            return Err(Error::incomplete(self.input_len));
85        }
86
87        let resumption = Resumption {
88            input_len: self.input_len,
89        };
90        self.input_len = nested_input_len;
91        Ok(resumption)
92    }
93
94    /// Resume processing the rest of a message after processing a nested inner portion.
95    pub(super) fn resume_nested(&mut self, resumption: Resumption) {
96        self.input_len = resumption.input_len;
97        self.depth = self.depth.saturating_sub(1);
98    }
99}
100
101/// Resumption state needed to continue processing a message after handling a nested inner portion.
102#[derive(Debug)]
103pub(super) struct Resumption {
104    /// Outer input length.
105    input_len: Length,
106}
107
108#[cfg(test)]
109#[allow(clippy::unwrap_used)]
110mod tests {
111    use super::Position;
112    use crate::{ErrorKind, Length};
113
114    const EXAMPLE_LEN: Length = match Length::new_usize(42) {
115        Ok(len) => len,
116        Err(_) => panic!("invalid example len"),
117    };
118
119    #[test]
120    fn initial_state() {
121        let pos = Position::new(EXAMPLE_LEN);
122        assert_eq!(pos.input_len(), EXAMPLE_LEN);
123        assert_eq!(pos.current(), Length::ZERO);
124    }
125
126    #[test]
127    fn advance() {
128        let mut pos = Position::new(EXAMPLE_LEN);
129
130        // advance 1 byte: success
131        let new_pos = pos.advance(Length::ONE).unwrap();
132        assert_eq!(new_pos, Length::ONE);
133        assert_eq!(pos.current(), Length::ONE);
134
135        // advance to end: success
136        let end_pos = pos.advance((EXAMPLE_LEN - Length::ONE).unwrap()).unwrap();
137        assert_eq!(end_pos, EXAMPLE_LEN);
138        assert_eq!(pos.current(), EXAMPLE_LEN);
139
140        // advance one byte past end: error
141        let err = pos.advance(Length::ONE).unwrap_err();
142        assert!(matches!(err.kind(), ErrorKind::Incomplete { .. }));
143    }
144
145    #[test]
146    fn nested() {
147        let mut pos = Position::new(EXAMPLE_LEN);
148
149        // split first byte
150        let resumption = pos.split_nested(Length::ONE).unwrap();
151        assert_eq!(pos.current(), Length::ZERO);
152        assert_eq!(pos.input_len(), Length::ONE);
153
154        // advance one byte
155        assert_eq!(pos.advance(Length::ONE).unwrap(), Length::ONE);
156
157        // can't advance two bytes
158        let err = pos.advance(Length::ONE).unwrap_err();
159        assert!(matches!(err.kind(), ErrorKind::Incomplete { .. }));
160
161        // resume processing the rest of the message
162        // TODO(tarcieri): should we fail here if we previously failed reading a nested message?
163        pos.resume_nested(resumption);
164
165        assert_eq!(pos.current(), Length::ONE);
166        assert_eq!(pos.input_len(), EXAMPLE_LEN);
167
168        // try to split one byte past end: error
169        let err = pos.split_nested(EXAMPLE_LEN).unwrap_err();
170        assert!(matches!(err.kind(), ErrorKind::Incomplete { .. }));
171    }
172}