Skip to main content

qubit_json/value/
safe_json_value.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! A JSON value whose owned-tree operations use explicit work stacks.
9
10#[cfg(test)]
11use std::cell::Cell;
12use std::fmt;
13use std::mem::ManuallyDrop;
14
15use serde_json::Value;
16
17use super::internal::JsonValueCopyFrame;
18use super::internal::SafeValueBuilder;
19use super::internal::debug_value;
20use super::internal::values_equal;
21
22/// An owned JSON value whose clone, equality, debug formatting, and drop use
23/// protected tree operations.
24///
25/// This protection does not extend to arbitrary recursive operations on a
26/// borrowed or extracted [`serde_json::Value`]. [`as_value`](Self::as_value)
27/// returns a shared borrow tied to this owner; callers remain responsible for
28/// the stack behavior of recursive operations they invoke through that borrow.
29/// [`into_value`](Self::into_value) gives up the protected owner and transfers
30/// responsibility for the resulting tree's lifetime to the caller.
31///
32/// Serde interoperability uses `stacker` and still depends on the concrete
33/// serializer or deserializer, which may impose its own recursion limits. The
34/// owner does not enforce an exact heap-memory limit.
35/// Its deserializer accepts `i128` and `u128` events when their values fit the
36/// JSON integer range, maps `None` to `null`, and transparently unwraps `Some`
37/// and newtype payloads.
38///
39/// The tree protection and the meaning of values produced by `Deserialize`
40/// are separate concerns. A direct Serde bridge cannot identify whether a
41/// map-shaped event came from an ordinary object or an upstream deserializer's
42/// private number representation. With `serde_json`'s `arbitrary_precision`
43/// feature, number shapes produced by that bridge can therefore differ from
44/// those produced without the feature. When decoding JSON text and preserving
45/// its numeric meaning is important, use [`JsonDecoder::decode_value_str`]
46/// (or the corresponding byte and reader methods) instead.
47///
48/// Direct deserialization also rejects duplicate object keys after decoding
49/// their string escapes. It does not normalize Unicode. In contrast,
50/// [`Self::new`] takes an existing [`serde_json::Value`] as-is: if an upstream
51/// deserializer has already merged duplicate keys, this type cannot recover
52/// them.
53///
54/// [`JsonDecoder::decode_value_str`]: crate::decode::JsonDecoder::decode_value_str
55///
56/// # Examples
57///
58/// ```
59/// use qubit_json::value::SafeJsonValue;
60/// use serde_json::json;
61///
62/// let owner = SafeJsonValue::new(json!({"items": [1, 2]}));
63/// let copy = owner.clone();
64/// assert_eq!(owner, copy);
65/// assert_eq!(owner.as_value()["items"][0], 1);
66/// let shallow_raw = copy.into_value();
67/// assert!(shallow_raw.is_object());
68/// assert!(serde_json::from_str::<SafeJsonValue>(r#"{"a":1,"\u0061":2}"#).is_err());
69/// ```
70pub struct SafeJsonValue {
71    /// The root is optional so protected operations can take it before cleanup.
72    root: Option<Value>,
73}
74
75impl SafeJsonValue {
76    /// Takes ownership of a JSON tree without traversing it or admitting it to
77    /// a budget.
78    ///
79    /// # Parameters
80    ///
81    /// * `value` - JSON tree whose ownership is transferred to this owner.
82    ///
83    /// # Returns
84    ///
85    /// An owner for the supplied tree. Construction does not copy or allocate.
86    #[must_use]
87    #[inline]
88    pub fn new(value: Value) -> Self {
89        Self { root: Some(value) }
90    }
91
92    /// Copies an entire JSON tree using an explicit traversal stack.
93    ///
94    /// This operation allocates work-stack storage and result storage for the
95    /// copied tree; it does not impose a budget on the source tree.
96    ///
97    /// # Parameters
98    ///
99    /// * `value` - JSON tree to copy.
100    ///
101    /// # Returns
102    ///
103    /// An independently owned copy of the complete tree.
104    #[must_use]
105    pub fn copy_from(value: &Value) -> Self {
106        #[cfg(test)]
107        COPY_FROM_CALLS.with(|calls| calls.set(calls.get() + 1));
108
109        let mut frames: Vec<JsonValueCopyFrame<'_>> = Vec::new();
110        let mut builders: Vec<SafeValueBuilder> = Vec::new();
111        let mut next_value = Some(value);
112        let mut completed_value = None;
113        loop {
114            if let Some(source) = next_value.take() {
115                match source {
116                    Value::Array(values) => {
117                        let mut frame = JsonValueCopyFrame::array(values);
118                        if let Some(child) = frame.next_value() {
119                            if frame.has_remaining() {
120                                frame.start_builder(&mut builders);
121                            }
122                            frames.push(frame);
123                            next_value = Some(child);
124                        } else {
125                            completed_value = Some(frame.finish_empty());
126                        }
127                    }
128                    Value::Object(values) => {
129                        let mut frame = JsonValueCopyFrame::object(values);
130                        if let Some(child) = frame.next_value() {
131                            if frame.has_remaining() {
132                                frame.start_builder(&mut builders);
133                            }
134                            frames.push(frame);
135                            next_value = Some(child);
136                        } else {
137                            completed_value = Some(frame.finish_empty());
138                        }
139                    }
140                    leaf => completed_value = Some(Self::new(leaf.clone())),
141                }
142            }
143
144            if let Some(child) = completed_value.take() {
145                let Some(parent) = frames.last_mut() else {
146                    return child;
147                };
148                if let Some(completed_parent) = parent.push_child(child, &mut builders) {
149                    frames.pop().expect("a completed child has a parent frame");
150                    completed_value = Some(completed_parent);
151                } else if let Some(source) = parent.next_value() {
152                    next_value = Some(source);
153                } else {
154                    completed_value = Some(
155                        frames
156                            .pop()
157                            .expect("a completed child has a parent frame")
158                            .finish(&mut builders),
159                    );
160                }
161            }
162        }
163    }
164
165    /// Returns a shared borrow of the underlying JSON tree for
166    /// interoperability.
167    ///
168    /// The returned borrow is tied to this owner. Recursive operations
169    /// performed through it are the caller's responsibility and may use the
170    /// backend's stack behavior.
171    ///
172    /// # Returns
173    ///
174    /// The underlying tree borrowed for no longer than this owner.
175    #[must_use]
176    #[inline]
177    pub fn as_value(&self) -> &Value {
178        self.root.as_ref().expect("owner root is present")
179    }
180
181    /// Transfers the tree and its destruction responsibility to the caller.
182    ///
183    /// The returned `Value` is no longer protected by this owner; recursive
184    /// operations and destruction are the caller's responsibility.
185    ///
186    /// # Returns
187    ///
188    /// The previously owned JSON tree.
189    #[must_use]
190    #[inline]
191    pub fn into_value(self) -> Value {
192        let mut this = ManuallyDrop::new(self);
193        this.root.take().expect("owner root is present")
194    }
195}
196
197impl Clone for SafeJsonValue {
198    /// Copies the complete JSON tree through the owner's guarded traversal.
199    fn clone(&self) -> Self {
200        Self::copy_from(self.as_value())
201    }
202}
203
204impl PartialEq for SafeJsonValue {
205    /// Compares two complete JSON trees without recursive descent.
206    ///
207    /// # Parameters
208    ///
209    /// * `other` - JSON owner whose tree is compared with this owner's tree.
210    ///
211    /// # Returns
212    ///
213    /// `true` when both trees contain equal JSON values.
214    fn eq(&self, other: &Self) -> bool {
215        values_equal(self.as_value(), other.as_value())
216    }
217}
218
219impl Eq for SafeJsonValue {}
220
221impl fmt::Debug for SafeJsonValue {
222    /// Formats the complete JSON tree through the owner's stack-safe formatter.
223    ///
224    /// # Parameters
225    ///
226    /// * `formatter` - Formatter that receives the tree's debug representation.
227    ///
228    /// # Returns
229    ///
230    /// The formatter's result, including any formatting error.
231    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
232        debug_value(self.as_value(), formatter)
233    }
234}
235
236impl Drop for SafeJsonValue {
237    /// Destroys the owned tree iteratively, with an allocation-free fallback.
238    fn drop(&mut self) {
239        if let Some(mut root) = self.root.take() {
240            drop_tree(&mut root);
241        }
242    }
243}
244
245/// Drops a value tree iteratively, falling back to allocation-free traversal.
246///
247/// # Parameters
248///
249/// * `root` - Root value whose descendants must be destroyed without recursive
250///   container drops.
251pub(super) fn drop_tree(root: &mut Value) {
252    let mut pending = Vec::new();
253    if !try_reserve_drop_work(&mut pending, 1) {
254        drop_tree_slow(root);
255        return;
256    }
257    pending.push(std::mem::replace(root, Value::Null));
258    while let Some(value) = pending.pop() {
259        match value {
260            Value::Array(mut values) => {
261                if !try_reserve_drop_work(&mut pending, values.len()) {
262                    for child in &mut values {
263                        drop_tree_slow(child);
264                    }
265                    for child in &mut pending {
266                        drop_tree_slow(child);
267                    }
268                    return;
269                }
270                pending.append(&mut values);
271            }
272            Value::Object(mut values) => {
273                if !try_reserve_drop_work(&mut pending, values.len()) {
274                    for child in values.values_mut() {
275                        drop_tree_slow(child);
276                    }
277                    for child in &mut pending {
278                        drop_tree_slow(child);
279                    }
280                    return;
281                }
282                pending.extend(values.into_values());
283            }
284            shallow => drop(shallow),
285        }
286    }
287}
288
289/// Attempts to grow the explicit cleanup stack without panicking on allocation
290/// failure.
291///
292/// # Parameters
293///
294/// * `pending` - Cleanup stack whose capacity is to be increased.
295/// * `additional` - Minimum additional number of elements to reserve.
296///
297/// # Returns
298///
299/// `true` if the requested capacity is available; otherwise, `false`.
300fn try_reserve_drop_work(pending: &mut Vec<Value>, additional: usize) -> bool {
301    #[cfg(test)]
302    if FAIL_DROP_RESERVE_AFTER.with(|remaining| match remaining.get() {
303        Some(0) => {
304            remaining.set(None);
305            true
306        }
307        Some(count) => {
308            remaining.set(Some(count - 1));
309            false
310        }
311        None => false,
312    }) {
313        return false;
314    }
315    pending.try_reserve(additional).is_ok()
316}
317
318#[cfg(test)]
319thread_local! {
320    static COPY_FROM_CALLS: Cell<usize> = const { Cell::new(0) };
321    static FAIL_DROP_RESERVE_AFTER: Cell<Option<usize>> = const { Cell::new(None) };
322    static PANIC_AFTER_INSERT: Cell<Option<usize>> = const { Cell::new(None) };
323}
324
325#[cfg(test)]
326impl SafeJsonValue {
327    pub(crate) fn take_copy_from_calls_for_test() -> usize {
328        COPY_FROM_CALLS.with(|calls| calls.replace(0))
329    }
330
331    pub(crate) fn fail_drop_reserve_after_for_test(successful_reserves: usize) {
332        FAIL_DROP_RESERVE_AFTER.with(|remaining| remaining.set(Some(successful_reserves)));
333    }
334
335    pub(crate) fn assert_drop_reserve_failure_consumed_for_test() {
336        assert!(FAIL_DROP_RESERVE_AFTER.with(Cell::get).is_none());
337    }
338
339    pub(crate) fn panic_after_guarded_inserts_for_test(count: usize) {
340        PANIC_AFTER_INSERT.with(|remaining| remaining.set(Some(count)));
341    }
342
343    pub(crate) fn assert_guarded_insert_panic_consumed_for_test() {
344        assert!(PANIC_AFTER_INSERT.with(|remaining| remaining.get().is_none()));
345    }
346}
347
348/// Injects an unwind after guarded inserts in tests; this is a no-op in builds.
349pub(super) fn maybe_panic_after_guarded_insert() {
350    #[cfg(test)]
351    PANIC_AFTER_INSERT.with(|remaining| {
352        if let Some(count) = remaining.get() {
353            if count <= 1 {
354                remaining.set(None);
355                panic!("injected unwind while building a protected JSON value");
356            }
357            remaining.set(Some(count - 1));
358        }
359    });
360}
361
362/// Cleans a tree without allocating by repeatedly removing leaves in place.
363///
364/// # Parameters
365///
366/// * `root` - Root value to clean in place using only shallow leaf drops.
367fn drop_tree_slow(root: &mut Value) {
368    loop {
369        let mut current = std::ptr::from_mut(root);
370        loop {
371            // SAFETY: `current` always points into `root`; no collection is
372            // structurally changed while its child pointer is being followed.
373            let value = unsafe { &mut *current };
374            let child = match value {
375                Value::Array(values) => {
376                    values.retain(|value| !value.is_null());
377                    values.first_mut().map(std::ptr::from_mut)
378                }
379                Value::Object(values) => {
380                    values.retain(|_, value| !value.is_null());
381                    values.values_mut().next().map(std::ptr::from_mut)
382                }
383                _ => None,
384            };
385            if let Some(child) = child {
386                current = child;
387                continue;
388            }
389            // SAFETY: `current` was obtained from the current tree and points
390            // at a leaf or empty container, whose destruction is shallow.
391            let old = unsafe { std::mem::replace(&mut *current, Value::Null) };
392            drop(old);
393            break;
394        }
395        if root.is_null() {
396            break;
397        }
398    }
399}