Skip to main content

bcp_decoder/
block_reader.rs

1use bcp_types::fields::{
2    FieldWireType, decode_bytes_value, decode_field_header, decode_varint_value,
3};
4
5use crate::error::DecodeError;
6
7/// A raw TLV field before type-specific interpretation.
8///
9/// Produced by [`BlockReader::next_field`]. The caller matches on
10/// `field_id` to decide which struct field to populate, and uses
11/// `wire_type` to interpret `data`:
12///
13///   - `Varint`: `data` contains the raw varint bytes (use
14///     [`decode_varint_value`] to extract the `u64`).
15///   - `Bytes` / `Nested`: `data` is the length-prefixed payload
16///     (the length prefix has already been consumed).
17///
18/// Unknown field IDs should be silently skipped for forward
19/// compatibility.
20pub struct RawField<'a> {
21    pub field_id: u64,
22    pub wire_type: FieldWireType,
23    pub data: &'a [u8],
24}
25
26/// Cursor-based TLV field reader for block bodies.
27///
28/// `BlockReader` wraps a byte slice and provides an iterator-like
29/// interface for consuming TLV fields one at a time. It delegates
30/// to the decode functions in `bcp_types::fields` but adds a
31/// stateful cursor so the caller doesn't have to manually track
32/// offsets.
33///
34/// This is an internal implementation detail of the decoder — it is
35/// not part of the public API.
36///
37/// # Usage pattern
38///
39/// ```text
40///   let mut reader = BlockReader::new(body);
41///   while let Some(field) = reader.next_field()? {
42///       match field.field_id {
43///           1 => { /* handle field 1 */ }
44///           2 => { /* handle field 2 */ }
45///           _ => { /* skip unknown */ }
46///       }
47///   }
48/// ```
49pub struct BlockReader<'a> {
50    buf: &'a [u8],
51    pos: usize,
52}
53
54impl<'a> BlockReader<'a> {
55    /// Create a new reader over the given body bytes.
56    ///
57    /// The reader starts at position 0 and advances through the buffer
58    /// as fields are consumed via [`next_field`](Self::next_field).
59    #[must_use]
60    pub fn new(buf: &'a [u8]) -> Self {
61        Self { buf, pos: 0 }
62    }
63
64    /// Read the next TLV field from the body.
65    ///
66    /// Returns `Ok(Some(field))` if a field was successfully read, or
67    /// `Ok(None)` when the buffer is exhausted. Returns `Err` if the
68    /// field header or payload is malformed.
69    ///
70    /// For `Varint` fields, `data` points to the varint bytes in the
71    /// original buffer (the caller should use [`decode_varint_value`]
72    /// to extract the value). For `Bytes` and `Nested` fields, `data`
73    /// is the raw payload after the length prefix.
74    ///
75    /// # Errors
76    ///
77    /// Returns [`DecodeError::Type`] or [`DecodeError::Wire`] if the
78    /// field header is malformed or the payload is truncated.
79    pub fn next_field(&mut self) -> Result<Option<RawField<'a>>, DecodeError> {
80        let remaining = &self.buf[self.pos..];
81        if remaining.is_empty() {
82            return Ok(None);
83        }
84
85        let (header, header_len) = decode_field_header(remaining)?;
86        let payload_buf = &remaining[header_len..];
87
88        let (data, payload_consumed) = match header.wire_type {
89            FieldWireType::Varint => {
90                // For varints, we return the raw varint bytes so the caller
91                // can decode the value. We need to know how many bytes it
92                // consumed to advance the cursor.
93                let (_, n) = decode_varint_value(payload_buf)?;
94                (&payload_buf[..n], n)
95            }
96            FieldWireType::Bytes | FieldWireType::Nested => {
97                // For bytes/nested, decode_bytes_value returns the inner
98                // slice (after length prefix) and total bytes consumed.
99                let (inner, n) = decode_bytes_value(payload_buf)?;
100                (inner, n)
101            }
102        };
103
104        self.pos += header_len + payload_consumed;
105
106        Ok(Some(RawField {
107            field_id: header.field_id,
108            wire_type: header.wire_type,
109            data,
110        }))
111    }
112
113    /// Return the number of bytes consumed so far.
114    #[must_use]
115    pub fn position(&self) -> usize {
116        self.pos
117    }
118
119    /// Return the remaining unread bytes.
120    #[must_use]
121    pub fn remaining(&self) -> &'a [u8] {
122        &self.buf[self.pos..]
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use bcp_types::fields::{encode_bytes_field, encode_nested_field, encode_varint_field};
130
131    #[test]
132    fn empty_buffer_returns_none() {
133        let mut reader = BlockReader::new(&[]);
134        assert!(reader.next_field().unwrap().is_none());
135    }
136
137    #[test]
138    fn reads_varint_field() {
139        let mut buf = Vec::new();
140        encode_varint_field(&mut buf, 1, 42);
141
142        let mut reader = BlockReader::new(&buf);
143        let field = reader.next_field().unwrap().unwrap();
144        assert_eq!(field.field_id, 1);
145        assert_eq!(field.wire_type, FieldWireType::Varint);
146
147        // Decode the varint value from the raw bytes
148        let (value, _) = decode_varint_value(field.data).unwrap();
149        assert_eq!(value, 42);
150
151        assert!(reader.next_field().unwrap().is_none());
152    }
153
154    #[test]
155    fn reads_bytes_field() {
156        let mut buf = Vec::new();
157        encode_bytes_field(&mut buf, 2, b"hello");
158
159        let mut reader = BlockReader::new(&buf);
160        let field = reader.next_field().unwrap().unwrap();
161        assert_eq!(field.field_id, 2);
162        assert_eq!(field.wire_type, FieldWireType::Bytes);
163        assert_eq!(field.data, b"hello");
164
165        assert!(reader.next_field().unwrap().is_none());
166    }
167
168    #[test]
169    fn reads_nested_field() {
170        let mut inner = Vec::new();
171        encode_varint_field(&mut inner, 1, 99);
172
173        let mut buf = Vec::new();
174        encode_nested_field(&mut buf, 3, &inner);
175
176        let mut reader = BlockReader::new(&buf);
177        let field = reader.next_field().unwrap().unwrap();
178        assert_eq!(field.field_id, 3);
179        assert_eq!(field.wire_type, FieldWireType::Nested);
180        assert_eq!(field.data, &inner);
181    }
182
183    #[test]
184    fn reads_multiple_fields_sequentially() {
185        let mut buf = Vec::new();
186        encode_varint_field(&mut buf, 1, 7);
187        encode_bytes_field(&mut buf, 2, b"world");
188        encode_varint_field(&mut buf, 3, 256);
189
190        let mut reader = BlockReader::new(&buf);
191
192        let f1 = reader.next_field().unwrap().unwrap();
193        assert_eq!(f1.field_id, 1);
194
195        let f2 = reader.next_field().unwrap().unwrap();
196        assert_eq!(f2.field_id, 2);
197        assert_eq!(f2.data, b"world");
198
199        let f3 = reader.next_field().unwrap().unwrap();
200        assert_eq!(f3.field_id, 3);
201
202        assert!(reader.next_field().unwrap().is_none());
203        assert_eq!(reader.position(), buf.len());
204    }
205
206    #[test]
207    fn position_tracks_correctly() {
208        let mut buf = Vec::new();
209        encode_varint_field(&mut buf, 1, 42);
210        encode_bytes_field(&mut buf, 2, b"hi");
211
212        let mut reader = BlockReader::new(&buf);
213        assert_eq!(reader.position(), 0);
214
215        reader.next_field().unwrap();
216        let mid = reader.position();
217        assert!(mid > 0 && mid < buf.len());
218
219        reader.next_field().unwrap();
220        assert_eq!(reader.position(), buf.len());
221        assert!(reader.remaining().is_empty());
222    }
223}