picojson 0.2.3

A resource-constrained JSON parser for embedded systems.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
// SPDX-License-Identifier: Apache-2.0

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

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

/// Automatic implementation for builtin-types ( u8, u32 etc ).
/// Any type that implements the required traits is automatically implemented for BitBucket.
impl<T> BitBucket for T
where
    T: Shl<u8, Output = T>
        + Shr<u8, Output = T>
        + BitAnd<T, Output = T>
        + BitOr<Output = T>
        + PartialEq
        + Clone
        + Default,
    T: From<u8>, // To create 0 and 1 constants
{
    fn push(&mut self, bit: bool) {
        *self = (self.clone() << 1u8) | T::from(bit as u8);
    }

    fn pop(&mut self) -> bool {
        let bit = (self.clone() & T::from(1)) != T::from(0);
        *self = self.clone() >> 1u8;
        bit
    }

    fn top(&self) -> bool {
        (self.clone() & T::from(1)) != T::from(0)
    }
}

/// Trait for depth counters - tracks nesting depth.
///
/// This trait provides overflow-safe operations for tracking JSON nesting depth.
/// Implemented for all unsigned integer types.
pub trait DepthCounter: core::fmt::Debug + Copy {
    /// Create a zero depth value
    fn zero() -> Self;

    /// Increment depth, returning (new_value, overflow_occurred)
    fn increment(self) -> (Self, bool);

    /// Decrement depth, returning (new_value, underflow_occurred)
    fn decrement(self) -> (Self, bool);

    /// Check if depth is zero
    fn is_zero(self) -> bool;
}

macro_rules! impl_depth_counter {
    ($($t:ty),*) => {
        $(
            impl DepthCounter for $t {
                #[inline]
                fn zero() -> Self { 0 }

                #[inline]
                fn increment(self) -> (Self, bool) { self.overflowing_add(1) }

                #[inline]
                fn decrement(self) -> (Self, bool) { self.overflowing_sub(1) }

                #[inline]
                fn is_zero(self) -> bool { self == 0 }
            }
        )*
    };
}

// Implement for all unsigned integer types
impl_depth_counter!(u8, u16, u32, u64, u128, usize);

/// Configuration trait for BitStack systems - defines bucket and counter types.
pub trait BitStackConfig {
    /// The type used for storing the bit stack (e.g., u32, or a custom array-based type).
    /// This type must implement the [`BitBucket`] trait.
    type Bucket: BitBucket + Default;
    /// The type used for tracking nesting depth (e.g., u8).
    /// This type must implement the [`DepthCounter`] trait.
    type Counter: DepthCounter + Default;
}

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

impl BitStackConfig for DefaultConfig {
    type Bucket = u32;
    type Counter = u8;
}
/// BitStack configuration for custom bit depth parsing.
///
/// Allows specifying custom types for bit storage and depth counting.
///
/// # Type Parameters
///
/// * `B` - The bit bucket type used for storing the bit stack. Must implement [`BitBucket`].
/// * `C` - The counter type used for tracking nesting depth. Must implement [`DepthCounter`].
///
/// Example: `BitStack<u64, u16>` for 64-bit nesting depth with 16 counter.
pub struct BitStackStruct<B, C> {
    _phantom: core::marker::PhantomData<(B, C)>,
}

impl<B, C> BitStackConfig for BitStackStruct<B, C>
where
    B: BitBucket + Default,
    C: DepthCounter + Default,
{
    type Bucket = B;
    type Counter = C;
}

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

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

impl<const N: usize, T: Default + Copy> Default for ArrayBitBucket<N, T> {
    fn default() -> Self {
        ArrayBitBucket([T::default(); N])
    }
}

impl<const N: usize, T> BitBucket for ArrayBitBucket<N, T>
where
    T: Shl<u8, Output = T>
        + Shr<u8, Output = T>
        + BitAnd<T, Output = T>
        + core::ops::BitOr<Output = T>
        + PartialEq
        + Clone
        + From<u8>
        + Copy
        + Default,
{
    fn push(&mut self, bit: bool) {
        // Strategy: Use array as big-endian storage, with leftmost element as most significant
        // Shift all elements left, carrying overflow from right to left
        let bit_val = T::from(bit as u8);
        let mut carry = bit_val;
        let element_bits = (core::mem::size_of::<T>() * 8) as u8;
        let msb_shift = element_bits.saturating_sub(1);

        // Start from the rightmost (least significant) element and work left
        for i in (0..N).rev() {
            let old_msb = if let Some(element) = self.0.get(i) {
                (*element >> msb_shift) & T::from(1) // Extract MSB that will be lost
            } else {
                continue;
            };
            if let Some(element_mut) = self.0.get_mut(i) {
                *element_mut = (*element_mut << 1u8) | carry;
            }
            carry = old_msb;
        }
        // Note: carry from leftmost element is discarded (overflow)
    }

    fn pop(&mut self) -> bool {
        // Safely get the last element, returning false if N is 0.
        let bit = if let Some(last_element) = self.0.get(N.saturating_sub(1)) {
            (*last_element & T::from(1)) != T::from(0)
        } else {
            return false;
        };

        // Shift all elements right, carrying underflow from left to right
        let mut carry = T::from(0);
        let element_bits = (core::mem::size_of::<T>() * 8) as u8;
        let msb_shift = element_bits.saturating_sub(1);

        // Start from the leftmost (most significant) element and work right
        for i in 0..N {
            let old_lsb = if let Some(element) = self.0.get(i) {
                *element & T::from(1) // Extract LSB that will be lost
            } else {
                continue;
            };
            if let Some(element_mut) = self.0.get_mut(i) {
                *element_mut = (*element_mut >> 1u8) | (carry << msb_shift);
            }
            carry = old_lsb;
        }

        bit
    }

    fn top(&self) -> bool {
        // Safely get the last element, returning false if N is 0.
        if let Some(last_element) = self.0.get(N.saturating_sub(1)) {
            (*last_element & T::from(1)) != T::from(0)
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_bitstack() {
        let mut bitstack = 0;
        bitstack.push(true);
        bitstack.push(false);
        assert!(!bitstack.pop());
        assert!(bitstack.pop());
    }

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

        // Test basic push/pop operations
        bitstack.push(true);
        bitstack.push(false);
        bitstack.push(true);

        // Verify top() doesn't modify stack
        assert!(bitstack.top());
        assert!(bitstack.top());

        // Verify LIFO order
        assert!(bitstack.pop());
        assert!(!bitstack.pop());
        assert!(bitstack.pop());
    }

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

        // Push many bits to test multi-element handling
        let pattern = [true, false, true, true, false, false, true, false];
        for &bit in &pattern {
            bitstack.push(bit);
        }

        // Pop and verify reverse order (LIFO)
        for &expected in pattern.iter().rev() {
            assert_eq!(bitstack.pop(), expected);
        }
    }

    #[test]
    fn test_element_size_handling() {
        // Test that bitstack correctly handles different element sizes

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

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

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

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

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

        // Verify we can retrieve all 32 bits in LIFO order
        for i in (0..32).rev() {
            assert_eq!(bitstack_u32.pop(), i % 3 == 0);
        }
    }

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

        // Test basic push/pop operations
        bitstack.push(true);
        bitstack.push(false);
        bitstack.push(true);

        // Verify top() doesn't modify stack
        assert!(bitstack.top());
        assert!(bitstack.top());

        // Verify LIFO order
        assert!(bitstack.pop());
        assert!(!bitstack.pop());
        assert!(bitstack.pop());
    }

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

        // Push many bits to test multi-element handling
        let pattern = [true, false, true, true, false, false, true, false];
        for &bit in &pattern {
            bitstack.push(bit);
        }

        // Pop and verify reverse order (LIFO)
        for &expected in pattern.iter().rev() {
            assert_eq!(bitstack.pop(), expected);
        }
    }

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

        // Push more than 8 bits to force usage of multiple elements
        let bits = [
            true, false, true, false, true, false, true, false, true, true,
        ];
        for &bit in &bits {
            bitstack.push(bit);
        }

        // Pop all bits and verify order
        for &expected in bits.iter().rev() {
            assert_eq!(bitstack.pop(), expected);
        }
    }

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

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

        // Test that underflow doesn't panic (at least it's safe)
        assert!(!bitstack.pop(), "Multiple underflow calls don't panic");
        assert!(!bitstack.pop(), "Multiple underflow calls don't panic");
    }

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

        // Multiple calls to pop() on empty stack should not panic
        for i in 0..5 {
            let result = bitstack.pop();
            // With new API, just ensure it doesn't panic and returns a bool
            assert!(
                !result,
                "Empty ArrayBitStack pop() attempt {} should return false",
                i + 1
            );

            let top_result = bitstack.top();
            assert!(
                !top_result,
                "Empty ArrayBitStack top() attempt {} should return false",
                i + 1
            );
        }
    }
}