• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kaidokert / picojson-rs / 16298716526

15 Jul 2025 04:19PM UTC coverage: 93.981% (+0.1%) from 93.865%
16298716526

Pull #59

github

web-flow
Merge 88d0fd657 into 1dbca311e
Pull Request #59: Big refactor

461 of 501 new or added lines in 8 files covered. (92.02%)

8 existing lines in 3 files now uncovered.

4731 of 5034 relevant lines covered (93.98%)

734.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.87
/picojson/src/slice_input_buffer.rs
1
// SPDX-License-Identifier: Apache-2.0
2

3
use crate::ParseError;
4

5
/// Error type for SliceInputBuffer operations.
6
#[derive(Debug, PartialEq)]
7
pub enum Error {
8
    /// Reached the end of input data.
9
    ReachedEnd,
10
    /// Invalid slice bounds provided.
11
    InvalidSliceBounds,
12
}
13

14
/// A buffer that manages input data and current parsing position.
15
/// This encapsulates the data slice and position that are always used together.
16
#[derive(Debug)]
17
pub struct SliceInputBuffer<'a> {
18
    data: &'a [u8],
19
    pos: usize,
20
}
21

22
pub trait InputBuffer {
23
    fn is_past_end(&self) -> bool;
24
    fn consume_byte(&mut self) -> Result<u8, Error>;
25
}
26

27
impl InputBuffer for SliceInputBuffer<'_> {
28
    fn is_past_end(&self) -> bool {
323✔
29
        self.pos > self.data.len()
323✔
30
    }
323✔
31
    fn consume_byte(&mut self) -> Result<u8, Error> {
2,200✔
32
        match self.data.get(self.pos) {
2,200✔
33
            Some(&byte) => {
2,168✔
34
                self.pos = self.pos.checked_add(1).ok_or(Error::InvalidSliceBounds)?;
2,168✔
35
                Ok(byte)
2,168✔
36
            }
37
            None => {
38
                self.pos = self.pos.checked_add(1).ok_or(Error::InvalidSliceBounds)?;
32✔
39
                Err(Error::ReachedEnd)
32✔
40
            }
41
        }
42
    }
2,200✔
43
}
44
impl<'a> SliceInputBuffer<'a> {
45
    pub fn current_pos(&self) -> usize {
370✔
46
        self.pos
370✔
47
    }
370✔
48
    /// Creates a new SliceInputBuffer with the given data.
49
    pub fn new(data: &'a [u8]) -> Self {
56✔
50
        Self { data, pos: 0 }
56✔
51
    }
56✔
52

53
    /// Gets a slice of the data from start to end positions, with bounds checking.
54
    pub fn slice(&self, start: usize, end: usize) -> Result<&'a [u8], Error> {
62✔
55
        self.data.get(start..end).ok_or(Error::InvalidSliceBounds)
62✔
56
    }
62✔
57

58
    /// Gets the length of the underlying data for bounds checking.
NEW
59
    pub fn data_len(&self) -> usize {
×
NEW
60
        self.data.len()
×
NEW
61
    }
×
62
}
63

64
impl crate::number_parser::NumberExtractor for SliceInputBuffer<'_> {
65
    fn get_number_slice(&self, start: usize, end: usize) -> Result<&[u8], ParseError> {
27✔
66
        self.slice(start, end)
27✔
67
            .map_err(|_| ParseError::InvalidNumber)
27✔
68
    }
27✔
69

UNCOV
70
    fn current_position(&self) -> usize {
×
71
        // Return the actual current position (AFTER any delimiter)
72
        // Delimiter handling is now centralized in parse_number_event()
UNCOV
73
        self.pos
×
UNCOV
74
    }
×
75

76
    fn is_empty(&self) -> bool {
27✔
77
        self.pos >= self.data.len()
27✔
78
    }
27✔
79
}
80

81
#[cfg(test)]
82
mod tests {
83
    use super::*;
84

85
    #[test]
86
    fn test_buffer_boundary_behavior() {
1✔
87
        let data = b"abc"; // 3 bytes: positions 0, 1, 2 are valid
1✔
88
        let mut buffer = SliceInputBuffer::new(data);
1✔
89

90
        // Position 0: start, should have data
91
        assert_eq!(buffer.current_pos(), 0);
1✔
92
        assert!(!buffer.is_past_end(), "pos=0 should not be past end");
1✔
93
        assert_eq!(buffer.consume_byte(), Ok(b'a'));
1✔
94

95
        // Position 1: middle, should have data
96
        assert_eq!(buffer.current_pos(), 1);
1✔
97
        assert!(!buffer.is_past_end(), "pos=1 should not be past end");
1✔
98
        assert_eq!(buffer.consume_byte(), Ok(b'b'));
1✔
99

100
        // Position 2: last byte, should have data
101
        assert_eq!(buffer.current_pos(), 2);
1✔
102
        assert!(!buffer.is_past_end(), "pos=2 should not be past end");
1✔
103
        assert_eq!(buffer.consume_byte(), Ok(b'c'));
1✔
104

105
        // Position 3: exactly at end (pos == data.len()), no more data
106
        assert_eq!(buffer.current_pos(), 3);
1✔
107
        assert_eq!(
1✔
108
            buffer.current_pos(),
1✔
109
            data.len(),
1✔
110
            "pos should equal data.len()"
×
111
        );
112

113
        // INTENTIONAL DESIGN: Different semantics when pos == data.len()
114
        // - is_past_end() returns false (parser can still finish processing)
115
        // - consume_byte() returns Err (no more bytes to read)
116
        // This allows the tokenizer to complete final events (like EndObject)
117
        // even when no input bytes remain to be consumed
118
        assert!(
1✔
119
            !buffer.is_past_end(),
1✔
120
            "pos == data.len() should NOT be past end (allows tokenizer.finish())"
×
121
        );
122
        assert!(
1✔
123
            buffer.consume_byte().is_err(),
1✔
124
            "consume_byte() should fail when pos == data.len() (no bytes)"
×
125
        );
126

127
        // Position 4: past end (pos > data.len()), definitely error
128
        assert_eq!(buffer.current_pos(), 4);
1✔
129
        assert!(buffer.is_past_end(), "pos > data.len() should be past end");
1✔
130
        assert!(
1✔
131
            buffer.consume_byte().is_err(),
1✔
132
            "consume_byte() should fail when pos > data.len()"
×
133
        );
134
    }
1✔
135
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc