Skip to main content

der/reader/
position.rs

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