Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions picojson/examples/push_parser_demo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Example demonstrating PushParser with SAX-style event handling

use picojson::{DefaultConfig, Event, ParseError, PushParseError, PushParser, PushParserHandler};

/// A simple event handler that prints JSON events as they arrive
struct JsonEventPrinter {
indent: usize,
event_count: usize,
}

impl JsonEventPrinter {
fn new() -> Self {
Self {
indent: 0,
event_count: 0,
}
}

fn indent_str(&self) -> String {
" ".repeat(self.indent)
}
}

impl<'input, 'scratch> PushParserHandler<'input, 'scratch, ParseError> for JsonEventPrinter {
fn handle_event(&mut self, event: Event<'input, 'scratch>) -> Result<(), ParseError> {
self.event_count += 1;

match event {
Event::StartObject => {
println!("{}🏁 StartObject", self.indent_str());
self.indent += 1;
}
Event::EndObject => {
self.indent = self.indent.saturating_sub(1);
println!("{}🏁 EndObject", self.indent_str());
}
Event::StartArray => {
println!("{}📋 StartArray", self.indent_str());
self.indent += 1;
}
Event::EndArray => {
self.indent = self.indent.saturating_sub(1);
println!("{}📋 EndArray", self.indent_str());
}
Event::Key(key) => {
println!("{}🔑 Key: '{}'", self.indent_str(), key.as_str());
}
Event::String(s) => {
println!("{}📝 String: '{}'", self.indent_str(), s.as_str());
}
Event::Number(num) => {
println!("{}🔢 Number: {}", self.indent_str(), num);
}
Event::Bool(b) => {
println!("{}✅ Bool: {}", self.indent_str(), b);
}
Event::Null => {
println!("{}⭕ Null", self.indent_str());
}
Event::EndDocument => {
println!("{}🏁 EndDocument", self.indent_str());
}
}
Ok(())
}
}

fn main() -> Result<(), PushParseError<ParseError>> {
println!("🚀 PushParser Demo - SAX-style JSON Processing");
println!("===============================================");
println!();

// Example JSON with various features to demonstrate push parsing
let json_chunks = vec![
br#"{"name": "Pic"#.as_slice(),
br#"oJSON", "version": 1.0, "#.as_slice(),
br#""features": ["fast", "no_std""#.as_slice(),
br#", "zero\u0041lloc"], "escapes": "hello\nworld", "#.as_slice(),
br#""nested": {"data": [1, 2.5, true, null]}}"#.as_slice(),
];

let full_json = json_chunks.concat();
let json_str = std::str::from_utf8(&full_json)?;

println!("📄 Input JSON: {}", json_str);
println!("📏 Total size: {} bytes", full_json.len());
println!(
"📦 Processing in {} chunks (simulates streaming)",
json_chunks.len()
);
println!();

// Create handler and parser
let handler = JsonEventPrinter::new();
let mut buffer = [0u8; 512]; // Scratch buffer for escape processing
let buffer_size = buffer.len();
let mut parser = PushParser::<_, DefaultConfig>::new(handler, &mut buffer);

println!("🔄 Starting PushParser with incremental data feeding:");
println!(" Buffer size: {} bytes", buffer_size);
println!();

// Feed data chunk by chunk to demonstrate streaming capability
for (i, chunk) in json_chunks.iter().enumerate() {
println!("📨 Processing chunk {} ({} bytes):", i + 1, chunk.len());
let chunk_str = std::str::from_utf8(chunk)?;
println!(" Chunk data: {:?}", chunk_str);

// Write chunk to parser - events are handled immediately
parser.write(chunk)?;
println!();
}

// Signal end of input and retrieve the handler
println!("🔚 Finishing parsing...");
let handler = parser.finish()?;

println!();
println!(
"✅ Successfully processed {} events with PushParser!",
handler.event_count
);

Ok(())
}
41 changes: 31 additions & 10 deletions picojson/src/event_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,29 @@ pub struct ParserCore<T: ujson::BitBucket, C: ujson::DepthCounter> {
pub parser_state: ParserState,
/// Tracks if the parser is currently inside any escape sequence (\n, \uXXXX, etc.)
in_escape_sequence: bool,
/// Whether this parser handles chunked input (true for PushParser, false for Slice/Stream)
/// When true, running out of input returns EndOfData. When false, calls tokenizer.finish().
handles_chunked_input: bool,
}

impl<T: ujson::BitBucket, C: ujson::DepthCounter> ParserCore<T, C> {
/// Create a new ParserCore
/// Create a new ParserCore for non-chunked parsers (SliceParser, StreamParser)
pub fn new() -> Self {
Self {
tokenizer: Tokenizer::new(),
parser_state: ParserState::new(),
in_escape_sequence: false,
handles_chunked_input: false,
}
}

/// Create a new ParserCore for chunked parsers (PushParser)
pub fn new_chunked() -> Self {
Self {
tokenizer: Tokenizer::new(),
parser_state: ParserState::new(),
in_escape_sequence: false,
handles_chunked_input: true,
}
}

Expand Down Expand Up @@ -87,15 +101,22 @@ impl<T: ujson::BitBucket, C: ujson::DepthCounter> ParserCore<T, C> {
byte_accumulator(provider, byte)?;
}
} else {
{
let mut finish_callback =
create_tokenizer_callback(&mut self.parser_state.evts);
let _bytes_processed = self.tokenizer.finish(&mut finish_callback)?;
} // Drop the callback to release the borrow

// If finish() generated events, process them. Otherwise, return EndDocument.
if !have_events(&self.parser_state.evts) {
return Ok(Event::EndDocument);
// Handle end of input - behavior depends on parser type
if self.handles_chunked_input {
// For chunked parsers (PushParser), return EndOfData so they can handle chunk boundaries
return Err(ParseError::EndOfData);
} else {
// For non-chunked parsers (SliceParser, StreamParser), finish the document
{
let mut finish_callback =
create_tokenizer_callback(&mut self.parser_state.evts);
let _bytes_processed = self.tokenizer.finish(&mut finish_callback)?;
} // Drop the callback to release the borrow

// If finish() generated events, process them. Otherwise, return EndDocument.
if !have_events(&self.parser_state.evts) {
return Ok(Event::EndDocument);
}
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions picojson/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,8 @@ pub use stream_parser::{Reader, StreamParser};

mod chunk_reader;
pub use chunk_reader::ChunkReader;

mod push_content_builder;
mod push_parser;
pub use push_content_builder::PushParserHandler;
pub use push_parser::{PushParseError, PushParser};
Loading
Loading