mod stage1;
mod stage2;
pub(crate) mod charclass;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
UnexpectedEof,
UnterminatedString,
MissingClosingBrace,
MissingClosingBracket,
InvalidCharacter(u8),
InvalidEscape,
MissingColon,
MissingComma,
InvalidValue,
MismatchedBracket,
InvalidStructure,
}
impl std::fmt::Display for ErrorKind {
#[cold]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ErrorKind::UnexpectedEof => write!(f, "Unexpected end of input"),
ErrorKind::UnterminatedString => write!(f, "Unterminated string"),
ErrorKind::MissingClosingBrace => write!(f, "Missing closing brace"),
ErrorKind::MissingClosingBracket => write!(f, "Missing closing bracket"),
ErrorKind::InvalidCharacter(c) => {
write!(f, "Invalid character: {}", char::from(*c))
}
ErrorKind::InvalidEscape => write!(f, "Invalid escape sequence"),
ErrorKind::MissingColon => write!(f, "Missing colon after object key"),
ErrorKind::MissingComma => write!(f, "Missing comma between values"),
ErrorKind::InvalidValue => write!(f, "Invalid value"),
ErrorKind::MismatchedBracket => write!(f, "Mismatched bracket"),
ErrorKind::InvalidStructure => write!(f, "Invalid JSON structure"),
}
}
}
impl std::error::Error for ErrorKind {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FragmentStatus {
Complete,
Incomplete(ErrorKind),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Fragment {
pub start: usize,
pub length: usize,
pub status: FragmentStatus,
}
impl Fragment {
#[inline]
pub fn end(&self) -> usize {
self.start + self.length
}
#[inline]
pub fn is_complete(&self) -> bool {
matches!(self.status, FragmentStatus::Complete)
}
}
pub struct StagedScanner {
stage1_output: stage1::Stage1Output,
fragments: Vec<Fragment>,
}
impl StagedScanner {
pub fn new() -> Self {
Self {
stage1_output: stage1::Stage1Output::new(),
fragments: Vec::new(),
}
}
pub fn scan_fragments(&mut self, data: &[u8]) -> &[Fragment] {
self.fragments.clear();
stage1::find_structural_indices(data, &mut self.stage1_output);
stage2::extract_fragments(
data,
&self.stage1_output.structural_indices,
&self.stage1_output.bracket_pairs,
&mut self.fragments,
);
&self.fragments
}
}
impl Default for StagedScanner {
fn default() -> Self {
Self::new()
}
}
pub struct JsonFragmentScanner;
impl JsonFragmentScanner {
pub fn scan_fragments(data: &[u8]) -> Vec<Fragment> {
let mut scanner = StagedScanner::new();
scanner.scan_fragments(data).to_vec()
}
}
pub fn extract_first(data: &str) -> Option<&str> {
let mut scanner = StagedScanner::new();
scanner
.scan_fragments(data.as_bytes())
.iter()
.find(|f| f.is_complete())
.map(|f| &data[f.start..f.end()])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_single_complete_object() {
let data = br#"{"name": "Alice"}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(
fragments.len(),
1,
"Expected 1 fragment, got {}",
fragments.len()
);
if !fragments.is_empty() {
eprintln!("Fragment status: {:?}", fragments[0].status);
}
assert!(fragments[0].is_complete());
assert_eq!(fragments[0].start, 0);
assert_eq!(fragments[0].length, 17);
}
#[test]
fn test_single_complete_array() {
let data = br#"[1, 2, 3]"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
assert_eq!(fragments[0].start, 0);
assert_eq!(fragments[0].length, 9);
}
#[test]
fn test_multiple_fragments() {
let data = br#"{"name": "Alice"} {"age": 30}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 2);
assert!(fragments[0].is_complete());
assert_eq!(fragments[0].start, 0);
assert_eq!(fragments[0].length, 17);
assert!(fragments[1].is_complete());
assert_eq!(fragments[1].start, 18);
assert_eq!(fragments[1].length, 11);
}
#[test]
fn test_nested_objects() {
let data = br#"{"outer": {"inner": 123}}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_nested_arrays() {
let data = br#"[[1, 2], [3, 4]]"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_mixed_nesting() {
let data = br#"{"array": [1, {"nested": true}]}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_string_with_escapes() {
let data = br#"{"text": "hello \"world\""}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_string_with_brackets() {
let data = br#"{"text": "has { and ] chars"}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_incomplete_fragment() {
let incomplete_data = br#"{"field": "#;
let fragments = JsonFragmentScanner::scan_fragments(incomplete_data);
assert_eq!(fragments.len(), 1);
assert!(!fragments[0].is_complete());
assert_eq!(fragments[0].start, 0);
}
#[test]
fn test_incomplete_string() {
let incomplete_string = br#"{"text": "hel"#;
let fragments = JsonFragmentScanner::scan_fragments(incomplete_string);
assert_eq!(fragments.len(), 1);
assert!(!fragments[0].is_complete());
}
#[test]
fn test_numbers() {
let data = br#"{"int": 123, "float": 45.67, "exp": 1.2e-10}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_booleans_and_null() {
let data = br#"{"bool": true, "other": false, "nothing": null}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_whitespace_handling() {
let data = b" \n\t { \"test\" : 123 } \n ";
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_text_before_fragment() {
let data = br#"some random text {"json": "here"} more text"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
assert_eq!(fragments[0].start, 17);
}
#[test]
fn test_empty_object() {
let data = br#"{}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
assert_eq!(fragments[0].length, 2);
}
#[test]
fn test_empty_array() {
let data = br#"[]"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
assert_eq!(fragments[0].length, 2);
}
#[test]
fn test_simple_object_stage1_debug() {
let json = br#"{"a":1}"#;
let mut stage1_out = crate::stage1::Stage1Output::new();
crate::stage1::find_structural_indices(json, &mut stage1_out);
println!("Simple JSON: {}", String::from_utf8_lossy(json));
println!(
"Stage1 found {} structural indices",
stage1_out.structural_indices.len()
);
println!("Expected: 5 (1 left-brace + 2 quotes + 1 colon + 1 right-brace)");
println!("Indices: {:?}", stage1_out.structural_indices);
assert!(
stage1_out.structural_indices.len() >= 5,
"Should find at least 5 structural chars"
);
}
#[test]
fn test_deeply_nested() {
let mut deep = String::from("{");
for _ in 0..50 {
deep.push_str("\"a\":{");
}
deep.push_str("\"value\":123");
for _ in 0..50 {
deep.push('}');
}
deep.push('}');
println!("Generated JSON: {} chars", deep.len());
println!("First 100 chars: {}", &deep[..100.min(deep.len())]);
let mut stage1_out = crate::stage1::Stage1Output::new();
crate::stage1::find_structural_indices(deep.as_bytes(), &mut stage1_out);
println!(
"Stage1: {} structural indices, {} bracket pairs",
stage1_out.structural_indices.len(),
stage1_out.bracket_pairs.len(),
);
let fragments = JsonFragmentScanner::scan_fragments(deep.as_bytes());
println!("Fragments found: {}", fragments.len());
for (i, f) in fragments.iter().enumerate() {
println!(
" Fragment {}: start={}, len={}, complete={}",
i,
f.start,
f.length,
f.is_complete()
);
}
assert_eq!(fragments.len(), 1, "Expected 1 fragment");
assert!(fragments[0].is_complete(), "Fragment should be complete");
}
#[test]
fn test_fragment_end_method() {
let fragment = Fragment {
start: 10,
length: 20,
status: FragmentStatus::Complete,
};
assert_eq!(fragment.end(), 30);
}
#[test]
fn test_trailing_comma_in_object() {
let data = br#"{"a": 1,}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_trailing_comma_in_array() {
let data = br#"[1, 2,]"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_complex_valid_object() {
let data = br#"{"a": 1, "b": [2, 3], "c": {"d": true}}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_complex_valid_array() {
let data = br#"[1, "two", {"three": 3}, [4, 5], true, null]"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_empty_string_as_key() {
let data = br#"{"": "value"}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_array_in_object_value_position() {
let data = br#"{"key": [1, 2, 3]}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_object_in_array() {
let data = br#"[{"a": 1}, {"b": 2}]"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_utf8_multibyte_emoji() {
let data = r#"{"emoji": "👍 🚀 ✅"}"#.as_bytes();
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_utf8_multibyte_cjk() {
let data = r#"{"text": "你好世界", "lang": "中文"}"#.as_bytes();
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_utf8_multibyte_mixed() {
let data = r#"{"msg": "Hello мир 世界 👋!"}"#.as_bytes();
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_utf8_in_keys() {
let data = r#"{"名前": "Alice", "возраст": 30}"#.as_bytes();
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_single_escaped_quote() {
let data = br#"{"text": "He said \"hello\""}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_double_backslash_then_quote() {
let data = br#"{"text": "path\\", "next": "value"}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_triple_backslash_then_quote() {
let data = br#"{"text": "value\\\""}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_four_backslashes_then_quote() {
let data = br#"{"text": "path\\\\", "next": "value"}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_many_consecutive_backslashes() {
let data = br#"{"text": "backslashes: \\\\\\\\"}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_escaped_backslash_in_middle() {
let data = br#"{"path": "C:\\Users\\Alice\\file.txt"}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_bracket_mismatch_array_closed_with_brace() {
let data = br#"[1, 2, 3}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(!fragments[0].is_complete());
}
#[test]
fn test_bracket_mismatch_object_closed_with_bracket() {
let data = br#"{"key": "value"]"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
assert!(!fragments[0].is_complete());
}
#[test]
fn test_bracket_mismatch_nested() {
let data = br#"{"array": [1, 2}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
}
#[test]
fn test_bracket_mismatch_multiple_fragments() {
let data = br#"[1, 2} {"valid": true}"#;
let fragments = JsonFragmentScanner::scan_fragments(data);
assert!(!fragments.is_empty());
assert!(!fragments[0].is_complete());
}
#[test]
fn test_null_byte_in_string() {
let mut data = Vec::from(br#"{"text": "before"#);
data.push(0); data.extend_from_slice(br#"after"}"#);
let fragments = JsonFragmentScanner::scan_fragments(&data);
assert_eq!(fragments.len(), 1);
}
#[test]
fn test_control_characters_in_string() {
let mut data = Vec::from(br#"{"text": "hello"#);
data.push(0x01); data.push(0x0F); data.extend_from_slice(br#"world"}"#);
let fragments = JsonFragmentScanner::scan_fragments(&data);
assert_eq!(fragments.len(), 1);
}
#[test]
fn test_tab_and_newline_in_string() {
let data = b"{\"text\": \"line1\nline2\tindented\"}";
let fragments = JsonFragmentScanner::scan_fragments(data);
assert_eq!(fragments.len(), 1);
}
#[test]
fn test_high_byte_values() {
let mut data = Vec::from(br#"{"data": ""#);
data.push(0xFF);
data.push(0xFE);
data.push(0xFD);
data.extend_from_slice(br#""}"#);
let fragments = JsonFragmentScanner::scan_fragments(&data);
assert_eq!(fragments.len(), 1);
}
#[test]
fn test_extreme_nesting_1000_levels() {
let mut json = String::new();
for i in 0..1000 {
json.push('{');
json.push_str(&format!("\"level_{}\":", i));
}
json.push_str("\"value\"");
for _ in 0..1000 {
json.push('}');
}
let fragments = JsonFragmentScanner::scan_fragments(json.as_bytes());
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
#[test]
fn test_extreme_nesting_mixed_brackets() {
let mut json = String::new();
for i in 0..500 {
if i % 2 == 0 {
json.push('{');
json.push_str(&format!("\"key_{}\":", i));
} else {
json.push('[');
}
}
json.push_str("42");
for i in (0..500).rev() {
if i % 2 == 0 {
json.push('}');
} else {
json.push(']');
}
}
let fragments = JsonFragmentScanner::scan_fragments(json.as_bytes());
assert_eq!(fragments.len(), 1);
assert!(fragments[0].is_complete());
}
}
#[cfg(test)]
mod proptest_tests {
use super::*;
use proptest::prelude::*;
fn json_fragment() -> impl Strategy<Value = String> {
let leaf = prop_oneof![
"[a-z]{1,10}".prop_map(|s| format!("\"{}\"", s)),
(-1000i32..1000i32).prop_map(|n| n.to_string()),
prop_oneof![
Just("true".to_string()),
Just("false".to_string()),
Just("null".to_string()),
],
];
leaf.prop_recursive(
4, 32, 10, |inner| {
prop_oneof![
prop::collection::vec(inner.clone(), 0..5)
.prop_map(|items| format!("[{}]", items.join(","))),
prop::collection::vec(("[a-z]{1,5}", inner.clone()), 0..5).prop_map(|items| {
let pairs: Vec<String> = items
.into_iter()
.map(|(k, v)| format!("\"{}\":{}", k, v))
.collect();
format!("{{{}}}", pairs.join(","))
}),
]
},
)
.prop_filter("Must be object or array", |s| {
s.starts_with('{') || s.starts_with('[')
})
}
#[test]
fn proptest_multiple_fragments() {
proptest!(|(jsons in prop::collection::vec(json_fragment(), 1..5))| {
let combined = jsons.join(" ");
let fragments = JsonFragmentScanner::scan_fragments(combined.as_bytes());
prop_assert_eq!(fragments.len(), jsons.len());
for frag in fragments {
prop_assert!(frag.is_complete());
}
});
}
#[test]
fn proptest_random_bytes_no_panic() {
proptest!(|(bytes in prop::collection::vec(any::<u8>(), 0..100))| {
let _ = JsonFragmentScanner::scan_fragments(&bytes);
});
}
}