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}