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

kaidokert / picojson-rs / 16059498867

03 Jul 2025 08:01PM UTC coverage: 94.515% (-0.01%) from 94.527%
16059498867

Pull #23

github

web-flow
Apply suggestions from code review

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Pull Request #23: Add panic checker tooling

2085 of 2206 relevant lines covered (94.51%)

135.49 hits per line

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

95.56
/picojson/src/ujson/bitstack/mod.rs
1
// SPDX-License-Identifier: Apache-2.0
2

3
use core::cmp::PartialEq;
4
use core::ops::{BitAnd, BitOr, Shl, Shr};
5

6
/// Trait for bit buckets - provides bit storage for JSON parser state.
7
/// This trait is implemented for both integer and [T; N] types.
8
///
9
/// NOTE: BitBucket implementations do NOT implement depth tracking.
10
/// This is the responsibility of the caller.
11
pub trait BitBucket: Default {
12
    /// Pushes a bit (true for 1, false for 0) onto the stack.
13
    fn push(&mut self, bit: bool);
14
    /// Pops the top bit off the stack, returning it if the stack isn’t empty.
15
    fn pop(&mut self) -> bool;
16
    /// Returns the top bit without removing it.
17
    fn top(&self) -> bool;
18
}
19

20
/// Automatic implementation for builtin-types ( u8, u32 etc ).
21
/// Any type that implements the required traits is automatically implemented for BitBucket.
22
impl<T> BitBucket for T
23
where
24
    T: Shl<u8, Output = T>
25
        + Shr<u8, Output = T>
26
        + BitAnd<T, Output = T>
27
        + BitOr<Output = T>
28
        + PartialEq
29
        + Clone
30
        + Default,
31
    T: From<u8>, // To create 0 and 1 constants
32
{
33
    fn push(&mut self, bit: bool) {
426✔
34
        *self = (self.clone() << 1u8) | T::from(bit as u8);
426✔
35
    }
426✔
36

37
    fn pop(&mut self) -> bool {
121✔
38
        let bit = (self.clone() & T::from(1)) != T::from(0);
121✔
39
        *self = self.clone() >> 1u8;
121✔
40
        bit
121✔
41
    }
121✔
42

43
    fn top(&self) -> bool {
240✔
44
        (self.clone() & T::from(1)) != T::from(0)
240✔
45
    }
240✔
46
}
47

48
// TODO: Can this be implemented for slices as well ?
49

50
/// Trait for depth counters - tracks nesting depth.
51
///
52
/// This is automatically implemented for any type that satisfies the individual bounds.
53
pub trait DepthCounter:
54
    From<u8>
55
    + core::cmp::PartialEq<Self>
56
    + core::ops::AddAssign<Self>
57
    + core::ops::SubAssign<Self>
58
    + core::ops::Not<Output = Self>
59
    + core::fmt::Debug
60
{
61
}
62

63
impl<T> DepthCounter for T where
64
    T: From<u8>
65
        + core::cmp::PartialEq<T>
66
        + core::ops::AddAssign<T>
67
        + core::ops::SubAssign<T>
68
        + core::ops::Not<Output = T>
69
        + core::fmt::Debug
70
{
71
}
72

73
/// Configuration trait for BitStack systems - defines bucket and counter types.
74
pub trait BitStackConfig {
75
    type Bucket: BitBucket + Default;
76
    type Counter: DepthCounter + Default;
77
}
78

79
/// Default depth configuration using [u32] for tracking bits and [u8] for counting depth.
80
pub struct DefaultConfig;
81

82
impl BitStackConfig for DefaultConfig {
83
    type Bucket = u32;
84
    type Counter = u8;
85
}
86
/// BitStack configuration for custom bit depth parsing.
87
///
88
/// Allows specifying custom types for bit storage and depth counting.
89
///
90
/// # Type Parameters
91
///
92
/// * `B` - The bit bucket type used for storing the bit stack. Must implement [`BitBucket`].
93
/// * `C` - The counter type used for tracking nesting depth. Must implement [`DepthCounter`].
94
///
95
/// Example: `BitStack<u64, u16>` for 64-bit nesting depth with 16 counter.
96
pub struct BitStackStruct<B, C> {
97
    _phantom: core::marker::PhantomData<(B, C)>,
98
}
99

100
impl<B, C> BitStackConfig for BitStackStruct<B, C>
101
where
102
    B: BitBucket + Default,
103
    C: DepthCounter + Default,
104
{
105
    type Bucket = B;
106
    type Counter = C;
107
}
108

109
/// Array-based BitStack implementation for large storage capacity.
110
///
111
/// Example use:
112
/// ```rust
113
/// # use picojson::{SliceParser, ArrayBitStack};
114
/// let parser = SliceParser::<ArrayBitStack<10, u32, u16>>::with_config("{}");
115
/// ```
116
/// This defines a 10-element array of [u32] for depth tracking bits, with a [u16] counter, allowing 320 levels of depth.
117
pub type ArrayBitStack<const N: usize, T, D> = BitStackStruct<ArrayBitBucket<N, T>, D>;
118

119
/// Array-based BitBucket implementation for large storage capacity.
120
///
121
/// Provides large BitBucket storage using multiple elements.
122
/// This can be used to parse very deeply nested JSON.
123
///
124
/// Use the [ArrayBitStack] convenience wrapper to create this.
125
#[derive(Debug)]
126
pub struct ArrayBitBucket<const N: usize, T>(pub [T; N]);
127

128
impl<const N: usize, T: Default + Copy> Default for ArrayBitBucket<N, T> {
129
    fn default() -> Self {
11✔
130
        ArrayBitBucket([T::default(); N])
11✔
131
    }
11✔
132
}
133

134
impl<const N: usize, T> BitBucket for ArrayBitBucket<N, T>
135
where
136
    T: Shl<u8, Output = T>
137
        + Shr<u8, Output = T>
138
        + BitAnd<T, Output = T>
139
        + core::ops::BitOr<Output = T>
140
        + PartialEq
141
        + Clone
142
        + From<u8>
143
        + Copy
144
        + Default,
145
{
146
    fn push(&mut self, bit: bool) {
78✔
147
        // Strategy: Use array as big-endian storage, with leftmost element as most significant
148
        // Shift all elements left, carrying overflow from right to left
149
        let bit_val = T::from(bit as u8);
78✔
150
        let mut carry = bit_val;
78✔
151
        let element_bits = (core::mem::size_of::<T>() * 8) as u8;
78✔
152
        let msb_shift = element_bits - 1;
78✔
153

154
        // Start from the rightmost (least significant) element and work left
155
        for i in (0..N).rev() {
276✔
156
            let old_msb = (self.0[i] >> msb_shift) & T::from(1); // Extract MSB that will be lost
276✔
157
            self.0[i] = (self.0[i] << 1u8) | carry;
276✔
158
            carry = old_msb;
276✔
159
        }
276✔
160
        // Note: carry from leftmost element is discarded (overflow)
161
    }
78✔
162

163
    fn pop(&mut self) -> bool {
86✔
164
        // Extract rightmost bit from least significant element
165
        let bit = (self.0[N - 1] & T::from(1)) != T::from(0);
86✔
166

167
        // Shift all elements right, carrying underflow from left to right
168
        let mut carry = T::from(0);
86✔
169
        let element_bits = (core::mem::size_of::<T>() * 8) as u8;
86✔
170
        let msb_shift = element_bits - 1;
86✔
171

172
        // Start from the leftmost (most significant) element and work right
173
        for i in 0..N {
373✔
174
            let old_lsb = self.0[i] & T::from(1); // Extract LSB that will be lost
287✔
175
            self.0[i] = (self.0[i] >> 1u8) | (carry << msb_shift);
287✔
176
            carry = old_lsb;
287✔
177
        }
287✔
178

179
        bit
86✔
180
    }
86✔
181

182
    fn top(&self) -> bool {
19✔
183
        // Return rightmost bit from least significant element without modifying
184
        (self.0[N - 1] & T::from(1)) != T::from(0)
19✔
185
    }
19✔
186
}
187

188
#[cfg(test)]
189
mod tests {
190
    use super::*;
191

192
    #[test]
193
    fn test_bitstack() {
1✔
194
        let mut bitstack = 0;
1✔
195
        bitstack.push(true);
1✔
196
        bitstack.push(false);
1✔
197
        assert_eq!(bitstack.pop(), false);
1✔
198
        assert_eq!(bitstack.pop(), true);
1✔
199
    }
1✔
200

201
    #[test]
202
    fn test_array_bitstack() {
1✔
203
        // Test ArrayBitStack with 2 u8 elements (16-bit total capacity)
204
        let mut bitstack: ArrayBitBucket<2, u8> = ArrayBitBucket::default();
1✔
205

206
        // Test basic push/pop operations
207
        bitstack.push(true);
1✔
208
        bitstack.push(false);
1✔
209
        bitstack.push(true);
1✔
210

211
        // Verify top() doesn't modify stack
212
        assert_eq!(bitstack.top(), true);
1✔
213
        assert_eq!(bitstack.top(), true);
1✔
214

215
        // Verify LIFO order
216
        assert_eq!(bitstack.pop(), true);
1✔
217
        assert_eq!(bitstack.pop(), false);
1✔
218
        assert_eq!(bitstack.pop(), true);
1✔
219
    }
1✔
220

221
    #[test]
222
    fn test_array_bitstack_large_capacity() {
1✔
223
        // Test larger ArrayBitStack (320-bit capacity with 10 u32 elements)
224
        let mut bitstack: ArrayBitBucket<10, u32> = ArrayBitBucket::default();
1✔
225

226
        // Push many bits to test multi-element handling
227
        let pattern = [true, false, true, true, false, false, true, false];
1✔
228
        for &bit in &pattern {
9✔
229
            bitstack.push(bit);
8✔
230
        }
8✔
231

232
        // Pop and verify reverse order (LIFO)
233
        for &expected in pattern.iter().rev() {
8✔
234
            assert_eq!(bitstack.pop(), expected);
8✔
235
        }
236
    }
1✔
237

238
    #[test]
239
    fn test_element_size_handling() {
1✔
240
        // Test that bitstack correctly handles different element sizes
241

242
        // Test u8 elements (8-bit each)
243
        let mut bitstack_u8: ArrayBitBucket<1, u8> = ArrayBitBucket::default();
1✔
244

245
        // Fill all 8 bits of a u8 element
246
        for i in 0..8 {
9✔
247
            bitstack_u8.push(i % 2 == 0); // alternating pattern: true, false, true, false...
8✔
248
        }
8✔
249

250
        // Verify we can retrieve all 8 bits in LIFO order
251
        for i in (0..8).rev() {
8✔
252
            assert_eq!(bitstack_u8.pop(), i % 2 == 0);
8✔
253
        }
254

255
        // Test u32 elements (32-bit each)
256
        let mut bitstack_u32: ArrayBitBucket<1, u32> = ArrayBitBucket::default();
1✔
257

258
        // Fill all 32 bits of a u32 element
259
        for i in 0..32 {
33✔
260
            bitstack_u32.push(i % 3 == 0); // pattern: true, false, false, true, false, false...
32✔
261
        }
32✔
262

263
        // Verify we can retrieve all 32 bits in LIFO order
264
        for i in (0..32).rev() {
32✔
265
            assert_eq!(bitstack_u32.pop(), i % 3 == 0);
32✔
266
        }
267
    }
1✔
268

269
    #[test]
270
    fn test_array_bitstack_basic_moved() {
1✔
271
        // Test ArrayBitStack with 2 u8 elements (16-bit total capacity)
272
        let mut bitstack: ArrayBitBucket<2, u8> = ArrayBitBucket::default();
1✔
273

274
        // Test basic push/pop operations
275
        bitstack.push(true);
1✔
276
        bitstack.push(false);
1✔
277
        bitstack.push(true);
1✔
278

279
        // Verify top() doesn't modify stack
280
        assert_eq!(bitstack.top(), true);
1✔
281
        assert_eq!(bitstack.top(), true);
1✔
282

283
        // Verify LIFO order
284
        assert_eq!(bitstack.pop(), true);
1✔
285
        assert_eq!(bitstack.pop(), false);
1✔
286
        assert_eq!(bitstack.pop(), true);
1✔
287
    }
1✔
288

289
    #[test]
290
    fn test_array_bitstack_large_capacity_moved() {
1✔
291
        // Test larger ArrayBitStack (320-bit capacity with 10 u32 elements)
292
        let mut bitstack: ArrayBitBucket<10, u32> = ArrayBitBucket::default();
1✔
293

294
        // Push many bits to test multi-element handling
295
        let pattern = [true, false, true, true, false, false, true, false];
1✔
296
        for &bit in &pattern {
9✔
297
            bitstack.push(bit);
8✔
298
        }
8✔
299

300
        // Pop and verify reverse order (LIFO)
301
        for &expected in pattern.iter().rev() {
8✔
302
            assert_eq!(bitstack.pop(), expected);
8✔
303
        }
304
    }
1✔
305

306
    #[test]
307
    fn test_array_bitstack_element_overflow_moved() {
1✔
308
        // Test ArrayBitStack with 2 u8 elements to verify cross-element operations
309
        let mut bitstack: ArrayBitBucket<2, u8> = ArrayBitBucket::default();
1✔
310

311
        // Push more than 8 bits to force usage of multiple elements
312
        let bits = [
1✔
313
            true, false, true, false, true, false, true, false, true, true,
1✔
314
        ];
1✔
315
        for &bit in &bits {
11✔
316
            bitstack.push(bit);
10✔
317
        }
10✔
318

319
        // Pop all bits and verify order
320
        for &expected in bits.iter().rev() {
10✔
321
            assert_eq!(bitstack.pop(), expected);
10✔
322
        }
323
    }
1✔
324

325
    #[test]
326
    fn test_array_bitstack_empty_behavior_moved() {
1✔
327
        // Test behavior when popping from an empty ArrayBitStack
328
        // With the new API, empty stacks return false (no depth tracking needed)
329
        let mut bitstack: ArrayBitBucket<2, u8> = ArrayBitBucket::default();
1✔
330

331
        // CURRENT BEHAVIOR: Empty stack returns false (was Some(false) before API change)
332
        // This behavior is now the intended design - no depth tracking needed
333
        assert!(!bitstack.pop(), "Empty stack returns false");
1✔
334
        assert!(!bitstack.top(), "Empty stack top() returns false");
1✔
335

336
        // Test that underflow doesn't panic (at least it's safe)
337
        assert_eq!(
1✔
338
            bitstack.pop(),
1✔
339
            false,
340
            "Multiple underflow calls don't panic"
×
341
        );
342
        assert_eq!(
1✔
343
            bitstack.pop(),
1✔
344
            false,
345
            "Multiple underflow calls don't panic"
×
346
        );
347
    }
1✔
348

349
    #[test]
350
    fn test_array_bitstack_underflow_does_not_panic_moved() {
1✔
351
        // Test that multiple underflow attempts are safe (don't panic)
352
        // This is important for robustness even with the current incorrect API
353
        let mut bitstack: ArrayBitBucket<1, u8> = ArrayBitBucket::default();
1✔
354

355
        // Multiple calls to pop() on empty stack should not panic
356
        for i in 0..5 {
6✔
357
            let result = bitstack.pop();
5✔
358
            // With new API, just ensure it doesn't panic and returns a bool
359
            assert_eq!(
5✔
360
                result,
361
                false,
362
                "Empty ArrayBitStack pop() attempt {} should return false",
×
363
                i + 1
×
364
            );
365

366
            let top_result = bitstack.top();
5✔
367
            assert_eq!(
5✔
368
                top_result,
369
                false,
370
                "Empty ArrayBitStack top() attempt {} should return false",
×
371
                i + 1
×
372
            );
373
        }
374
    }
1✔
375
}
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