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    fn clone(&self) -> Self {
199        Self::copy_from(self.as_value())
200    }
201}
202
203impl PartialEq for SafeJsonValue {
204    fn eq(&self, other: &Self) -> bool {
205        values_equal(self.as_value(), other.as_value())
206    }
207}
208
209impl Eq for SafeJsonValue {}
210
211impl fmt::Debug for SafeJsonValue {
212    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
213        debug_value(self.as_value(), formatter)
214    }
215}
216
217impl Drop for SafeJsonValue {
218    fn drop(&mut self) {
219        if let Some(mut root) = self.root.take() {
220            drop_tree(&mut root);
221        }
222    }
223}
224
225/// Drops a value tree iteratively, falling back to allocation-free traversal.
226pub(super) fn drop_tree(root: &mut Value) {
227    let mut pending = Vec::new();
228    if !try_reserve_drop_work(&mut pending, 1) {
229        drop_tree_slow(root);
230        return;
231    }
232    pending.push(std::mem::replace(root, Value::Null));
233    while let Some(value) = pending.pop() {
234        match value {
235            Value::Array(mut values) => {
236                if !try_reserve_drop_work(&mut pending, values.len()) {
237                    for child in &mut values {
238                        drop_tree_slow(child);
239                    }
240                    for child in &mut pending {
241                        drop_tree_slow(child);
242                    }
243                    return;
244                }
245                pending.append(&mut values);
246            }
247            Value::Object(mut values) => {
248                if !try_reserve_drop_work(&mut pending, values.len()) {
249                    for child in values.values_mut() {
250                        drop_tree_slow(child);
251                    }
252                    for child in &mut pending {
253                        drop_tree_slow(child);
254                    }
255                    return;
256                }
257                pending.extend(values.into_values());
258            }
259            shallow => drop(shallow),
260        }
261    }
262}
263
264/// Attempts to grow the explicit cleanup stack without panicking on allocation
265/// failure.
266fn try_reserve_drop_work(pending: &mut Vec<Value>, additional: usize) -> bool {
267    #[cfg(test)]
268    if FAIL_DROP_RESERVE_AFTER.with(|remaining| match remaining.get() {
269        Some(0) => {
270            remaining.set(None);
271            true
272        }
273        Some(count) => {
274            remaining.set(Some(count - 1));
275            false
276        }
277        None => false,
278    }) {
279        return false;
280    }
281    pending.try_reserve(additional).is_ok()
282}
283
284#[cfg(test)]
285thread_local! {
286    static COPY_FROM_CALLS: Cell<usize> = const { Cell::new(0) };
287    static FAIL_DROP_RESERVE_AFTER: Cell<Option<usize>> = const { Cell::new(None) };
288    static PANIC_AFTER_INSERT: Cell<Option<usize>> = const { Cell::new(None) };
289}
290
291#[cfg(test)]
292impl SafeJsonValue {
293    pub(crate) fn take_copy_from_calls_for_test() -> usize {
294        COPY_FROM_CALLS.with(|calls| calls.replace(0))
295    }
296
297    pub(crate) fn fail_drop_reserve_after_for_test(successful_reserves: usize) {
298        FAIL_DROP_RESERVE_AFTER.with(|remaining| remaining.set(Some(successful_reserves)));
299    }
300
301    pub(crate) fn assert_drop_reserve_failure_consumed_for_test() {
302        assert!(FAIL_DROP_RESERVE_AFTER.with(Cell::get).is_none());
303    }
304
305    pub(crate) fn panic_after_guarded_inserts_for_test(count: usize) {
306        PANIC_AFTER_INSERT.with(|remaining| remaining.set(Some(count)));
307    }
308
309    pub(crate) fn assert_guarded_insert_panic_consumed_for_test() {
310        assert!(PANIC_AFTER_INSERT.with(|remaining| remaining.get().is_none()));
311    }
312}
313
314/// Injects an unwind after guarded inserts in tests; this is a no-op in builds.
315pub(super) fn maybe_panic_after_guarded_insert() {
316    #[cfg(test)]
317    PANIC_AFTER_INSERT.with(|remaining| {
318        if let Some(count) = remaining.get() {
319            if count <= 1 {
320                remaining.set(None);
321                panic!("injected unwind while building a protected JSON value");
322            }
323            remaining.set(Some(count - 1));
324        }
325    });
326}
327
328/// Cleans a tree without allocating by repeatedly removing leaves in place.
329fn drop_tree_slow(root: &mut Value) {
330    loop {
331        let mut current = std::ptr::from_mut(root);
332        loop {
333            // SAFETY: `current` always points into `root`; no collection is
334            // structurally changed while its child pointer is being followed.
335            let value = unsafe { &mut *current };
336            let child = match value {
337                Value::Array(values) => {
338                    values.retain(|value| !value.is_null());
339                    values.first_mut().map(std::ptr::from_mut)
340                }
341                Value::Object(values) => {
342                    values.retain(|_, value| !value.is_null());
343                    values.values_mut().next().map(std::ptr::from_mut)
344                }
345                _ => None,
346            };
347            if let Some(child) = child {
348                current = child;
349                continue;
350            }
351            // SAFETY: `current` was obtained from the current tree and points
352            // at a leaf or empty container, whose destruction is shallow.
353            let old = unsafe { std::mem::replace(&mut *current, Value::Null) };
354            drop(old);
355            break;
356        }
357        if root.is_null() {
358            break;
359        }
360    }
361}