fionn_tape/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! SIMD-DSON tape wrapper using SIMD-JSON
3//!
4//! This module provides a wrapper around SIMD-JSON's tape structure
5//! for efficient zero-allocation JSON parsing with skip optimization.
6
7use ahash::{AHashMap, AHashSet};
8use fionn_core::{DsonError, Result};
9pub use fionn_core::{ParsedPath, PathComponent, PathComponentRef};
10use simd_json::value::tape::{Node, Tape};
11
12// Type aliases for performance-optimized collections
13type FastHashMap<K, V> = AHashMap<K, V>;
14type FastHashSet<T> = AHashSet<T>;
15
16/// Escape a string for JSON output.
17///
18/// This handles all characters that must be escaped in JSON strings:
19/// - `"` becomes `\"`
20/// - `\` becomes `\\`
21/// - Control characters (0x00-0x1F) are escaped as `\uXXXX` or their short forms
22#[inline]
23fn escape_json_string(s: &str, output: &mut String) {
24    use std::fmt::Write;
25    for c in s.chars() {
26        match c {
27            '"' => output.push_str("\\\""),
28            '\\' => output.push_str("\\\\"),
29            '\n' => output.push_str("\\n"),
30            '\r' => output.push_str("\\r"),
31            '\t' => output.push_str("\\t"),
32            // Control characters (0x00-0x1F) except the ones handled above
33            c if c.is_control() => {
34                let _ = write!(output, "\\u{:04x}", c as u32);
35            }
36            c => output.push(c),
37        }
38    }
39}
40
41/// SIMD-accelerated value representation
42#[derive(Debug, Clone)]
43pub enum SimdValue {
44    /// A string value
45    String(String),
46    /// A null value
47    Null,
48    /// A boolean value
49    Bool(bool),
50    /// A number value stored as string for precision
51    Number(String),
52}
53
54/// SIMD-DSON tape wrapper
55pub struct DsonTape {
56    tape: Tape<'static>,
57    data: Vec<u8>, // Keep the data alive
58}
59
60impl DsonTape {
61    /// Create a new DSON tape from JSON input
62    ///
63    /// # Errors
64    /// Returns an error if the JSON is malformed
65    pub fn parse(json: &str) -> Result<Self> {
66        // SIMD-JSON requires mutable bytes, so we need to copy
67        let mut bytes = json.as_bytes().to_vec();
68        let tape = unsafe {
69            // Extend the lifetime - this is safe because we store the data
70            std::mem::transmute::<Tape<'_>, Tape<'static>>(
71                simd_json::to_tape(&mut bytes)
72                    .map_err(|e| DsonError::ParseError(format!("SIMD-JSON parse error: {e}")))?,
73            )
74        };
75
76        Ok(Self { tape, data: bytes })
77    }
78
79    /// Get the underlying SIMD-JSON tape
80    #[must_use]
81    pub const fn tape(&self) -> &Tape<'static> {
82        &self.tape
83    }
84
85    /// Get tape nodes
86    #[must_use]
87    #[inline]
88    pub fn nodes(&self) -> &[Node<'static>] {
89        &self.tape.0
90    }
91
92    /// Get the root node (first node in tape)
93    #[must_use]
94    #[inline]
95    pub fn root(&self) -> Node<'static> {
96        self.nodes()[0]
97    }
98
99    /// Skip to next field in object/array (for lazy evaluation)
100    ///
101    /// # Errors
102    /// Returns an error if navigation fails
103    pub fn skip_field(&self, current_index: usize) -> Result<Option<usize>> {
104        let nodes = self.nodes();
105        if current_index >= nodes.len() {
106            return Ok(None);
107        }
108
109        // For SIMD acceleration, we need to understand the tape structure better
110        // For now, implement a basic skip that advances past the current field/value
111        let mut index = current_index;
112
113        // Skip the current field name (if we're on one)
114        if let Node::String(_) = &nodes[index] {
115            index += 1; // Skip field name
116        }
117
118        // Skip the field value
119        index = self.skip_value(index)?;
120
121        Ok(Some(index))
122    }
123
124    /// Skip a complete value (object, array, or primitive) starting at the given index
125    ///
126    /// # Errors
127    /// Returns an error if navigation fails
128    pub fn skip_value(&self, start_index: usize) -> Result<usize> {
129        let nodes = self.nodes();
130        if start_index >= nodes.len() {
131            return Ok(start_index);
132        }
133
134        let node = &nodes[start_index];
135
136        // Navigate based on actual Node structure
137        match node {
138            Node::Object { len, count } => {
139                // For objects, we need to skip the object header plus all field name/value pairs
140                // Each field consists of a string key followed by a value
141                // The 'count' field tells us how many total nodes are in the object
142                let skip_count = if *count > 0 { *count } else { *len * 2 + 1 };
143                Ok(start_index + skip_count)
144            }
145            Node::Array { len, count } => {
146                // For arrays, skip the array header plus all elements
147                // The 'count' field tells us how many total nodes are in the array
148                let skip_count = if *count > 0 { *count } else { *len + 1 };
149                Ok(start_index + skip_count)
150            }
151            Node::String(_) | Node::Static(_) => {
152                // Primitives are single nodes
153                Ok(start_index + 1)
154            }
155        }
156    }
157
158    /// SIMD-accelerated schema path matching
159    #[inline]
160    #[must_use]
161    pub fn simd_schema_match(
162        &self,
163        path: &str,
164        schema: &std::collections::HashSet<String>,
165    ) -> bool {
166        let path_bytes = path.as_bytes();
167
168        for schema_path in schema {
169            let schema_bytes = schema_path.as_bytes();
170
171            // Exact match (SIMD-accelerated)
172            if self.simd_string_equals(path_bytes, schema_bytes) {
173                return true;
174            }
175
176            // Path starts with schema (e.g., "user" matches "user.name")
177            if path_bytes.len() > schema_bytes.len()
178                && path_bytes[schema_bytes.len()] == b'.'
179                && self.simd_string_equals(&path_bytes[..schema_bytes.len()], schema_bytes)
180            {
181                return true;
182            }
183
184            // Schema starts with path (e.g., "user.name" matches "user")
185            if schema_bytes.len() > path_bytes.len()
186                && schema_bytes[path_bytes.len()] == b'.'
187                && self.simd_string_equals(&schema_bytes[..path_bytes.len()], path_bytes)
188            {
189                return true;
190            }
191        }
192
193        false
194    }
195
196    /// SIMD-accelerated value type detection and extraction
197    #[must_use]
198    pub fn extract_value_simd(&self, index: usize) -> Option<SimdValue> {
199        let nodes = self.nodes();
200        if index >= nodes.len() {
201            return None;
202        }
203
204        match &nodes[index] {
205            Node::String(s) => Some(SimdValue::String(s.to_string())),
206            Node::Static(static_node) => match static_node {
207                simd_json::StaticNode::Null => Some(SimdValue::Null),
208                simd_json::StaticNode::Bool(b) => Some(SimdValue::Bool(*b)),
209                simd_json::StaticNode::I64(n) => Some(SimdValue::Number(n.to_string())),
210                simd_json::StaticNode::U64(n) => Some(SimdValue::Number(n.to_string())),
211                simd_json::StaticNode::F64(n) => Some(SimdValue::Number(n.to_string())),
212            },
213            _ => None,
214        }
215    }
216
217    /// SIMD-accelerated JSON serialization for specific patterns
218    #[must_use]
219    pub fn serialize_simd(
220        &self,
221        modifications: &std::collections::HashMap<String, fionn_core::OperationValue>,
222    ) -> Option<String> {
223        // For now, implement a fast path for simple modifications
224        // This can be extended to handle more complex cases with SIMD acceleration
225
226        if modifications.is_empty() {
227            // Fast path: return original JSON directly (already SIMD-parsed)
228            return Some(String::from_utf8_lossy(&self.data).to_string());
229        }
230
231        // For modified JSON, we could implement SIMD-accelerated serialization
232        // that avoids the overhead of serde_json for common patterns
233        None // Fall back to existing serialization for now
234    }
235
236    /// Read a field at the given tape index
237    ///
238    /// # Errors
239    /// Returns an error if the field cannot be accessed
240    pub fn read_field(&self, index: usize) -> Result<Node<'static>> {
241        self.nodes()
242            .get(index)
243            .copied()
244            .ok_or_else(|| DsonError::InvalidField(format!("Index {index} out of bounds")))
245    }
246
247    /// Check if a field should survive processing based on schema
248    ///
249    /// A field survives if it matches the given schema paths or if the schema is empty (keep all).
250    #[must_use]
251    #[inline]
252    pub fn should_survive(
253        &self,
254        field_path: &str,
255        schema: &std::collections::HashSet<String>,
256    ) -> bool {
257        // If schema is empty, all fields survive
258        if schema.is_empty() {
259            return true;
260        }
261
262        // Check exact match
263        if schema.contains(field_path) {
264            return true;
265        }
266
267        // Check if any schema path is a prefix of field_path (parent survives)
268        for schema_path in schema {
269            // Parent path survives (e.g., "user" survives if "user.name" is in schema)
270            if field_path.starts_with(schema_path)
271                && (field_path.len() == schema_path.len()
272                    || field_path.as_bytes().get(schema_path.len()) == Some(&b'.'))
273            {
274                return true;
275            }
276
277            // Child path survives (e.g., "user.name" survives if "user" is in schema)
278            if schema_path.starts_with(field_path)
279                && (schema_path.len() == field_path.len()
280                    || schema_path.as_bytes().get(field_path.len()) == Some(&b'.'))
281            {
282                return true;
283            }
284
285            // Wildcard matching (e.g., "users.*" matches "users.name")
286            if schema_path.ends_with(".*") {
287                let prefix = &schema_path[..schema_path.len() - 2];
288                if field_path.starts_with(prefix) {
289                    return true;
290                }
291            }
292        }
293
294        false
295    }
296
297    /// Create a filtered tape containing only schema-matching paths
298    ///
299    /// This implementation converts to JSON, filters, and re-parses.
300    /// While not true zero-copy tape-level filtering, this approach is correct
301    /// and provides proper schema-based filtering with SIMD-accelerated parsing.
302    ///
303    /// # Errors
304    /// Returns an error if filtering fails
305    pub fn filter_by_schema(&self, schema: &std::collections::HashSet<String>) -> Result<Self> {
306        // Convert to JSON, filter by schema, then re-parse with SIMD
307        // This ensures correctness while still leveraging SIMD for final parsing
308        let full_json = self.to_json_string()?;
309        let full_value: serde_json::Value = serde_json::from_str(&full_json)
310            .map_err(|e| fionn_core::DsonError::ParseError(format!("JSON parse error: {e}")))?;
311
312        let filtered_value = self.filter_json_by_schema(&full_value, schema, &mut Vec::new());
313        let filtered_json = serde_json::to_string(&filtered_value).map_err(|e| {
314            fionn_core::DsonError::SerializationError(format!("JSON serialize error: {e}"))
315        })?;
316
317        Self::parse(&filtered_json)
318    }
319
320    /// Recursively filter JSON value by schema paths
321    fn filter_json_by_schema(
322        &self,
323        value: &serde_json::Value,
324        schema: &std::collections::HashSet<String>,
325        current_path: &mut Vec<String>,
326    ) -> serde_json::Value {
327        match value {
328            serde_json::Value::Object(obj) => {
329                let mut filtered_obj = serde_json::Map::new();
330
331                for (key, val) in obj {
332                    current_path.push(key.clone());
333                    let path_str = current_path.join(".");
334
335                    // Include this field if it matches the schema (SIMD-accelerated)
336                    if self.simd_schema_match(&path_str, schema) {
337                        let filtered_val = self.filter_json_by_schema(val, schema, current_path);
338                        filtered_obj.insert(key.clone(), filtered_val);
339                    }
340
341                    current_path.pop();
342                }
343
344                serde_json::Value::Object(filtered_obj)
345            }
346            serde_json::Value::Array(arr) => {
347                let mut filtered_arr = Vec::new();
348
349                for (index, val) in arr.iter().enumerate() {
350                    // For array elements, construct path as "array[index]" format
351                    let array_path = if current_path.is_empty() {
352                        format!("[{index}]")
353                    } else {
354                        format!("{}[{}]", current_path.join("."), index)
355                    };
356
357                    // Check if this array element or any of its children should be included
358                    let should_include = schema.iter().any(|schema_path| {
359                        schema_path == &array_path ||
360                        array_path.starts_with(&format!("{schema_path}.")) ||
361                        schema_path.starts_with(&format!("{array_path}.")) ||
362                        // Also check for wildcard matches
363                        schema_path.starts_with(&format!("{}[{}].", current_path.join("."), index))
364                    });
365
366                    if should_include {
367                        // Add the array index to current path for recursive filtering
368                        current_path.push(format!("[{index}]"));
369                        let filtered_val = self.filter_json_by_schema(val, schema, current_path);
370                        filtered_arr.push(filtered_val);
371                        current_path.pop();
372                    }
373                }
374
375                serde_json::Value::Array(filtered_arr)
376            }
377            // Keep primitive values as-is
378            _ => value.clone(),
379        }
380    }
381
382    /// Find the tape index for a given JSON path using SIMD-accelerated search
383    ///
384    /// # Errors
385    /// Returns an error if the path cannot be resolved
386    pub fn resolve_path(&self, path: &str) -> Result<Option<usize>> {
387        let mut components = Vec::new();
388        fionn_core::parse_simd_ref_into(path, &mut components);
389        self.resolve_path_components_ref(&components, 0, 0)
390    }
391
392    /// Resolve a path from owned components (benchmark helper).
393    ///
394    /// # Errors
395    /// Returns an error if path resolution fails.
396    pub fn resolve_path_components_owned(
397        &self,
398        components: &[PathComponent],
399    ) -> Result<Option<usize>> {
400        self.resolve_path_components_owned_internal(components, 0, 0)
401    }
402
403    /// Resolve a pre-parsed path using a caller-provided buffer.
404    ///
405    /// # Errors
406    /// Returns an error if path resolution fails.
407    pub fn resolve_parsed_path_with_buffer<'a>(
408        &self,
409        parsed: &'a ParsedPath,
410        buffer: &mut Vec<PathComponentRef<'a>>,
411    ) -> Result<Option<usize>> {
412        parsed.components_ref(buffer);
413        self.resolve_path_components_ref(buffer, 0, 0)
414    }
415
416    /// Resolve a pre-parsed path (allocates a temporary buffer).
417    ///
418    /// # Errors
419    /// Returns an error if path resolution fails.
420    pub fn resolve_parsed_path(&self, parsed: &ParsedPath) -> Result<Option<usize>> {
421        let mut buffer = Vec::new();
422        self.resolve_parsed_path_with_buffer(parsed, &mut buffer)
423    }
424
425    /// Resolve path components starting from a given tape index
426    fn resolve_path_components_ref(
427        &self,
428        components: &[PathComponentRef<'_>],
429        start_index: usize,
430        component_index: usize,
431    ) -> Result<Option<usize>> {
432        if component_index >= components.len() {
433            return Ok(Some(start_index));
434        }
435
436        let nodes = self.nodes();
437        if start_index >= nodes.len() {
438            return Ok(None);
439        }
440
441        let component = &components[component_index];
442
443        match component {
444            PathComponentRef::Field(field_name) => {
445                // Search for field name in object starting at start_index
446                self.find_field_in_object(field_name, start_index)
447                    .map_or(Ok(None), |field_index| {
448                        // Found the field, now resolve the rest of the path from the value
449                        self.resolve_path_components_ref(
450                            components,
451                            field_index + 1,
452                            component_index + 1,
453                        )
454                    })
455            }
456            PathComponentRef::ArrayIndex(index) => {
457                // Navigate to array element at the given index
458                self.find_array_element(*index, start_index)
459                    .map_or(Ok(None), |element_index| {
460                        self.resolve_path_components_ref(
461                            components,
462                            element_index,
463                            component_index + 1,
464                        )
465                    })
466            }
467        }
468    }
469
470    /// Resolve owned components (string-backed) without re-parsing.
471    fn resolve_path_components_owned_internal(
472        &self,
473        components: &[PathComponent],
474        start_index: usize,
475        component_index: usize,
476    ) -> Result<Option<usize>> {
477        if component_index >= components.len() {
478            return Ok(Some(start_index));
479        }
480
481        let nodes = self.nodes();
482        if start_index >= nodes.len() {
483            return Ok(None);
484        }
485
486        let component = &components[component_index];
487
488        match component {
489            PathComponent::Field(field_name) => self
490                .find_field_in_object(field_name, start_index)
491                .map_or(Ok(None), |field_index| {
492                    self.resolve_path_components_owned_internal(
493                        components,
494                        field_index + 1,
495                        component_index + 1,
496                    )
497                }),
498            PathComponent::ArrayIndex(index) => self
499                .find_array_element(*index, start_index)
500                .map_or(Ok(None), |element_index| {
501                    self.resolve_path_components_owned_internal(
502                        components,
503                        element_index,
504                        component_index + 1,
505                    )
506                }),
507        }
508    }
509
510    /// Find a field with the given name in an object starting at the given index
511    fn find_field_in_object(&self, field_name: &str, start_index: usize) -> Option<usize> {
512        let nodes = self.nodes();
513        let mut index = start_index;
514
515        // SIMD-accelerated field name search using memchr-like approach
516        // Look for string nodes that match the field name
517        while index < nodes.len() {
518            if let Node::String(field_str) = &nodes[index] {
519                // SIMD-accelerated string comparison
520                if self.simd_string_equals(field_str.as_bytes(), field_name.as_bytes()) {
521                    return Some(index);
522                }
523            }
524            index += 1;
525
526            // Prevent infinite loops in case of malformed data
527            if index > start_index + 1000 {
528                break;
529            }
530        }
531
532        None
533    }
534
535    /// String equality check (uses Rust's optimized slice comparison)
536    #[inline]
537    #[must_use]
538    pub fn simd_string_equals(&self, a: &[u8], b: &[u8]) -> bool {
539        // Rust's standard library slice comparison is already SIMD-optimized
540        a == b
541    }
542
543    /// Find the nth element in an array starting at the given index
544    fn find_array_element(&self, target_index: usize, start_index: usize) -> Option<usize> {
545        let nodes = self.nodes();
546        let mut index = start_index;
547        let mut current_element = 0;
548
549        while index < nodes.len() && current_element <= target_index {
550            // Skip to next array element
551            match self.skip_value(index) {
552                Ok(new_index) => index = new_index,
553                Err(_) => return None,
554            }
555            current_element += 1;
556        }
557
558        if current_element > target_index {
559            Some(index - 1) // Return the index of the found element
560        } else {
561            None
562        }
563    }
564
565    /// Serialize the tape to JSON string, applying modifications efficiently
566    ///
567    /// # Errors
568    /// Returns an error if serialization fails
569    pub fn to_json_string(&self) -> Result<String> {
570        self.serialize_with_modifications(&FastHashMap::default(), &FastHashSet::default())
571    }
572
573    /// Serialize the tape to JSON string with modifications and deletions applied
574    ///
575    /// # Errors
576    /// Returns an error if serialization fails
577    pub fn serialize_with_modifications(
578        &self,
579        modifications: &FastHashMap<String, fionn_core::OperationValue>,
580        deletions: &FastHashSet<String>,
581    ) -> Result<String> {
582        // FAST PATH: If no modifications, return tape's native JSON directly
583        if modifications.is_empty() && deletions.is_empty() {
584            return self.serialize_tape_to_json();
585        }
586
587        // MODIFIED PATH: Apply modifications during serialization
588        self.serialize_tape_with_overlay(modifications, deletions)
589    }
590
591    /// Serialize tape to JSON.
592    ///
593    /// Note: We cannot use the original buffer directly because `simd_json`
594    /// modifies it in-place when decoding escape sequences (e.g., `\u4e16` -> `世`).
595    /// We must always re-serialize from the tape nodes to produce valid JSON.
596    fn serialize_tape_to_json(&self) -> Result<String> {
597        // Always use TapeSerializer to properly handle escape sequences
598        self.serialize_tape_with_overlay(&FastHashMap::default(), &FastHashSet::default())
599    }
600
601    /// Serialize tape with modification overlay applied using zero-copy iterator
602    fn serialize_tape_with_overlay(
603        &self,
604        modifications: &FastHashMap<String, fionn_core::OperationValue>,
605        deletions: &FastHashSet<String>,
606    ) -> Result<String> {
607        let mut serializer = TapeSerializer::new(self, modifications, deletions);
608        serializer.serialize()
609    }
610}
611
612/// Zero-copy serializer that iterates over tape and applies modifications
613struct TapeSerializer<'a> {
614    tape: &'a DsonTape,
615    modifications: &'a FastHashMap<String, fionn_core::OperationValue>,
616    deletions: &'a FastHashSet<String>,
617    output: String,
618    current_path: Vec<String>,
619}
620
621impl<'a> TapeSerializer<'a> {
622    fn new(
623        tape: &'a DsonTape,
624        modifications: &'a FastHashMap<String, fionn_core::OperationValue>,
625        deletions: &'a FastHashSet<String>,
626    ) -> Self {
627        Self {
628            tape,
629            modifications,
630            deletions,
631            output: String::with_capacity(tape.data.len() * 2),
632            current_path: Vec::new(),
633        }
634    }
635
636    fn serialize(&mut self) -> Result<String> {
637        let nodes = self.tape.nodes();
638        if !nodes.is_empty() {
639            self.serialize_node(0)?;
640        }
641        Ok(self.output.clone())
642    }
643
644    fn serialize_node(&mut self, index: usize) -> Result<usize> {
645        // Construct path correctly handling dot notation and array brackets
646        let mut path = String::with_capacity(64);
647        for (i, component) in self.current_path.iter().enumerate() {
648            if i > 0 && !component.starts_with('[') {
649                path.push('.');
650            }
651            path.push_str(component);
652        }
653
654        // Check if this path is deleted
655        if self.deletions.contains(&path) {
656            return self.tape.skip_value(index);
657        }
658
659        // Check if this path is modified
660        if let Some(value) = self.modifications.get(&path) {
661            self.serialize_operation_value(value);
662            return self.tape.skip_value(index);
663        }
664
665        let nodes = self.tape.nodes();
666        let node = &nodes[index];
667
668        match node {
669            Node::String(s) => {
670                self.output.push('"');
671                escape_json_string(s, &mut self.output);
672                self.output.push('"');
673                Ok(index + 1)
674            }
675            Node::Static(s) => {
676                match s {
677                    simd_json::StaticNode::Null => self.output.push_str("null"),
678                    simd_json::StaticNode::Bool(b) => {
679                        self.output.push_str(if *b { "true" } else { "false" });
680                    }
681                    simd_json::StaticNode::I64(n) => self.output.push_str(&n.to_string()),
682                    simd_json::StaticNode::U64(n) => self.output.push_str(&n.to_string()),
683                    simd_json::StaticNode::F64(n) => self.output.push_str(&n.to_string()),
684                }
685                Ok(index + 1)
686            }
687            Node::Object { len, .. } => {
688                self.output.push('{');
689                let mut current_idx = index + 1;
690                let mut first = true;
691                let mut seen_keys = FastHashSet::default();
692
693                for _ in 0..*len {
694                    // Get key
695                    if let Node::String(key) = &nodes[current_idx] {
696                        let key_str = key.to_string();
697                        // Track seen key
698                        seen_keys.insert(key_str.clone());
699
700                        self.current_path.push(key_str.clone());
701                        // Recalculate item path - optimized
702                        // We can't reuse `path` easily, but we can reuse logic or just append
703                        // Inside object, child always prefixed by "." unless root.
704                        // But wait, path construction is at top of function.
705                        // Here recursively calling serialize_node will rebuild path.
706                        // For checking deletion of key/value pair usage (skip key), we need path.
707
708                        let mut item_path = path.clone();
709                        if !item_path.is_empty() {
710                            item_path.push('.');
711                        }
712                        item_path.push_str(&key_str);
713
714                        // Check if item is deleted BEFORE writing key
715                        if self.deletions.contains(&item_path) {
716                            // Skip key and value
717                            current_idx += 1;
718                            current_idx = self.tape.skip_value(current_idx)?;
719                        } else {
720                            if !first {
721                                self.output.push(',');
722                            }
723                            self.output.push('"');
724                            escape_json_string(key, &mut self.output);
725                            self.output.push('"');
726                            self.output.push(':');
727
728                            // Move to value
729                            current_idx += 1;
730                            current_idx = self.serialize_node(current_idx)?;
731                            first = false;
732                        }
733                        self.current_path.pop();
734                    }
735                }
736
737                self.serialize_added_fields(&path, first, &seen_keys);
738
739                self.output.push('}');
740                Ok(current_idx)
741            }
742            Node::Array { len, .. } => {
743                self.output.push('[');
744                let mut current_idx = index + 1;
745                let mut first = true;
746
747                for i in 0..*len {
748                    let idx_str = format!("[{i}]");
749                    self.current_path.push(idx_str);
750
751                    if !first {
752                        self.output.push(',');
753                    }
754                    current_idx = self.serialize_node(current_idx)?;
755                    first = false;
756
757                    self.current_path.pop();
758                }
759
760                // Append any added elements (extensions to the array)
761                self.serialize_added_array_elements(&path, *len);
762
763                self.output.push(']');
764                Ok(current_idx)
765            }
766        }
767    }
768
769    fn serialize_operation_value(&mut self, value: &fionn_core::OperationValue) {
770        // Re-use logic to serialize OperationValue to JSON string
771        match value {
772            fionn_core::OperationValue::StringRef(s) => {
773                self.output.push('"');
774                escape_json_string(s, &mut self.output);
775                self.output.push('"');
776            }
777            fionn_core::OperationValue::NumberRef(n) => self.output.push_str(n),
778            fionn_core::OperationValue::BoolRef(b) => {
779                self.output.push_str(if *b { "true" } else { "false" });
780            }
781            fionn_core::OperationValue::Null => self.output.push_str("null"),
782            fionn_core::OperationValue::ObjectRef { .. } => {
783                // ObjectRef represents a reference to an object in the tape or constructed
784                // Since we can't easily resolve it here without access to the source tape,
785                // we output an empty object as a safe fallback.
786                self.output.push_str("{}");
787            }
788            fionn_core::OperationValue::ArrayRef { .. } => {
789                // Same as ObjectRef, safe fallback for array references
790                self.output.push_str("[]");
791            }
792        }
793    }
794
795    fn serialize_added_fields(
796        &mut self,
797        parent_path: &str,
798        mut first: bool,
799        seen_keys: &FastHashSet<String>,
800    ) {
801        // Collect all direct child keys implied by modifications that haven't been seen/serialized
802        let mut implied_keys: FastHashSet<String> = FastHashSet::default();
803
804        for (path, _) in self.modifications {
805            let relative = if parent_path.is_empty() {
806                path.as_str()
807            } else {
808                // Check if path is child of parent_path
809                if path.len() > parent_path.len()
810                    && path.starts_with(parent_path)
811                    && path.as_bytes()[parent_path.len()] == b'.'
812                {
813                    &path[parent_path.len() + 1..]
814                } else {
815                    continue;
816                }
817            };
818
819            // Extract first component (key name)
820            // Stop at '.' or '[' (array start)
821            // If implicit array index (starts with '['), we ignore for object context
822            let end = relative.find(['.', '[']).unwrap_or(relative.len());
823            if end > 0 {
824                let key = &relative[..end];
825                if !seen_keys.contains(key) {
826                    implied_keys.insert(key.to_string());
827                }
828            }
829        }
830
831        // Serialize inferred keys
832        // Sort for deterministic output if needed? Tests might verify string equality.
833        // fast hashmap iteration is random.
834        // Let's sort them.
835        let mut sorted_keys: Vec<_> = implied_keys.into_iter().collect();
836        sorted_keys.sort();
837
838        for key in sorted_keys {
839            if !first {
840                self.output.push(',');
841            }
842            self.output.push('"');
843            escape_json_string(&key, &mut self.output);
844            self.output.push('"');
845            self.output.push(':');
846
847            let full_path = if parent_path.is_empty() {
848                key.clone()
849            } else {
850                format!("{parent_path}.{key}")
851            };
852
853            if let Some(val) = self.modifications.get(&full_path) {
854                // It's a direct modification/addition with a value
855                self.serialize_operation_value(val);
856            } else {
857                // It's an intermediate path. Determine if Object or Array.
858                // Heuristic: check if any modification starts with "full_path["
859                let mut is_array = false;
860                // Optimization: scan just enough to find one array indicator
861                let prefix_bracket = format!("{full_path}[");
862                for p in self.modifications.keys() {
863                    if p.starts_with(&prefix_bracket) {
864                        is_array = true;
865                        break;
866                    }
867                }
868
869                if is_array {
870                    self.output.push('[');
871                    self.serialize_added_array_elements(&full_path, 0);
872                    self.output.push(']');
873                } else {
874                    self.output.push('{');
875                    let empty_seen = FastHashSet::default();
876                    self.serialize_added_fields(&full_path, true, &empty_seen);
877                    self.output.push('}');
878                }
879            }
880            first = false;
881        }
882    }
883
884    fn serialize_added_array_elements(&mut self, parent_path: &str, start_index: usize) {
885        // 1. Find max index
886        let mut max_index: Option<usize> = None;
887        let prefix = format!("{parent_path}[");
888
889        for path in self.modifications.keys() {
890            if path.starts_with(&prefix) {
891                // Extract index: parent[123]...
892                // We want the part between [ and ]
893                if let Some(end_bracket) = path[prefix.len()..].find(']') {
894                    let index_str = &path[prefix.len()..prefix.len() + end_bracket];
895                    if let Ok(idx) = index_str.parse::<usize>() {
896                        max_index = Some(max_index.map_or(idx, |m| m.max(idx)));
897                    }
898                }
899            }
900        }
901
902        if let Some(max) = max_index
903            && max >= start_index
904        {
905            // Determine if we need a comma before the first added element
906            // If start_index > 0, we are appending to an existing array (which had elements 0..start_index-1)
907            // BUT, the comma is written only if there was a previous element.
908            // In serialize_node, we write comma before elements.
909            // Here, if start_index > 0, we definitely need a comma before start_index (assuming array wasn't empty)
910            // Actually serialize_node logic writes comma *between* elements.
911            // If we are appending, we need a comma before the first new element IF the array wasn't empty.
912            // The caller should handle the initial comma state? Or we handle it here.
913            // Ideally, `serialize_node` writes the last element, then calls us.
914            // So we need to write `,` before the first element we write.
915
916            for i in start_index..=max {
917                // Always write comma because we are either:
918                // 1. Appending to non-empty array (start_index > 0) -> Need comma.
919                // 2. Creating new array (start_index=0).
920                //    If i=0, NO comma. If i>0, comma.
921
922                if i > 0 {
923                    self.output.push(',');
924                }
925
926                let idx_path = format!("{parent_path}[{i}]");
927
928                if let Some(val) = self.modifications.get(&idx_path) {
929                    self.serialize_operation_value(val);
930                } else {
931                    // Check if it's an intermediate object/array at this index
932                    // ... (same logic as before)
933                    let mut is_intermediate = false;
934                    let dot_prefix = format!("{idx_path}.");
935                    let bracket_prefix = format!("{idx_path}[");
936
937                    for p in self.modifications.keys() {
938                        if p.starts_with(&dot_prefix) || p.starts_with(&bracket_prefix) {
939                            is_intermediate = true;
940                            break;
941                        }
942                    }
943
944                    if is_intermediate {
945                        let mut is_array_child = false;
946                        for p in self.modifications.keys() {
947                            if p.starts_with(&bracket_prefix) {
948                                is_array_child = true;
949                                break;
950                            }
951                        }
952
953                        if is_array_child {
954                            self.output.push('[');
955                            self.serialize_added_array_elements(&idx_path, 0);
956                            self.output.push(']');
957                        } else {
958                            self.output.push('{');
959                            let empty_seen = FastHashSet::default();
960                            self.serialize_added_fields(&idx_path, true, &empty_seen);
961                            self.output.push('}');
962                        }
963                    } else {
964                        // Sparse array or missing element -> null
965                        self.output.push_str("null");
966                    }
967                }
968            }
969        }
970    }
971}
972
973impl DsonTape {
974    /// Parse a JSON path into components, handling array notation (SIMD-accelerated)
975    #[inline]
976    #[must_use]
977    pub fn parse_path(path: &str) -> Vec<PathComponent> {
978        fionn_core::parse_simd(path)
979    }
980
981    /// Reconstruct a value from tape positions with full tape access.
982    ///
983    /// # Errors
984    /// Returns an error if the tape node cannot be converted to JSON.
985    pub fn reconstruct_value_from_tape(
986        &self,
987        start: usize,
988        _end: usize,
989    ) -> Result<serde_json::Value> {
990        let nodes = self.nodes();
991        if start >= nodes.len() {
992            return Ok(serde_json::Value::Null);
993        }
994
995        self.node_to_json_value(start)
996    }
997
998    /// Convert a tape node at given index to JSON value, recursively
999    fn node_to_json_value(&self, index: usize) -> Result<serde_json::Value> {
1000        let nodes = self.nodes();
1001        if index >= nodes.len() {
1002            return Ok(serde_json::Value::Null);
1003        }
1004
1005        let node = &nodes[index];
1006
1007        match node {
1008            Node::String(s) => Ok(serde_json::Value::String(s.to_string())),
1009            Node::Static(static_val) => match static_val {
1010                simd_json::StaticNode::Null => Ok(serde_json::Value::Null),
1011                simd_json::StaticNode::Bool(b) => Ok(serde_json::Value::Bool(*b)),
1012                simd_json::StaticNode::I64(n) => Ok(serde_json::Value::Number((*n).into())),
1013                simd_json::StaticNode::U64(n) => Ok(serde_json::Value::Number((*n).into())),
1014                simd_json::StaticNode::F64(n) => Ok(serde_json::Number::from_f64(*n).map_or_else(
1015                    || serde_json::Value::String(n.to_string()),
1016                    serde_json::Value::Number,
1017                )),
1018            },
1019            Node::Object { len, count: _ } => {
1020                let mut obj = serde_json::Map::new();
1021                let mut current_idx = index + 1;
1022
1023                for _ in 0..*len {
1024                    if current_idx >= nodes.len() {
1025                        break;
1026                    }
1027
1028                    // Get field name
1029                    if let Node::String(field_name) = &nodes[current_idx] {
1030                        let key = field_name.to_string();
1031                        current_idx += 1;
1032
1033                        // Get field value
1034                        if current_idx < nodes.len() {
1035                            let value = self.node_to_json_value(current_idx)?;
1036                            current_idx = self.skip_value(current_idx)?;
1037                            obj.insert(key, value);
1038                        }
1039                    } else {
1040                        current_idx += 1;
1041                    }
1042                }
1043
1044                Ok(serde_json::Value::Object(obj))
1045            }
1046            Node::Array { len, count: _ } => {
1047                let mut arr = Vec::with_capacity(*len);
1048                let mut current_idx = index + 1;
1049
1050                for _ in 0..*len {
1051                    if current_idx >= nodes.len() {
1052                        break;
1053                    }
1054
1055                    let value = self.node_to_json_value(current_idx)?;
1056                    current_idx = self.skip_value(current_idx)?;
1057                    arr.push(value);
1058                }
1059
1060                Ok(serde_json::Value::Array(arr))
1061            }
1062        }
1063    }
1064}
1065
1066#[cfg(test)]
1067mod tests {
1068    use super::*;
1069
1070    #[test]
1071    fn test_dson_tape_parse_simple() {
1072        let tape = DsonTape::parse(r#"{"name":"test"}"#);
1073        assert!(tape.is_ok());
1074    }
1075
1076    #[test]
1077    fn test_dson_tape_parse_array() {
1078        let tape = DsonTape::parse(r"[1, 2, 3]");
1079        assert!(tape.is_ok());
1080    }
1081
1082    #[test]
1083    fn test_dson_tape_parse_nested() {
1084        let tape = DsonTape::parse(r#"{"user":{"name":"test","age":30}}"#);
1085        assert!(tape.is_ok());
1086    }
1087
1088    #[test]
1089    fn test_dson_tape_parse_invalid() {
1090        let tape = DsonTape::parse("not valid json");
1091        assert!(tape.is_err());
1092    }
1093
1094    #[test]
1095    fn test_dson_tape_nodes() {
1096        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1097        assert!(!tape.nodes().is_empty());
1098    }
1099
1100    #[test]
1101    fn test_dson_tape_root() {
1102        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1103        let _root = tape.root();
1104        // Should not panic
1105    }
1106
1107    #[test]
1108    fn test_dson_tape_tape() {
1109        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1110        let _inner = tape.tape();
1111        // Should not panic
1112    }
1113
1114    #[test]
1115    fn test_dson_tape_skip_field() {
1116        let tape = DsonTape::parse(r#"{"name":"test","age":30}"#).unwrap();
1117        let result = tape.skip_field(0);
1118        assert!(result.is_ok());
1119    }
1120
1121    #[test]
1122    fn test_dson_tape_skip_value() {
1123        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1124        let result = tape.skip_value(0);
1125        assert!(result.is_ok());
1126    }
1127
1128    #[test]
1129    fn test_dson_tape_resolve_path_field() {
1130        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1131        let result = tape.resolve_path("name");
1132        assert!(result.is_ok());
1133    }
1134
1135    #[test]
1136    fn test_dson_tape_resolve_path_nested() {
1137        let tape = DsonTape::parse(r#"{"user":{"name":"test"}}"#).unwrap();
1138        let result = tape.resolve_path("user.name");
1139        assert!(result.is_ok());
1140    }
1141
1142    #[test]
1143    fn test_dson_tape_resolve_path_array() {
1144        let tape = DsonTape::parse(r#"{"items":[1,2,3]}"#).unwrap();
1145        let result = tape.resolve_path("items[0]");
1146        assert!(result.is_ok());
1147    }
1148
1149    #[test]
1150    fn test_dson_tape_resolve_path_not_found() {
1151        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1152        let result = tape.resolve_path("nonexistent");
1153        assert!(result.is_ok());
1154        assert!(result.unwrap().is_none());
1155    }
1156
1157    #[test]
1158    fn test_path_component_debug() {
1159        let field = PathComponent::Field("test".to_string());
1160        let debug = format!("{field:?}");
1161        assert!(debug.contains("Field"));
1162    }
1163
1164    #[test]
1165    fn test_path_component_clone() {
1166        let field = PathComponent::Field("test".to_string());
1167        let cloned = field;
1168        assert!(matches!(cloned, PathComponent::Field(_)));
1169    }
1170
1171    #[test]
1172    fn test_simd_value_debug() {
1173        let val = SimdValue::String("test".to_string());
1174        let debug = format!("{val:?}");
1175        assert!(debug.contains("String"));
1176    }
1177
1178    #[test]
1179    fn test_simd_value_clone() {
1180        let val = SimdValue::Null;
1181        let cloned = val;
1182        assert!(matches!(cloned, SimdValue::Null));
1183    }
1184
1185    #[test]
1186    fn test_parse_path_simple() {
1187        let components = DsonTape::parse_path("name");
1188        assert_eq!(components.len(), 1);
1189        assert!(matches!(&components[0], PathComponent::Field(f) if f == "name"));
1190    }
1191
1192    #[test]
1193    fn test_parse_path_nested() {
1194        let components = DsonTape::parse_path("user.name");
1195        assert_eq!(components.len(), 2);
1196    }
1197
1198    #[test]
1199    fn test_parse_path_array() {
1200        let components = DsonTape::parse_path("items[0]");
1201        assert_eq!(components.len(), 2);
1202        assert!(matches!(&components[1], PathComponent::ArrayIndex(0)));
1203    }
1204
1205    #[test]
1206    fn test_node_to_json_value_string() {
1207        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1208        // Get index of the string value
1209        let result = tape.node_to_json_value(2);
1210        assert!(result.is_ok());
1211    }
1212
1213    #[test]
1214    fn test_node_to_json_value_object() {
1215        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1216        let result = tape.node_to_json_value(0);
1217        assert!(result.is_ok());
1218        let val = result.unwrap();
1219        assert!(val.is_object());
1220    }
1221
1222    #[test]
1223    fn test_node_to_json_value_array() {
1224        let tape = DsonTape::parse(r"[1, 2, 3]").unwrap();
1225        let result = tape.node_to_json_value(0);
1226        assert!(result.is_ok());
1227        let val = result.unwrap();
1228        assert!(val.is_array());
1229    }
1230
1231    #[test]
1232    fn test_node_to_json_value_number() {
1233        let tape = DsonTape::parse(r#"{"value":42}"#).unwrap();
1234        let result = tape.node_to_json_value(2);
1235        assert!(result.is_ok());
1236    }
1237
1238    #[test]
1239    fn test_node_to_json_value_bool() {
1240        let tape = DsonTape::parse(r#"{"flag":true}"#).unwrap();
1241        let result = tape.node_to_json_value(2);
1242        assert!(result.is_ok());
1243    }
1244
1245    #[test]
1246    fn test_node_to_json_value_null() {
1247        let tape = DsonTape::parse(r#"{"value":null}"#).unwrap();
1248        let result = tape.node_to_json_value(2);
1249        assert!(result.is_ok());
1250    }
1251
1252    #[test]
1253    fn test_skip_value_out_of_bounds() {
1254        let tape = DsonTape::parse(r"{}").unwrap();
1255        let result = tape.skip_value(100);
1256        assert!(result.is_ok());
1257    }
1258
1259    #[test]
1260    fn test_simd_value_variants() {
1261        let s = SimdValue::String("test".to_string());
1262        let n = SimdValue::Number("42".to_string());
1263        let b = SimdValue::Bool(true);
1264        let null = SimdValue::Null;
1265
1266        assert!(matches!(s, SimdValue::String(_)));
1267        assert!(matches!(n, SimdValue::Number(_)));
1268        assert!(matches!(b, SimdValue::Bool(true)));
1269        assert!(matches!(null, SimdValue::Null));
1270    }
1271
1272    #[test]
1273    fn test_to_json_string() {
1274        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1275        let result = tape.to_json_string();
1276        assert!(result.is_ok());
1277        let json = result.unwrap();
1278        assert!(json.contains("name"));
1279        assert!(json.contains("test"));
1280    }
1281
1282    #[test]
1283    fn test_filter_by_schema() {
1284        let tape = DsonTape::parse(r#"{"name":"test","age":30}"#).unwrap();
1285        let schema = std::collections::HashSet::from(["name".to_string()]);
1286        let result = tape.filter_by_schema(&schema);
1287        assert!(result.is_ok());
1288    }
1289
1290    #[test]
1291    fn test_filter_by_schema_empty() {
1292        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1293        let schema = std::collections::HashSet::new();
1294        let result = tape.filter_by_schema(&schema);
1295        assert!(result.is_ok());
1296    }
1297
1298    #[test]
1299    fn test_extract_value_simd_string() {
1300        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1301        let value = tape.extract_value_simd(2);
1302        assert!(value.is_some());
1303    }
1304
1305    #[test]
1306    fn test_extract_value_simd_out_of_bounds() {
1307        let tape = DsonTape::parse(r"{}").unwrap();
1308        let value = tape.extract_value_simd(100);
1309        assert!(value.is_none());
1310    }
1311
1312    #[test]
1313    fn test_serialize_simd() {
1314        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1315        let modifications = std::collections::HashMap::new();
1316        let result = tape.serialize_simd(&modifications);
1317        // Just check it doesn't panic
1318        let _ = result;
1319    }
1320
1321    #[test]
1322    fn test_read_field() {
1323        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1324        let result = tape.read_field(0);
1325        assert!(result.is_ok());
1326    }
1327
1328    #[test]
1329    fn test_read_field_out_of_bounds() {
1330        let tape = DsonTape::parse(r"{}").unwrap();
1331        let result = tape.read_field(100);
1332        assert!(result.is_err());
1333    }
1334
1335    #[test]
1336    fn test_should_survive_exact_match() {
1337        let tape = DsonTape::parse(r"{}").unwrap();
1338        let path = "user.name";
1339        let schema = std::collections::HashSet::from(["user.name".to_string()]);
1340        let result = tape.should_survive(path, &schema);
1341        assert!(result);
1342    }
1343
1344    #[test]
1345    fn test_should_survive_prefix_match() {
1346        let tape = DsonTape::parse(r"{}").unwrap();
1347        let path = "user.name";
1348        let schema = std::collections::HashSet::from(["user".to_string()]);
1349        let result = tape.should_survive(path, &schema);
1350        assert!(result);
1351    }
1352
1353    #[test]
1354    fn test_should_survive_no_match() {
1355        let tape = DsonTape::parse(r"{}").unwrap();
1356        let path = "other.field";
1357        let schema = std::collections::HashSet::from(["user".to_string()]);
1358        let result = tape.should_survive(path, &schema);
1359        assert!(!result);
1360    }
1361
1362    #[test]
1363    fn test_simd_schema_match() {
1364        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1365        let schema = std::collections::HashSet::from(["name".to_string()]);
1366        let result = tape.simd_schema_match("name", &schema);
1367        // Just verify it doesn't panic
1368        assert!(result);
1369    }
1370
1371    #[test]
1372    fn test_simd_schema_match_empty_schema() {
1373        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1374        let schema = std::collections::HashSet::new();
1375        let result = tape.simd_schema_match("name", &schema);
1376        assert!(!result);
1377    }
1378
1379    #[test]
1380    fn test_serialize_with_modifications() {
1381        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1382        let mut modifications = ahash::AHashMap::default();
1383        modifications.insert(
1384            "name".to_string(),
1385            fionn_core::OperationValue::StringRef("modified".to_string()),
1386        );
1387        let deletions = ahash::AHashSet::default();
1388        let result = tape.serialize_with_modifications(&modifications, &deletions);
1389        assert!(result.is_ok());
1390    }
1391
1392    #[test]
1393    fn test_serialize_with_deletions() {
1394        let tape = DsonTape::parse(r#"{"name":"test","age":30}"#).unwrap();
1395        let modifications = ahash::AHashMap::default();
1396        let mut deletions = ahash::AHashSet::default();
1397        deletions.insert("age".to_string());
1398        let result = tape.serialize_with_modifications(&modifications, &deletions);
1399        assert!(result.is_ok());
1400    }
1401
1402    #[test]
1403    fn test_simd_string_equals() {
1404        let tape = DsonTape::parse(r"{}").unwrap();
1405        assert!(tape.simd_string_equals(b"hello", b"hello"));
1406        assert!(!tape.simd_string_equals(b"hello", b"world"));
1407    }
1408
1409    #[test]
1410    fn test_simd_string_equals_different_lengths() {
1411        let tape = DsonTape::parse(r"{}").unwrap();
1412        assert!(!tape.simd_string_equals(b"hello", b"hi"));
1413    }
1414
1415    #[test]
1416    fn test_simd_string_equals_long_strings() {
1417        let tape = DsonTape::parse(r"{}").unwrap();
1418        let a = b"this is a long string for testing simd comparison";
1419        let b = b"this is a long string for testing simd comparison";
1420        assert!(tape.simd_string_equals(a, b));
1421    }
1422
1423    #[test]
1424    fn test_resolve_path_empty() {
1425        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1426        let result = tape.resolve_path("");
1427        assert!(result.is_ok());
1428    }
1429
1430    #[test]
1431    fn test_resolve_path_deep_nesting() {
1432        let tape = DsonTape::parse(r#"{"a":{"b":{"c":{"d":1}}}}"#).unwrap();
1433        let result = tape.resolve_path("a.b.c.d");
1434        assert!(result.is_ok());
1435    }
1436
1437    #[test]
1438    fn test_resolve_path_array_out_of_bounds() {
1439        let tape = DsonTape::parse(r#"{"items":[1,2,3]}"#).unwrap();
1440        let result = tape.resolve_path("items[100]");
1441        assert!(result.is_ok());
1442        // Index out of bounds returns None
1443        assert!(result.unwrap().is_none());
1444    }
1445
1446    #[test]
1447    fn test_resolve_parsed_path() {
1448        let tape = DsonTape::parse(r#"{"user":{"name":"test"}}"#).unwrap();
1449        let parsed = fionn_core::ParsedPath::parse("user.name");
1450        let result = tape.resolve_parsed_path(&parsed);
1451        assert!(result.is_ok());
1452    }
1453
1454    #[test]
1455    fn test_node_to_json_value_nested_object() {
1456        let tape = DsonTape::parse(r#"{"user":{"name":"test","age":30}}"#).unwrap();
1457        let result = tape.node_to_json_value(0);
1458        assert!(result.is_ok());
1459        let val = result.unwrap();
1460        assert!(val.is_object());
1461    }
1462
1463    #[test]
1464    fn test_node_to_json_value_nested_array() {
1465        let tape = DsonTape::parse(r"[[1,2],[3,4]]").unwrap();
1466        let result = tape.node_to_json_value(0);
1467        assert!(result.is_ok());
1468        let val = result.unwrap();
1469        assert!(val.is_array());
1470    }
1471
1472    #[test]
1473    fn test_parse_path_empty() {
1474        let components = DsonTape::parse_path("");
1475        assert!(components.is_empty());
1476    }
1477
1478    #[test]
1479    fn test_parse_path_complex() {
1480        let components = DsonTape::parse_path("users[0].name.first");
1481        assert_eq!(components.len(), 4);
1482    }
1483
1484    #[test]
1485    fn test_reconstruct_value_from_tape() {
1486        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1487        let result = tape.reconstruct_value_from_tape(0, 10);
1488        assert!(result.is_ok());
1489    }
1490
1491    #[test]
1492    fn test_reconstruct_value_from_tape_array() {
1493        let tape = DsonTape::parse(r"[1,2,3]").unwrap();
1494        let result = tape.reconstruct_value_from_tape(0, 10);
1495        assert!(result.is_ok());
1496    }
1497
1498    #[test]
1499    fn test_path_component_array_index() {
1500        let idx = PathComponent::ArrayIndex(5);
1501        let debug = format!("{idx:?}");
1502        assert!(debug.contains("ArrayIndex"));
1503    }
1504
1505    #[test]
1506    fn test_skip_field_not_object() {
1507        let tape = DsonTape::parse(r"[1,2,3]").unwrap();
1508        let result = tape.skip_field(0);
1509        assert!(result.is_ok());
1510    }
1511
1512    #[test]
1513    fn test_skip_value_array() {
1514        let tape = DsonTape::parse(r"[1,2,3]").unwrap();
1515        let result = tape.skip_value(0);
1516        assert!(result.is_ok());
1517        assert!(result.unwrap() > 0);
1518    }
1519
1520    #[test]
1521    fn test_skip_value_string() {
1522        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1523        let result = tape.skip_value(2);
1524        assert!(result.is_ok());
1525    }
1526
1527    #[test]
1528    fn test_skip_value_static() {
1529        let tape = DsonTape::parse(r#"{"val":true}"#).unwrap();
1530        let result = tape.skip_value(2);
1531        assert!(result.is_ok());
1532    }
1533
1534    #[test]
1535    fn test_resolve_path_components_owned() {
1536        let tape = DsonTape::parse(r#"{"user":{"name":"test"}}"#).unwrap();
1537        let components = vec![
1538            PathComponent::Field("user".to_string()),
1539            PathComponent::Field("name".to_string()),
1540        ];
1541        let result = tape.resolve_path_components_owned(&components);
1542        assert!(result.is_ok());
1543    }
1544
1545    #[test]
1546    fn test_extract_value_simd_number() {
1547        let tape = DsonTape::parse(r#"{"val":42}"#).unwrap();
1548        let value = tape.extract_value_simd(2);
1549        assert!(value.is_some());
1550        assert!(matches!(value.unwrap(), SimdValue::Number(_)));
1551    }
1552
1553    #[test]
1554    fn test_extract_value_simd_bool() {
1555        let tape = DsonTape::parse(r#"{"val":true}"#).unwrap();
1556        let value = tape.extract_value_simd(2);
1557        assert!(value.is_some());
1558        assert!(matches!(value.unwrap(), SimdValue::Bool(true)));
1559    }
1560
1561    #[test]
1562    fn test_extract_value_simd_null() {
1563        let tape = DsonTape::parse(r#"{"val":null}"#).unwrap();
1564        let value = tape.extract_value_simd(2);
1565        assert!(value.is_some());
1566        assert!(matches!(value.unwrap(), SimdValue::Null));
1567    }
1568
1569    #[test]
1570    fn test_filter_by_schema_with_array() {
1571        let tape = DsonTape::parse(r#"{"items":[1,2,3],"name":"test"}"#).unwrap();
1572        let schema = std::collections::HashSet::from(["items".to_string()]);
1573        let result = tape.filter_by_schema(&schema);
1574        assert!(result.is_ok());
1575    }
1576
1577    #[test]
1578    fn test_filter_by_schema_nested_array() {
1579        let tape = DsonTape::parse(r#"{"data":{"items":[{"id":1},{"id":2}]}}"#).unwrap();
1580        let schema =
1581            std::collections::HashSet::from(["data".to_string(), "data.items".to_string()]);
1582        let result = tape.filter_by_schema(&schema);
1583        assert!(result.is_ok());
1584    }
1585
1586    #[test]
1587    fn test_resolve_path_with_array_index() {
1588        let tape = DsonTape::parse(r#"{"items":["a","b","c"]}"#).unwrap();
1589        let components = vec![
1590            PathComponent::Field("items".to_string()),
1591            PathComponent::ArrayIndex(1),
1592        ];
1593        let result = tape.resolve_path_components_owned(&components);
1594        assert!(result.is_ok());
1595    }
1596
1597    #[test]
1598    fn test_resolve_path_nested_array() {
1599        let tape = DsonTape::parse(r#"{"data":[{"name":"first"},{"name":"second"}]}"#).unwrap();
1600        let components = vec![
1601            PathComponent::Field("data".to_string()),
1602            PathComponent::ArrayIndex(0),
1603            PathComponent::Field("name".to_string()),
1604        ];
1605        let result = tape.resolve_path_components_owned(&components);
1606        assert!(result.is_ok());
1607    }
1608
1609    #[test]
1610    fn test_resolve_path_missing_field() {
1611        let tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1612        let components = vec![PathComponent::Field("nonexistent".to_string())];
1613        let result = tape.resolve_path_components_owned(&components);
1614        assert!(result.is_ok());
1615        assert!(result.unwrap().is_none());
1616    }
1617
1618    #[test]
1619    fn test_resolve_path_missing_array_index() {
1620        let tape = DsonTape::parse(r#"{"items":["a","b"]}"#).unwrap();
1621        let components = vec![
1622            PathComponent::Field("items".to_string()),
1623            PathComponent::ArrayIndex(99),
1624        ];
1625        let result = tape.resolve_path_components_owned(&components);
1626        assert!(result.is_ok());
1627        // Should return None for out-of-bounds index
1628    }
1629
1630    #[test]
1631    fn test_serialize_with_modifications_nested() {
1632        let tape = DsonTape::parse(r#"{"user":{"name":"Alice"}}"#).unwrap();
1633        let mut modifications = FastHashMap::default();
1634        modifications.insert(
1635            "user.email".to_string(),
1636            fionn_core::OperationValue::StringRef("alice@example.com".to_string()),
1637        );
1638        let deletions = FastHashSet::default();
1639        let result = tape.serialize_with_modifications(&modifications, &deletions);
1640        assert!(result.is_ok());
1641    }
1642
1643    #[test]
1644    fn test_serialize_with_deletions_extended() {
1645        let tape = DsonTape::parse(r#"{"name":"Alice","age":30}"#).unwrap();
1646        let modifications = FastHashMap::default();
1647        let mut deletions = FastHashSet::default();
1648        deletions.insert("age".to_string());
1649        let result = tape.serialize_with_modifications(&modifications, &deletions);
1650        assert!(result.is_ok());
1651        let output = result.unwrap();
1652        assert!(!output.contains("\"age\""));
1653    }
1654
1655    #[test]
1656    fn test_serialize_with_array_modifications() {
1657        let tape = DsonTape::parse(r#"{"items":["a","b","c"]}"#).unwrap();
1658        let mut modifications = FastHashMap::default();
1659        modifications.insert(
1660            "items[1]".to_string(),
1661            fionn_core::OperationValue::StringRef("modified".to_string()),
1662        );
1663        let deletions = FastHashSet::default();
1664        let result = tape.serialize_with_modifications(&modifications, &deletions);
1665        assert!(result.is_ok());
1666    }
1667
1668    #[test]
1669    fn test_reconstruct_value_from_tape_extended() {
1670        let tape = DsonTape::parse(r#"{"name":"test","age":42}"#).unwrap();
1671        let node_count = tape.nodes().len();
1672        let result = tape.reconstruct_value_from_tape(0, node_count);
1673        assert!(result.is_ok());
1674        let value = result.unwrap();
1675        assert!(value.is_object());
1676    }
1677
1678    #[test]
1679    fn test_reconstruct_value_from_tape_array_extended() {
1680        let tape = DsonTape::parse(r"[1,2,3]").unwrap();
1681        let node_count = tape.nodes().len();
1682        let result = tape.reconstruct_value_from_tape(0, node_count);
1683        assert!(result.is_ok());
1684        let value = result.unwrap();
1685        assert!(value.is_array());
1686    }
1687
1688    #[test]
1689    fn test_field_matches_schema_prefix() {
1690        let tape = DsonTape::parse(r#"{"user":{"name":"test"}}"#).unwrap();
1691        let schema = std::collections::HashSet::from(["user".to_string()]);
1692        // Test that "user.name" matches when "user" is in schema
1693        let result = tape.filter_by_schema(&schema);
1694        assert!(result.is_ok());
1695    }
1696
1697    #[test]
1698    fn test_field_matches_schema_wildcard() {
1699        let tape = DsonTape::parse(r#"{"users":{"alice":{"id":1},"bob":{"id":2}}}"#).unwrap();
1700        let schema = std::collections::HashSet::from(["users.**".to_string()]);
1701        let result = tape.filter_by_schema(&schema);
1702        assert!(result.is_ok());
1703    }
1704
1705    #[test]
1706    fn test_empty_tape_serialization() {
1707        let tape = DsonTape::parse(r"{}").unwrap();
1708        let modifications = FastHashMap::default();
1709        let deletions = FastHashSet::default();
1710        let result = tape.serialize_with_modifications(&modifications, &deletions);
1711        assert!(result.is_ok());
1712        assert_eq!(result.unwrap(), "{}");
1713    }
1714
1715    #[test]
1716    fn test_empty_array_serialization() {
1717        let tape = DsonTape::parse(r"[]").unwrap();
1718        let modifications = FastHashMap::default();
1719        let deletions = FastHashSet::default();
1720        let result = tape.serialize_with_modifications(&modifications, &deletions);
1721        assert!(result.is_ok());
1722        assert_eq!(result.unwrap(), "[]");
1723    }
1724
1725    #[test]
1726    fn test_extract_value_simd_i64() {
1727        let tape = DsonTape::parse(r#"{"val":9223372036854775807}"#).unwrap();
1728        let value = tape.extract_value_simd(2);
1729        // Large i64 value
1730        assert!(value.is_some());
1731    }
1732
1733    #[test]
1734    fn test_extract_value_simd_f64() {
1735        let tape = DsonTape::parse(r#"{"val":3.14159}"#).unwrap();
1736        let value = tape.extract_value_simd(2);
1737        assert!(value.is_some());
1738        if let Some(SimdValue::Number(n)) = value {
1739            assert!(n.contains("3.14"));
1740        }
1741    }
1742
1743    #[test]
1744    fn test_filter_by_schema_empty_result() {
1745        let tape = DsonTape::parse(r#"{"name":"test","age":30}"#).unwrap();
1746        let schema = std::collections::HashSet::from(["nonexistent".to_string()]);
1747        let result = tape.filter_by_schema(&schema);
1748        assert!(result.is_ok());
1749    }
1750
1751    #[test]
1752    fn test_filter_by_schema_all_fields() {
1753        let tape = DsonTape::parse(r#"{"name":"test","age":30}"#).unwrap();
1754        let schema = std::collections::HashSet::from(["name".to_string(), "age".to_string()]);
1755        let result = tape.filter_by_schema(&schema);
1756        assert!(result.is_ok());
1757    }
1758
1759    #[test]
1760    fn test_get_field_value_missing() {
1761        let _tape = DsonTape::parse(r#"{"name":"test"}"#).unwrap();
1762        // Test accessing a field that doesn't exist via path
1763        let components = DsonTape::parse_path("missing");
1764        assert_eq!(components.len(), 1);
1765    }
1766
1767    #[test]
1768    fn test_get_nested_field_path() {
1769        let tape = DsonTape::parse(r#"{"user":{"name":"test"}}"#).unwrap();
1770        let components = DsonTape::parse_path("user.name");
1771        assert_eq!(components.len(), 2);
1772        let _ = tape;
1773    }
1774
1775    #[test]
1776    fn test_skip_value_nested_object() {
1777        let tape = DsonTape::parse(r#"{"outer":{"inner":{"deep":"value"}}}"#).unwrap();
1778        let result = tape.skip_value(0);
1779        assert!(result.is_ok());
1780    }
1781
1782    #[test]
1783    fn test_skip_value_nested_array() {
1784        let tape = DsonTape::parse(r"[[1,2],[3,4],[5,6]]").unwrap();
1785        let result = tape.skip_value(0);
1786        assert!(result.is_ok());
1787    }
1788
1789    #[test]
1790    fn test_serialize_with_modifications_add_new_field() {
1791        let tape = DsonTape::parse(r#"{"existing":"value"}"#).unwrap();
1792        let mut modifications = FastHashMap::default();
1793        modifications.insert(
1794            "new_field".to_string(),
1795            fionn_core::OperationValue::StringRef("new_value".to_string()),
1796        );
1797        let deletions = FastHashSet::default();
1798        let result = tape.serialize_with_modifications(&modifications, &deletions);
1799        assert!(result.is_ok());
1800        let output = result.unwrap();
1801        assert!(output.contains("new_field") || output.contains("existing"));
1802    }
1803
1804    #[test]
1805    fn test_serialize_with_modifications_number() {
1806        let tape = DsonTape::parse(r#"{"value":42}"#).unwrap();
1807        let mut modifications = FastHashMap::default();
1808        modifications.insert(
1809            "value".to_string(),
1810            fionn_core::OperationValue::NumberRef("100".to_string()),
1811        );
1812        let deletions = FastHashSet::default();
1813        let result = tape.serialize_with_modifications(&modifications, &deletions);
1814        assert!(result.is_ok());
1815    }
1816
1817    #[test]
1818    fn test_serialize_with_modifications_bool() {
1819        let tape = DsonTape::parse(r#"{"flag":true}"#).unwrap();
1820        let mut modifications = FastHashMap::default();
1821        modifications.insert(
1822            "flag".to_string(),
1823            fionn_core::OperationValue::BoolRef(false),
1824        );
1825        let deletions = FastHashSet::default();
1826        let result = tape.serialize_with_modifications(&modifications, &deletions);
1827        assert!(result.is_ok());
1828    }
1829
1830    #[test]
1831    fn test_serialize_with_modifications_null() {
1832        let tape = DsonTape::parse(r#"{"value":"test"}"#).unwrap();
1833        let mut modifications = FastHashMap::default();
1834        modifications.insert("value".to_string(), fionn_core::OperationValue::Null);
1835        let deletions = FastHashSet::default();
1836        let result = tape.serialize_with_modifications(&modifications, &deletions);
1837        assert!(result.is_ok());
1838    }
1839
1840    #[test]
1841    fn test_path_component_field() {
1842        let component = PathComponent::Field("test".to_string());
1843        match component {
1844            PathComponent::Field(name) => assert_eq!(name, "test"),
1845            PathComponent::ArrayIndex(_) => panic!("Expected Field"),
1846        }
1847    }
1848
1849    #[test]
1850    fn test_path_component_array_index_match() {
1851        let component = PathComponent::ArrayIndex(5);
1852        match component {
1853            PathComponent::ArrayIndex(idx) => assert_eq!(idx, 5),
1854            PathComponent::Field(_) => panic!("Expected ArrayIndex"),
1855        }
1856    }
1857
1858    #[test]
1859    fn test_parse_complex_path() {
1860        let components = DsonTape::parse_path("users[0].profile.name");
1861        assert_eq!(components.len(), 4);
1862    }
1863
1864    #[test]
1865    fn test_to_json_string_array() {
1866        let tape = DsonTape::parse(r#"[1,"two",true,null]"#).unwrap();
1867        let result = tape.to_json_string();
1868        assert!(result.is_ok());
1869        let output = result.unwrap();
1870        assert!(output.contains('1'));
1871        assert!(output.contains("two"));
1872    }
1873
1874    #[test]
1875    fn test_to_json_string_nested() {
1876        let tape = DsonTape::parse(r#"{"a":{"b":{"c":1}}}"#).unwrap();
1877        let result = tape.to_json_string();
1878        assert!(result.is_ok());
1879    }
1880
1881    #[test]
1882    fn test_simd_value_string() {
1883        let value = SimdValue::String("test".to_string());
1884        match value {
1885            SimdValue::String(s) => assert_eq!(s, "test"),
1886            _ => panic!("Expected String"),
1887        }
1888    }
1889
1890    #[test]
1891    fn test_simd_value_number_display() {
1892        let value = SimdValue::Number("42".to_string());
1893        let debug_str = format!("{value:?}");
1894        assert!(debug_str.contains("42"));
1895    }
1896
1897    #[test]
1898    fn test_serialize_with_deep_nesting() {
1899        let tape = DsonTape::parse(r#"{"a":{"b":{"c":{"d":{"e":1}}}}}"#).unwrap();
1900        let modifications = FastHashMap::default();
1901        let deletions = FastHashSet::default();
1902        let result = tape.serialize_with_modifications(&modifications, &deletions);
1903        assert!(result.is_ok());
1904    }
1905
1906    #[test]
1907    fn test_serialize_with_array_of_objects() {
1908        let tape = DsonTape::parse(r#"{"items":[{"id":1},{"id":2}]}"#).unwrap();
1909        let modifications = FastHashMap::default();
1910        let deletions = FastHashSet::default();
1911        let result = tape.serialize_with_modifications(&modifications, &deletions);
1912        assert!(result.is_ok());
1913    }
1914
1915    #[test]
1916    fn test_filter_by_schema_deep_path() {
1917        let tape = DsonTape::parse(r#"{"user":{"profile":{"name":"test"}}}"#).unwrap();
1918        let schema = std::collections::HashSet::from([
1919            "user".to_string(),
1920            "user.profile".to_string(),
1921            "user.profile.name".to_string(),
1922        ]);
1923        let result = tape.filter_by_schema(&schema);
1924        assert!(result.is_ok());
1925    }
1926
1927    #[test]
1928    fn test_skip_field_out_of_bounds() {
1929        let tape = DsonTape::parse(r#"{"a": 1}"#).unwrap();
1930        // Try to skip a field at an index beyond the tape
1931        let result = tape.skip_field(100);
1932        assert!(result.is_ok());
1933        assert!(result.unwrap().is_none());
1934    }
1935
1936    #[test]
1937    fn test_skip_field_on_string() {
1938        let tape = DsonTape::parse(r#"{"name": "value"}"#).unwrap();
1939        // Skip field starting at index 1 (field name)
1940        let result = tape.skip_field(1);
1941        assert!(result.is_ok());
1942    }
1943
1944    #[test]
1945    fn test_serialize_with_object_ref() {
1946        let tape = DsonTape::parse(r#"{"a": 1}"#).unwrap();
1947        let mut modifications = FastHashMap::default();
1948        modifications.insert(
1949            "new".to_string(),
1950            fionn_core::OperationValue::ObjectRef { start: 0, end: 1 },
1951        );
1952        let deletions = FastHashSet::default();
1953        let result = tape.serialize_with_modifications(&modifications, &deletions);
1954        assert!(result.is_ok());
1955    }
1956
1957    #[test]
1958    fn test_serialize_with_array_ref() {
1959        let tape = DsonTape::parse(r#"{"a": 1}"#).unwrap();
1960        let mut modifications = FastHashMap::default();
1961        modifications.insert(
1962            "arr".to_string(),
1963            fionn_core::OperationValue::ArrayRef { start: 0, end: 1 },
1964        );
1965        let deletions = FastHashSet::default();
1966        let result = tape.serialize_with_modifications(&modifications, &deletions);
1967        assert!(result.is_ok());
1968    }
1969
1970    #[test]
1971    fn test_filter_by_schema_wildcard() {
1972        let tape = DsonTape::parse(r#"{"users": {"name": "test", "age": 30}}"#).unwrap();
1973        let schema = std::collections::HashSet::from(["users.*".to_string()]);
1974        let result = tape.filter_by_schema(&schema);
1975        assert!(result.is_ok());
1976    }
1977
1978    #[test]
1979    fn test_filter_by_schema_child_path() {
1980        let tape = DsonTape::parse(r#"{"user": {"profile": {"name": "test"}}}"#).unwrap();
1981        let schema =
1982            std::collections::HashSet::from(["user".to_string(), "user.profile".to_string()]);
1983        let result = tape.filter_by_schema(&schema);
1984        assert!(result.is_ok());
1985    }
1986
1987    #[test]
1988    fn test_skip_value_on_array() {
1989        let tape = DsonTape::parse(r"[[1,2],[3,4]]").unwrap();
1990        let result = tape.skip_value(0);
1991        assert!(result.is_ok());
1992    }
1993
1994    #[test]
1995    fn test_serialize_sparse_array_modifications() {
1996        let tape = DsonTape::parse(r#"{"items": []}"#).unwrap();
1997        let mut modifications = FastHashMap::default();
1998        modifications.insert(
1999            "items[2]".to_string(),
2000            fionn_core::OperationValue::StringRef("sparse".to_string()),
2001        );
2002        let deletions = FastHashSet::default();
2003        let result = tape.serialize_with_modifications(&modifications, &deletions);
2004        assert!(result.is_ok());
2005    }
2006
2007    #[test]
2008    fn test_serialize_nested_array_in_array() {
2009        let tape = DsonTape::parse(r#"{"data": []}"#).unwrap();
2010        let mut modifications = FastHashMap::default();
2011        modifications.insert(
2012            "data[0][0]".to_string(),
2013            fionn_core::OperationValue::NumberRef("42".to_string()),
2014        );
2015        let deletions = FastHashSet::default();
2016        let result = tape.serialize_with_modifications(&modifications, &deletions);
2017        assert!(result.is_ok());
2018    }
2019
2020    #[test]
2021    fn test_serialize_nested_object_in_array() {
2022        let tape = DsonTape::parse(r#"{"data": []}"#).unwrap();
2023        let mut modifications = FastHashMap::default();
2024        modifications.insert(
2025            "data[0].name".to_string(),
2026            fionn_core::OperationValue::StringRef("test".to_string()),
2027        );
2028        let deletions = FastHashSet::default();
2029        let result = tape.serialize_with_modifications(&modifications, &deletions);
2030        assert!(result.is_ok());
2031    }
2032
2033    #[test]
2034    fn test_path_survives_filter_exact_match() {
2035        let tape = DsonTape::parse(r#"{"field": "value"}"#).unwrap();
2036        let schema = std::collections::HashSet::from(["field".to_string()]);
2037        let result = tape.filter_by_schema(&schema);
2038        assert!(result.is_ok());
2039        let filtered = result.unwrap();
2040        let json = filtered.to_json_string();
2041        assert!(json.is_ok());
2042    }
2043
2044    #[test]
2045    fn test_reconstruct_value_empty_object() {
2046        let tape = DsonTape::parse(r"{}").unwrap();
2047        let result = tape.reconstruct_value_from_tape(0, 2);
2048        assert!(result.is_ok());
2049    }
2050
2051    #[test]
2052    fn test_reconstruct_value_empty_array() {
2053        let tape = DsonTape::parse(r"[]").unwrap();
2054        let result = tape.reconstruct_value_from_tape(0, 2);
2055        assert!(result.is_ok());
2056    }
2057
2058    #[test]
2059    fn test_simd_value_string_variant() {
2060        let value = SimdValue::String("test".to_string());
2061        let debug_str = format!("{value:?}");
2062        assert!(debug_str.contains("String"));
2063    }
2064
2065    #[test]
2066    fn test_simd_value_bool_variant() {
2067        let value = SimdValue::Bool(true);
2068        let debug_str = format!("{value:?}");
2069        assert!(debug_str.contains("Bool"));
2070    }
2071
2072    #[test]
2073    fn test_simd_value_null_variant() {
2074        let value = SimdValue::Null;
2075        let debug_str = format!("{value:?}");
2076        assert!(debug_str.contains("Null"));
2077    }
2078
2079    #[test]
2080    fn test_simd_value_number_variant() {
2081        let value = SimdValue::Number("3.14".to_string());
2082        let debug_str = format!("{value:?}");
2083        assert!(debug_str.contains("Number"));
2084    }
2085
2086    #[test]
2087    fn test_parse_path_with_multiple_arrays() {
2088        let components = DsonTape::parse_path("data[0].items[1].value");
2089        assert_eq!(components.len(), 5);
2090    }
2091
2092    #[test]
2093    fn test_roundtrip_escaped_quote() {
2094        // This was a fuzzer-found crash: string with escaped quote
2095        let input = r#"[ "Hello, \u4e16\"emoji"]"#;
2096        let tape = DsonTape::parse(input).expect("should parse");
2097        let serialized = tape.to_json_string().expect("should serialize");
2098
2099        // Debug: print what we got
2100        eprintln!("Input:      {}", input);
2101        eprintln!("Serialized: {}", serialized);
2102
2103        // The serialized output should be valid JSON
2104        let reparsed = DsonTape::parse(&serialized);
2105        assert!(
2106            reparsed.is_ok(),
2107            "Round-trip failed: serialized JSON is invalid: {}",
2108            serialized
2109        );
2110    }
2111
2112    #[test]
2113    fn test_roundtrip_backslash() {
2114        let input = r#"{"key": "back\\slash"}"#;
2115        let tape = DsonTape::parse(input).expect("should parse");
2116        let serialized = tape.to_json_string().expect("should serialize");
2117        let reparsed = DsonTape::parse(&serialized);
2118        assert!(
2119            reparsed.is_ok(),
2120            "Round-trip failed for backslash: {}",
2121            serialized
2122        );
2123    }
2124
2125    #[test]
2126    fn test_roundtrip_unicode_escape() {
2127        let input = r#"["Hello, \u4e16\u754c!"]"#;
2128        let tape = DsonTape::parse(input).expect("should parse");
2129        let serialized = tape.to_json_string().expect("should serialize");
2130        let reparsed = DsonTape::parse(&serialized);
2131        assert!(
2132            reparsed.is_ok(),
2133            "Round-trip failed for unicode: {}",
2134            serialized
2135        );
2136    }
2137
2138    /// Test all the fuzzer-discovered crash inputs to ensure they're fixed.
2139    /// These inputs found issues with string escape handling during serialization.
2140    #[test]
2141    fn test_fuzz_crash_inputs() {
2142        let crash_inputs = vec![
2143            // Escaped quote in string
2144            r#"[ "Hello, \u4e16\"emoji"]"#,
2145            // Object with escaped characters in key
2146            r#"[{"id": 1}, {"id": 2}, {"ttab\\slash\"quoid": 3}]"#,
2147            // Multiple unicode escapes
2148            r#"[ "Hello, \u4e16\u754c!", "emoji"]"#,
2149            // Escaped backslash followed by unicode
2150            r#"[ "Udvrz-\\u8f02\u568b%", "asvzx"]"#,
2151            // Many backslashes
2152            r#"[ "Hello, \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\u4e16\u754c!", "emoji"]"#,
2153            // Carriage return escape
2154            r#"[ "Hello, \r4e16\u754c!", "emoji"]"#,
2155            // Unicode with extra text
2156            r#"[ "Hello, \u4e16\u054c!", "emoji"]"#,
2157        ];
2158
2159        for (i, input) in crash_inputs.iter().enumerate() {
2160            // First verify serde_json can parse it (to confirm input is valid JSON)
2161            if serde_json::from_str::<serde_json::Value>(input).is_err() {
2162                // Input itself is invalid JSON, skip
2163                continue;
2164            }
2165
2166            // Parse with DsonTape
2167            let tape = match DsonTape::parse(input) {
2168                Ok(t) => t,
2169                Err(_) => continue, // Some inputs may be invalid for simd-json
2170            };
2171
2172            // Serialize back
2173            let serialized = match tape.to_json_string() {
2174                Ok(s) => s,
2175                Err(e) => panic!("Crash input {} failed to serialize: {}", i, e),
2176            };
2177
2178            // Verify round-trip produces valid JSON
2179            match DsonTape::parse(&serialized) {
2180                Ok(_) => {} // Success!
2181                Err(e) => panic!(
2182                    "Crash input {} round-trip failed:\n  Input: {}\n  Serialized: {}\n  Error: {}",
2183                    i, input, serialized, e
2184                ),
2185            }
2186        }
2187    }
2188}