1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
// SPDX-License-Identifier: Apache-2.0
//! ContentBuilder implementation for StreamParser using StreamBuffer.
use crate::escape_processor::UnicodeEscapeCollector;
use crate::event_processor::ContentExtractor;
use crate::shared::{ContentRange, DataSource, State};
use crate::stream_buffer::StreamBuffer;
use crate::stream_parser::Reader;
use crate::{Event, JsonNumber, ParseError};
/// ContentBuilder implementation for StreamParser that uses StreamBuffer for streaming and escape processing
pub struct StreamContentBuilder<'scratch, R: Reader> {
/// The reader for fetching more data
reader: R,
/// StreamBuffer for single-buffer input and escape processing
stream_buffer: StreamBuffer<'scratch>,
/// Parser state tracking
parser_state: State,
/// Unicode escape collector for \uXXXX sequences
unicode_escape_collector: UnicodeEscapeCollector,
/// Flag to reset unescaped content on next operation
unescaped_reset_queued: bool,
/// Flag to track when the input stream has been finished (for number parsing)
finished: bool,
}
impl<'scratch, R: Reader> StreamContentBuilder<'scratch, R> {
/// Create a new StreamContentBuilder
pub fn new(buffer: &'scratch mut [u8], reader: R) -> Self {
Self {
reader,
stream_buffer: StreamBuffer::new(buffer),
parser_state: State::None,
unicode_escape_collector: UnicodeEscapeCollector::new(),
unescaped_reset_queued: false,
finished: false,
}
}
/// Fill the buffer from the reader
fn fill_buffer_from_reader(&mut self) -> Result<(), ParseError> {
// If buffer is full, try to compact it first (original compaction logic)
if self.stream_buffer.get_fill_slice().is_none() {
// Buffer is full - ALWAYS attempt compaction
let compact_start_pos = match self.parser_state {
State::Number(start_pos) => start_pos,
State::Key(start_pos) => start_pos,
State::String(start_pos) => start_pos,
_ => self.stream_buffer.current_position(),
};
let compaction_offset = self
.stream_buffer
.compact_from(compact_start_pos)
.map_err(ParseError::from)?;
if compaction_offset == 0 {
// Buffer too small for current token - this is an input buffer size issue
return Err(ParseError::InputBufferFull);
}
// Update parser state positions after compaction (original logic)
self.update_positions_after_compaction(compaction_offset)?;
}
if let Some(fill_slice) = self.stream_buffer.get_fill_slice() {
let bytes_read = self
.reader
.read(fill_slice)
.map_err(|_| ParseError::ReaderError)?;
self.stream_buffer
.mark_filled(bytes_read)
.map_err(ParseError::from)?;
}
Ok(())
}
/// Update parser state positions after compaction (original logic)
fn update_positions_after_compaction(
&mut self,
compaction_offset: usize,
) -> Result<(), ParseError> {
// Update positions - since we compact from the token start position,
// positions should not be discarded in normal operation
match &mut self.parser_state {
State::None => {
// No position-based state to update
}
State::Key(pos) | State::String(pos) | State::Number(pos) => {
if *pos >= compaction_offset {
*pos = pos.checked_sub(compaction_offset).unwrap_or(0);
} else {
return Err(ParseError::Unexpected(
crate::shared::UnexpectedState::InvalidSliceBounds,
));
}
}
}
Ok(())
}
/// Set the finished state (called by StreamParser when input is exhausted)
pub fn is_finished(&self) -> bool {
self.finished
}
/// Apply queued unescaped content reset if flag is set
pub fn apply_unescaped_reset_if_queued(&mut self) {
if self.unescaped_reset_queued {
self.stream_buffer.clear_unescaped();
self.unescaped_reset_queued = false;
}
}
/// Queue a reset of unescaped content for the next operation
fn queue_unescaped_reset(&mut self) {
self.unescaped_reset_queued = true;
}
}
impl<R: Reader> ContentExtractor for StreamContentBuilder<'_, R> {
fn next_byte(&mut self) -> Result<Option<u8>, ParseError> {
// If buffer is empty, try to fill it first
if self.stream_buffer.is_empty() {
self.fill_buffer_from_reader()?;
}
// If still empty after fill attempt, we're at EOF
if self.stream_buffer.is_empty() {
if !self.finished {
self.finished = true;
}
return Ok(None);
}
// Get byte and advance
let byte = self.stream_buffer.current_byte()?;
self.stream_buffer.advance()?;
Ok(Some(byte))
}
fn parser_state_mut(&mut self) -> &mut State {
&mut self.parser_state
}
fn parser_state(&self) -> &State {
&self.parser_state
}
fn unicode_escape_collector_mut(&mut self) -> &mut UnicodeEscapeCollector {
&mut self.unicode_escape_collector
}
fn current_position(&self) -> usize {
self.stream_buffer.current_position()
}
fn begin_string_content(&mut self, _pos: usize) {
// StreamParser doesn't need explicit string begin processing
// as it handles content accumulation automatically
}
fn extract_string_content(&mut self, start_pos: usize) -> Result<Event<'_, '_>, ParseError> {
// StreamParser-specific: Queue reset to prevent content contamination
if self.has_unescaped_content() {
self.queue_unescaped_reset();
}
let current_pos = self.current_position();
let content_piece = crate::shared::get_content_piece(self, start_pos, current_pos)?;
Ok(Event::String(content_piece.into_string()?))
}
fn extract_key_content(&mut self, start_pos: usize) -> Result<Event<'_, '_>, ParseError> {
// StreamParser-specific: Queue reset to prevent content contamination
if self.has_unescaped_content() {
self.queue_unescaped_reset();
}
let current_pos = self.current_position();
let content_piece = crate::shared::get_content_piece(self, start_pos, current_pos)?;
Ok(Event::Key(content_piece.into_string()?))
}
fn extract_number(
&mut self,
start_pos: usize,
from_container_end: bool,
finished: bool,
) -> Result<Event<'_, '_>, ParseError> {
let current_pos = self.current_position();
// A standalone number at the end of the document has no trailing delimiter, so we use the full span.
let use_full_span = !from_container_end && finished;
let end_pos = ContentRange::number_end_position(current_pos, use_full_span);
// Use the DataSource trait method to get the number bytes
let number_bytes = self.get_borrowed_slice(start_pos, end_pos)?;
let json_number = JsonNumber::from_slice(number_bytes)?;
Ok(Event::Number(json_number))
}
fn begin_escape_sequence(&mut self) -> Result<(), ParseError> {
// Initialize escape processing with StreamBuffer if not already started
if !self.stream_buffer.has_unescaped_content() {
if let State::String(start_pos) | State::Key(start_pos) = self.parser_state {
let current_pos = self.stream_buffer.current_position();
// start_pos already points to content start position (not quote position)
let content_start = start_pos;
// Content to copy ends right before the escape character
let content_end = if self.unicode_escape_collector.has_pending_high_surrogate() {
// Skip copying high surrogate text when processing low surrogate
content_start
} else {
ContentRange::end_position_excluding_delimiter(current_pos)
};
// Estimate max length needed for unescaping (content so far + remaining buffer)
let content_len = content_end.wrapping_sub(content_start);
let max_escaped_len = self
.stream_buffer
.remaining_bytes()
.checked_add(content_len)
.ok_or(ParseError::NumericOverflow)?;
// Start unescaping with StreamBuffer and copy existing content
self.stream_buffer.start_unescaping_with_copy(
max_escaped_len,
content_start,
content_end,
)?;
}
}
Ok(())
}
fn begin_unicode_escape(&mut self) -> Result<(), ParseError> {
// Called when Begin(UnicodeEscape) is received
Ok(())
}
fn handle_simple_escape_char(&mut self, escape_char: u8) -> Result<(), ParseError> {
self.stream_buffer
.append_unescaped_byte(escape_char)
.map_err(ParseError::from)
}
fn process_unicode_escape_with_collector(&mut self) -> Result<(), ParseError> {
// Get pending surrogate before borrowing self
let pending_surrogate = self.unicode_escape_collector.get_pending_high_surrogate();
// Call the shared processor, which now returns the result by value
let (utf8_bytes_result, _, new_pending_surrogate) =
crate::escape_processor::process_unicode_escape_sequence(
self.stream_buffer.current_position(),
pending_surrogate,
self, // Pass self as the DataSource
)?;
// Update the collector's state
self.unicode_escape_collector
.set_pending_high_surrogate(new_pending_surrogate);
// Handle the UTF-8 bytes if we have them
if let Some((utf8_bytes, len)) = utf8_bytes_result {
// Append the resulting bytes to the unescaped buffer
for &byte in &utf8_bytes[..len] {
self.stream_buffer
.append_unescaped_byte(byte)
.map_err(ParseError::from)?;
}
}
Ok(())
}
fn is_finished(&self) -> bool {
self.finished
}
}
impl<R: Reader> StreamContentBuilder<'_, R> {
/// Handle byte accumulation for StreamParser-specific requirements
/// This method is called when a byte doesn't generate any events
pub fn handle_byte_accumulation(&mut self, byte: u8) -> Result<(), ParseError> {
// Check if we're in a string or key state and should accumulate bytes
let in_string_mode = matches!(self.parser_state, State::String(_) | State::Key(_));
if in_string_mode {
// When unescaped content is active, we need to accumulate ALL string content.
// The ParserCore now correctly prevents this function from being called for
// bytes that are part of an escape sequence.
if self.stream_buffer.has_unescaped_content() {
self.stream_buffer
.append_unescaped_byte(byte)
.map_err(ParseError::from)?;
}
}
Ok(())
}
}
/// DataSource implementation for StreamContentBuilder
///
/// This implementation provides access to both borrowed content from the StreamBuffer's
/// internal buffer and unescaped content from the StreamBuffer's scratch space.
/// Note: StreamParser doesn't have a distinct 'input lifetime since it reads from a stream,
/// so we use the buffer lifetime 'scratch for both borrowed and unescaped content.
impl<'scratch, R: Reader> DataSource<'scratch, 'scratch> for StreamContentBuilder<'scratch, R> {
fn get_borrowed_slice(
&'scratch self,
start: usize,
end: usize,
) -> Result<&'scratch [u8], ParseError> {
self.stream_buffer
.get_string_slice(start, end)
.map_err(Into::into)
}
fn get_unescaped_slice(&'scratch self) -> Result<&'scratch [u8], ParseError> {
self.stream_buffer.get_unescaped_slice().map_err(Into::into)
}
fn has_unescaped_content(&self) -> bool {
self.stream_buffer.has_unescaped_content()
}
}