Skip to main content

nautilus_serialization/sbe/
cursor.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Zero-copy SBE byte cursor for sequential decoding.
17
18use std::str;
19
20use super::error::{MAX_GROUP_SIZE, SbeDecodeError};
21
22/// Zero-copy SBE byte cursor for sequential decoding.
23///
24/// Wraps a byte slice and tracks position, providing typed read methods
25/// that automatically advance the cursor.
26#[derive(Debug, Clone)]
27pub struct SbeCursor<'a> {
28    buf: &'a [u8],
29    pos: usize,
30}
31
32impl<'a> SbeCursor<'a> {
33    /// Creates a new cursor at position 0.
34    #[must_use]
35    pub const fn new(buf: &'a [u8]) -> Self {
36        Self { buf, pos: 0 }
37    }
38
39    /// Creates a cursor starting at a specific offset.
40    #[must_use]
41    pub const fn new_at(buf: &'a [u8], pos: usize) -> Self {
42        Self { buf, pos }
43    }
44
45    /// Current position in the buffer.
46    #[must_use]
47    pub const fn pos(&self) -> usize {
48        self.pos
49    }
50
51    /// Remaining bytes from current position.
52    #[must_use]
53    pub const fn remaining(&self) -> usize {
54        self.buf.len().saturating_sub(self.pos)
55    }
56
57    /// Returns the underlying buffer.
58    #[must_use]
59    pub const fn buffer(&self) -> &'a [u8] {
60        self.buf
61    }
62
63    /// Returns remaining bytes as a slice.
64    #[must_use]
65    pub fn peek(&self) -> &'a [u8] {
66        &self.buf[self.pos..]
67    }
68
69    /// Ensures at least `n` bytes remain.
70    ///
71    /// # Errors
72    ///
73    /// Returns an error if fewer than `n` bytes remain.
74    #[inline]
75    pub fn require(&self, n: usize) -> Result<(), SbeDecodeError> {
76        if self.remaining() < n {
77            return Err(SbeDecodeError::BufferTooShort {
78                expected: self.pos + n,
79                actual: self.buf.len(),
80            });
81        }
82        Ok(())
83    }
84
85    /// Advances position by `n` bytes.
86    ///
87    /// # Errors
88    ///
89    /// Returns an error if fewer than `n` bytes remain.
90    #[inline]
91    pub fn advance(&mut self, n: usize) -> Result<(), SbeDecodeError> {
92        self.require(n)?;
93        self.pos += n;
94        Ok(())
95    }
96
97    /// Skips `n` bytes without bounds checking.
98    ///
99    /// Caller must ensure `n` bytes are available.
100    #[inline]
101    pub fn skip(&mut self, n: usize) {
102        self.pos += n;
103    }
104
105    /// Resets cursor to start of buffer.
106    pub fn reset(&mut self) {
107        self.pos = 0;
108    }
109
110    /// Sets cursor to a specific position.
111    pub fn set_pos(&mut self, pos: usize) {
112        self.pos = pos;
113    }
114
115    /// Reads a u8 and advances by 1 byte.
116    ///
117    /// # Errors
118    ///
119    /// Returns an error if fewer than 1 byte remains.
120    #[inline]
121    pub fn read_u8(&mut self) -> Result<u8, SbeDecodeError> {
122        self.require(1)?;
123        let value = self.buf[self.pos];
124        self.pos += 1;
125        Ok(value)
126    }
127
128    /// Reads an i8 and advances by 1 byte.
129    ///
130    /// # Errors
131    ///
132    /// Returns an error if fewer than 1 byte remains.
133    #[inline]
134    pub fn read_i8(&mut self) -> Result<i8, SbeDecodeError> {
135        self.require(1)?;
136        let value = self.buf[self.pos] as i8;
137        self.pos += 1;
138        Ok(value)
139    }
140
141    /// Reads a u16 little-endian and advances by 2 bytes.
142    ///
143    /// # Errors
144    ///
145    /// Returns an error if fewer than 2 bytes remain.
146    #[inline]
147    pub fn read_u16_le(&mut self) -> Result<u16, SbeDecodeError> {
148        Ok(u16::from_le_bytes(self.read_array::<2>()?))
149    }
150
151    /// Reads an i16 little-endian and advances by 2 bytes.
152    ///
153    /// # Errors
154    ///
155    /// Returns an error if fewer than 2 bytes remain.
156    #[inline]
157    pub fn read_i16_le(&mut self) -> Result<i16, SbeDecodeError> {
158        Ok(i16::from_le_bytes(self.read_array::<2>()?))
159    }
160
161    /// Reads a u32 little-endian and advances by 4 bytes.
162    ///
163    /// # Errors
164    ///
165    /// Returns an error if fewer than 4 bytes remain.
166    #[inline]
167    pub fn read_u32_le(&mut self) -> Result<u32, SbeDecodeError> {
168        Ok(u32::from_le_bytes(self.read_array::<4>()?))
169    }
170
171    /// Reads an i32 little-endian and advances by 4 bytes.
172    ///
173    /// # Errors
174    ///
175    /// Returns an error if fewer than 4 bytes remain.
176    #[inline]
177    pub fn read_i32_le(&mut self) -> Result<i32, SbeDecodeError> {
178        Ok(i32::from_le_bytes(self.read_array::<4>()?))
179    }
180
181    /// Reads a u64 little-endian and advances by 8 bytes.
182    ///
183    /// # Errors
184    ///
185    /// Returns an error if fewer than 8 bytes remain.
186    #[inline]
187    pub fn read_u64_le(&mut self) -> Result<u64, SbeDecodeError> {
188        Ok(u64::from_le_bytes(self.read_array::<8>()?))
189    }
190
191    /// Reads an i64 little-endian and advances by 8 bytes.
192    ///
193    /// # Errors
194    ///
195    /// Returns an error if fewer than 8 bytes remain.
196    #[inline]
197    pub fn read_i64_le(&mut self) -> Result<i64, SbeDecodeError> {
198        Ok(i64::from_le_bytes(self.read_array::<8>()?))
199    }
200
201    /// Reads a u128 little-endian and advances by 16 bytes.
202    ///
203    /// # Errors
204    ///
205    /// Returns an error if fewer than 16 bytes remain.
206    #[inline]
207    pub fn read_u128_le(&mut self) -> Result<u128, SbeDecodeError> {
208        Ok(u128::from_le_bytes(self.read_array::<16>()?))
209    }
210
211    /// Reads an i128 little-endian and advances by 16 bytes.
212    ///
213    /// # Errors
214    ///
215    /// Returns an error if fewer than 16 bytes remain.
216    #[inline]
217    pub fn read_i128_le(&mut self) -> Result<i128, SbeDecodeError> {
218        Ok(i128::from_le_bytes(self.read_array::<16>()?))
219    }
220
221    /// Reads an optional i64 where `i64::MIN` represents None.
222    ///
223    /// # Errors
224    ///
225    /// Returns an error if fewer than 8 bytes remain.
226    #[inline]
227    pub fn read_optional_i64_le(&mut self) -> Result<Option<i64>, SbeDecodeError> {
228        let value = self.read_i64_le()?;
229        Ok(if value == i64::MIN { None } else { Some(value) })
230    }
231
232    /// Reads N bytes and advances.
233    ///
234    /// # Errors
235    ///
236    /// Returns an error if fewer than `n` bytes remain.
237    #[expect(
238        clippy::inline_always,
239        reason = "preserves checked slice lengths for fixed-width SBE decoding"
240    )]
241    #[inline(always)]
242    pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8], SbeDecodeError> {
243        self.require(n)?;
244        let slice = &self.buf[self.pos..self.pos + n];
245        self.pos += n;
246        Ok(slice)
247    }
248
249    // Const-generic slice-to-array conversion lets LLVM lower the read to
250    // a single aligned load after one bounds check, matching the pattern
251    // the compiler recognizes for `from_le_bytes`.
252    #[inline]
253    fn read_array<const N: usize>(&mut self) -> Result<[u8; N], SbeDecodeError> {
254        self.require(N)?;
255        let bytes: [u8; N] = self.buf[self.pos..self.pos + N]
256            .try_into()
257            .expect("slice length matches N");
258        self.pos += N;
259        Ok(bytes)
260    }
261
262    /// Reads a varString8 (1-byte length prefix + UTF-8 data).
263    ///
264    /// Returns empty string if length is 0.
265    ///
266    /// # Errors
267    ///
268    /// Returns an error if the buffer is too short, or if the data is not valid UTF-8.
269    #[inline]
270    pub fn read_var_string8(&mut self) -> Result<String, SbeDecodeError> {
271        Ok(self.read_var_string8_ref()?.to_owned())
272    }
273
274    /// Reads a varString8 as a &str (zero-copy).
275    ///
276    /// # Errors
277    ///
278    /// Returns an error if the buffer is too short, or if the data is not valid UTF-8.
279    #[inline]
280    pub fn read_var_string8_ref(&mut self) -> Result<&'a str, SbeDecodeError> {
281        let len = self.read_u8()? as usize;
282        if len == 0 {
283            return Ok("");
284        }
285        self.require(len)?;
286        let s = str::from_utf8(&self.buf[self.pos..self.pos + len])
287            .map_err(|_| SbeDecodeError::InvalidUtf8)?;
288        self.pos += len;
289        Ok(s)
290    }
291
292    /// Reads a varString16 (2-byte length prefix + UTF-8 data).
293    ///
294    /// Returns empty string if length is 0.
295    ///
296    /// # Errors
297    ///
298    /// Returns an error if the buffer is too short, or if the data is not valid UTF-8.
299    #[inline]
300    pub fn read_var_string16(&mut self) -> Result<String, SbeDecodeError> {
301        Ok(self.read_var_string16_ref()?.to_owned())
302    }
303
304    /// Reads a varString16 as a `&str` (zero-copy).
305    ///
306    /// # Errors
307    ///
308    /// Returns an error if the buffer is too short, or if the data is not valid UTF-8.
309    #[inline]
310    pub fn read_var_string16_ref(&mut self) -> Result<&'a str, SbeDecodeError> {
311        let len = usize::from(self.read_u16_le()?);
312        if len == 0 {
313            return Ok("");
314        }
315        self.require(len)?;
316        let s = str::from_utf8(&self.buf[self.pos..self.pos + len])
317            .map_err(|_| SbeDecodeError::InvalidUtf8)?;
318        self.pos += len;
319        Ok(s)
320    }
321
322    /// Skips a varData8 field (1-byte length prefix + binary data).
323    ///
324    /// Used for skipping binary fields that should not be decoded as UTF-8.
325    ///
326    /// # Errors
327    ///
328    /// Returns an error if the buffer is too short.
329    pub fn skip_var_data8(&mut self) -> Result<(), SbeDecodeError> {
330        let len = self.read_u8()? as usize;
331        self.advance(len)
332    }
333
334    /// Reads a varData8 field (1-byte length prefix + binary data).
335    ///
336    /// Returns the raw bytes without UTF-8 decoding.
337    ///
338    /// # Errors
339    ///
340    /// Returns an error if the buffer is too short.
341    pub fn read_var_bytes8(&mut self) -> Result<Vec<u8>, SbeDecodeError> {
342        let len = self.read_u8()? as usize;
343        Ok(self.read_bytes(len)?.to_vec())
344    }
345
346    /// Skips a varData16 field (2-byte length prefix + binary data).
347    ///
348    /// # Errors
349    ///
350    /// Returns an error if the buffer is too short.
351    pub fn skip_var_data16(&mut self) -> Result<(), SbeDecodeError> {
352        let len = usize::from(self.read_u16_le()?);
353        self.advance(len)
354    }
355
356    /// Reads a varData16 field (2-byte length prefix + binary data).
357    ///
358    /// Returns the raw bytes without UTF-8 decoding.
359    ///
360    /// # Errors
361    ///
362    /// Returns an error if the buffer is too short.
363    pub fn read_var_bytes16(&mut self) -> Result<Vec<u8>, SbeDecodeError> {
364        let len = usize::from(self.read_u16_le()?);
365        Ok(self.read_bytes(len)?.to_vec())
366    }
367
368    /// Reads group header (u16 block_length + u32 num_in_group).
369    ///
370    /// Returns (block_length, num_in_group).
371    ///
372    /// # Errors
373    ///
374    /// Returns an error if fewer than 6 bytes are available or `num_in_group` exceeds
375    /// `MAX_GROUP_SIZE`.
376    #[inline]
377    pub fn read_group_header(&mut self) -> Result<(u16, u32), SbeDecodeError> {
378        let block_length = self.read_u16_le()?;
379        let num_in_group = self.read_u32_le()?;
380
381        if num_in_group > MAX_GROUP_SIZE {
382            return Err(SbeDecodeError::GroupSizeTooLarge {
383                count: num_in_group,
384                max: MAX_GROUP_SIZE,
385            });
386        }
387
388        Ok((block_length, num_in_group))
389    }
390
391    /// Reads compact group header (u16 block_length + u16 num_in_group).
392    ///
393    /// Returns (block_length, num_in_group).
394    ///
395    /// # Errors
396    ///
397    /// Returns an error if fewer than 4 bytes are available or `num_in_group` exceeds
398    /// `MAX_GROUP_SIZE`.
399    #[inline]
400    pub fn read_group_header_16(&mut self) -> Result<(u16, u16), SbeDecodeError> {
401        let block_length = self.read_u16_le()?;
402        let num_in_group = self.read_u16_le()?;
403
404        if u32::from(num_in_group) > MAX_GROUP_SIZE {
405            return Err(SbeDecodeError::GroupSizeTooLarge {
406                count: u32::from(num_in_group),
407                max: MAX_GROUP_SIZE,
408            });
409        }
410
411        Ok((block_length, num_in_group))
412    }
413
414    /// Iterates over a group, calling `decode_item` for each element.
415    ///
416    /// The decoder receives a cursor at the item start with absolute offsets, and a buffer
417    /// that ends `block_length` bytes after it. Unread trailing bytes are skipped before the
418    /// next item. If `decode_item` fails, this cursor is left at the decoder's position.
419    ///
420    /// # Errors
421    ///
422    /// Returns an error if:
423    /// - The buffer does not contain all group entries.
424    /// - `decode_item` fails.
425    pub fn read_group<T, F>(
426        &mut self,
427        block_length: u16,
428        num_in_group: u32,
429        mut decode_item: F,
430    ) -> Result<Vec<T>, SbeDecodeError>
431    where
432        F: FnMut(&mut Self) -> Result<T, SbeDecodeError>,
433    {
434        let block_len = block_length as usize;
435        let count = num_in_group as usize;
436
437        // Validate we have enough bytes for all items.
438        self.require(count * block_len)?;
439
440        let mut items = Vec::with_capacity(count);
441        for _ in 0..count {
442            let item_start = self.pos;
443            let item_end = item_start + block_len;
444
445            let buf = self
446                .buf
447                .get(..item_end)
448                .ok_or(SbeDecodeError::BufferTooShort {
449                    expected: item_end,
450                    actual: self.buf.len(),
451                })?;
452
453            let mut entry = Self::new_at(buf, item_start);
454            let item = decode_item(&mut entry);
455            self.pos = entry.pos;
456            items.push(item?);
457            self.pos = item_end;
458        }
459
460        Ok(items)
461    }
462}
463
464#[cfg(test)]
465mod tests {
466    use rstest::rstest;
467
468    use super::*;
469
470    #[rstest]
471    fn test_new_starts_at_zero() {
472        let buf = [1, 2, 3, 4];
473        let cursor = SbeCursor::new(&buf);
474        assert_eq!(cursor.pos(), 0);
475        assert_eq!(cursor.remaining(), 4);
476    }
477
478    #[rstest]
479    fn test_new_at_starts_at_offset() {
480        let buf = [1, 2, 3, 4];
481        let cursor = SbeCursor::new_at(&buf, 2);
482        assert_eq!(cursor.pos(), 2);
483        assert_eq!(cursor.remaining(), 2);
484    }
485
486    #[rstest]
487    fn test_read_u8() {
488        let buf = [0x42, 0xFF];
489        let mut cursor = SbeCursor::new(&buf);
490
491        assert_eq!(cursor.read_u8().unwrap(), 0x42);
492        assert_eq!(cursor.pos(), 1);
493
494        assert_eq!(cursor.read_u8().unwrap(), 0xFF);
495        assert_eq!(cursor.pos(), 2);
496
497        assert!(cursor.read_u8().is_err());
498    }
499
500    #[rstest]
501    fn test_read_i8() {
502        let buf = [0x7F, 0x80]; // 127, -128
503        let mut cursor = SbeCursor::new(&buf);
504
505        assert_eq!(cursor.read_i8().unwrap(), 127);
506        assert_eq!(cursor.read_i8().unwrap(), -128);
507    }
508
509    #[rstest]
510    fn test_read_u16_le() {
511        let buf = [0x34, 0x12]; // 0x1234 in little-endian
512        let mut cursor = SbeCursor::new(&buf);
513
514        assert_eq!(cursor.read_u16_le().unwrap(), 0x1234);
515        assert_eq!(cursor.pos(), 2);
516    }
517
518    #[rstest]
519    fn test_read_i64_le() {
520        let value: i64 = -1_234_567_890_123_456_789;
521        let buf = value.to_le_bytes();
522        let mut cursor = SbeCursor::new(&buf);
523
524        assert_eq!(cursor.read_i64_le().unwrap(), value);
525        assert_eq!(cursor.pos(), 8);
526    }
527
528    #[rstest]
529    #[case::u16(&[0x34][..], 2)]
530    #[case::u32(&[0x34, 0x12, 0x00][..], 4)]
531    #[case::u64(&[0; 7][..], 8)]
532    #[case::u128(&[0; 15][..], 16)]
533    fn test_multi_byte_reads_buffer_too_short(#[case] buf: &[u8], #[case] needed: usize) {
534        let mut cursor = SbeCursor::new(buf);
535        let err = match needed {
536            2 => cursor.read_u16_le().map(|_| ()).unwrap_err(),
537            4 => cursor.read_u32_le().map(|_| ()).unwrap_err(),
538            8 => cursor.read_u64_le().map(|_| ()).unwrap_err(),
539            16 => cursor.read_u128_le().map(|_| ()).unwrap_err(),
540            _ => unreachable!(),
541        };
542
543        assert_eq!(
544            err,
545            SbeDecodeError::BufferTooShort {
546                expected: needed,
547                actual: buf.len()
548            }
549        );
550        assert_eq!(cursor.pos(), 0, "position must not advance on error");
551    }
552
553    #[rstest]
554    fn test_read_optional_i64_null() {
555        let buf = i64::MIN.to_le_bytes();
556        let mut cursor = SbeCursor::new(&buf);
557
558        assert_eq!(cursor.read_optional_i64_le().unwrap(), None);
559    }
560
561    #[rstest]
562    fn test_read_optional_i64_present() {
563        let value: i64 = 12345;
564        let buf = value.to_le_bytes();
565        let mut cursor = SbeCursor::new(&buf);
566
567        assert_eq!(cursor.read_optional_i64_le().unwrap(), Some(12345));
568    }
569
570    #[rstest]
571    fn test_read_var_string8() {
572        let mut buf = vec![5]; // length = 5
573        buf.extend_from_slice(b"hello");
574        let mut cursor = SbeCursor::new(&buf);
575
576        assert_eq!(cursor.read_var_string8().unwrap(), "hello");
577        assert_eq!(cursor.pos(), 6); // 1 + 5
578    }
579
580    #[rstest]
581    fn test_read_var_string8_empty() {
582        let buf = [0]; // length = 0
583        let mut cursor = SbeCursor::new(&buf);
584
585        assert_eq!(cursor.read_var_string8().unwrap(), "");
586        assert_eq!(cursor.pos(), 1);
587    }
588
589    #[rstest]
590    fn test_read_var_string8_invalid_utf8() {
591        let buf = [2, 0xFF, 0xFE]; // length = 2, invalid UTF-8
592        let mut cursor = SbeCursor::new(&buf);
593
594        assert!(matches!(
595            cursor.read_var_string8(),
596            Err(SbeDecodeError::InvalidUtf8)
597        ));
598    }
599
600    #[rstest]
601    fn test_read_group_header() {
602        // block_length = 24, num_in_group = 3
603        let buf = [24, 0, 3, 0, 0, 0];
604        let mut cursor = SbeCursor::new(&buf);
605
606        let (block_len, count) = cursor.read_group_header().unwrap();
607        assert_eq!(block_len, 24);
608        assert_eq!(count, 3);
609        assert_eq!(cursor.pos(), 6);
610    }
611
612    #[rstest]
613    fn test_read_group_header_too_large() {
614        // num_in_group = MAX_GROUP_SIZE + 1
615        let count = MAX_GROUP_SIZE + 1;
616        let mut buf = vec![24, 0]; // block_length = 24
617        buf.extend_from_slice(&count.to_le_bytes());
618        let mut cursor = SbeCursor::new(&buf);
619
620        assert!(matches!(
621            cursor.read_group_header(),
622            Err(SbeDecodeError::GroupSizeTooLarge { .. })
623        ));
624    }
625
626    #[rstest]
627    fn test_read_group() {
628        // 2 items, each 4 bytes containing a u32
629        let mut buf = Vec::new();
630        buf.extend_from_slice(&100u32.to_le_bytes()); // item 0
631        buf.extend_from_slice(&200u32.to_le_bytes()); // item 1
632
633        let mut cursor = SbeCursor::new(&buf);
634        let items: Vec<u32> = cursor
635            .read_group(4, 2, super::SbeCursor::read_u32_le)
636            .unwrap();
637
638        assert_eq!(items, vec![100, 200]);
639        assert_eq!(cursor.pos(), 8);
640    }
641
642    #[rstest]
643    fn test_read_group_respects_block_length() {
644        // 2 items, block_length = 8, but we only read 4 bytes per item
645        let mut buf = Vec::new();
646        buf.extend_from_slice(&100u32.to_le_bytes());
647        buf.extend_from_slice(&[0, 0, 0, 0]); // padding
648        buf.extend_from_slice(&200u32.to_le_bytes());
649        buf.extend_from_slice(&[0, 0, 0, 0]); // padding
650
651        let mut cursor = SbeCursor::new(&buf);
652        let items: Vec<u32> = cursor
653            .read_group(8, 2, super::SbeCursor::read_u32_le)
654            .unwrap();
655
656        assert_eq!(items, vec![100, 200]);
657        assert_eq!(cursor.pos(), 16); // 2 * 8
658    }
659
660    #[rstest]
661    #[case(0)]
662    #[case(3)]
663    fn test_read_group_rejects_entry_overread(#[case] block_length: u16) {
664        let buf = [9, 8, 7, 6, 5, 4, 3, 2, 1];
665        let mut cursor = SbeCursor::new_at(&buf, 1);
666        let mut calls = 0;
667        let item_end = 1 + usize::from(block_length);
668
669        let result = cursor.read_group(block_length, 2, |entry| {
670            calls += 1;
671            assert_eq!(entry.pos(), 1);
672            assert_eq!(entry.buffer(), &buf[..item_end]);
673            assert_eq!(entry.remaining(), usize::from(block_length));
674            assert_eq!(entry.peek(), &buf[1..item_end]);
675            entry.read_u32_le()
676        });
677
678        assert_eq!(
679            result,
680            Err(SbeDecodeError::BufferTooShort {
681                expected: 5,
682                actual: item_end,
683            })
684        );
685        assert_eq!(cursor.pos(), 1);
686        assert_eq!(calls, 1);
687    }
688
689    #[rstest]
690    fn test_read_group_rejects_position_past_buffer() {
691        let mut cursor = SbeCursor::new_at(&[1, 2], 3);
692        let result = cursor.read_group(0, 1, SbeCursor::read_u8);
693
694        assert_eq!(
695            result,
696            Err(SbeDecodeError::BufferTooShort {
697                expected: 3,
698                actual: 2
699            })
700        );
701        assert_eq!(cursor.pos(), 3);
702    }
703
704    #[rstest]
705    fn test_read_group_preserves_position_after_partial_overread() {
706        let mut cursor = SbeCursor::new_at(&[9, 8, 7, 6, 5, 4, 3, 2], 1);
707
708        let result = cursor.read_group(3, 2, |entry| {
709            entry.read_u8()?;
710            entry.read_u32_le()
711        });
712
713        assert_eq!(
714            result,
715            Err(SbeDecodeError::BufferTooShort {
716                expected: 6,
717                actual: 4
718            })
719        );
720        assert_eq!(cursor.pos(), 2);
721    }
722
723    #[rstest]
724    fn test_require_success() {
725        let buf = [1, 2, 3, 4];
726        let cursor = SbeCursor::new(&buf);
727
728        assert!(cursor.require(4).is_ok());
729        assert!(cursor.require(3).is_ok());
730    }
731
732    #[rstest]
733    fn test_require_failure() {
734        let buf = [1, 2];
735        let cursor = SbeCursor::new(&buf);
736
737        let err = cursor.require(3).unwrap_err();
738        assert_eq!(
739            err,
740            SbeDecodeError::BufferTooShort {
741                expected: 3,
742                actual: 2
743            }
744        );
745    }
746
747    #[rstest]
748    fn test_advance() {
749        let buf = [1, 2, 3, 4];
750        let mut cursor = SbeCursor::new(&buf);
751
752        cursor.advance(2).unwrap();
753        assert_eq!(cursor.pos(), 2);
754        assert_eq!(cursor.remaining(), 2);
755
756        assert!(cursor.advance(3).is_err());
757    }
758
759    #[rstest]
760    fn test_peek() {
761        let buf = [1, 2, 3, 4];
762        let mut cursor = SbeCursor::new(&buf);
763
764        assert_eq!(cursor.peek(), &[1, 2, 3, 4]);
765        cursor.advance(2).unwrap();
766        assert_eq!(cursor.peek(), &[3, 4]);
767    }
768
769    #[rstest]
770    fn test_reset() {
771        let buf = [1, 2, 3, 4];
772        let mut cursor = SbeCursor::new(&buf);
773
774        cursor.advance(3).unwrap();
775        assert_eq!(cursor.pos(), 3);
776
777        cursor.reset();
778        assert_eq!(cursor.pos(), 0);
779        assert_eq!(cursor.remaining(), 4);
780    }
781
782    #[rstest]
783    #[case::short(false)]
784    #[case::wide(true)]
785    fn test_var_bytes_preserve_binary_and_position(#[case] wide: bool) {
786        let mut buf = if wide { vec![3, 0] } else { vec![3] };
787        buf.extend_from_slice(&[0xff, 0, 0x80, 0x42]);
788        let mut read = SbeCursor::new(&buf);
789        let mut skip = read.clone();
790
791        let bytes = if wide {
792            skip.skip_var_data16().unwrap();
793            read.read_var_bytes16().unwrap()
794        } else {
795            skip.skip_var_data8().unwrap();
796            read.read_var_bytes8().unwrap()
797        };
798
799        assert_eq!(bytes, [0xff, 0, 0x80]);
800        assert_eq!(read.pos(), buf.len() - 1);
801        assert_eq!(skip.pos(), read.pos());
802        assert_eq!(read.peek(), [0x42]);
803        assert_eq!(skip.peek(), [0x42]);
804    }
805
806    #[rstest]
807    #[case::short(false)]
808    #[case::wide(true)]
809    fn test_var_bytes_reject_truncated_payload(#[case] wide: bool) {
810        let buf = if wide {
811            vec![3, 0, 0xff]
812        } else {
813            vec![3, 0xff]
814        };
815
816        let prefix = if wide { 2 } else { 1 };
817        let mut read = SbeCursor::new(&buf);
818        let mut skip = read.clone();
819
820        let (read_result, skip_result) = if wide {
821            (read.read_var_bytes16(), skip.skip_var_data16())
822        } else {
823            (read.read_var_bytes8(), skip.skip_var_data8())
824        };
825
826        let expected = SbeDecodeError::BufferTooShort {
827            expected: prefix + 3,
828            actual: buf.len(),
829        };
830
831        assert_eq!(read_result, Err(expected.clone()));
832        assert_eq!(skip_result, Err(expected));
833        assert_eq!(read.pos(), prefix);
834        assert_eq!(skip.pos(), prefix);
835    }
836
837    #[rstest]
838    fn test_read_group_propagates_item_error() {
839        let mut cursor = SbeCursor::new(&[1, 2, 3, 4]);
840
841        let result = cursor.read_group(2, 2, |entry| {
842            let value = entry.read_u8()?;
843            if value == 3 {
844                Err(SbeDecodeError::InvalidValue { field: "entry" })
845            } else {
846                Ok(value)
847            }
848        });
849
850        assert_eq!(result, Err(SbeDecodeError::InvalidValue { field: "entry" }));
851        assert_eq!(cursor.pos(), 3);
852    }
853}