use crate::Reader;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ChunkReader<'a> {
data: &'a [u8],
pos: usize,
chunk_size: usize,
}
impl<'a> ChunkReader<'a> {
pub fn new(data: &'a [u8], chunk_size: usize) -> Self {
Self {
data,
pos: 0,
chunk_size: chunk_size.max(1), }
}
pub fn full_slice(data: &'a [u8]) -> Self {
Self {
data,
pos: 0,
chunk_size: usize::MAX,
}
}
}
impl Reader for ChunkReader<'_> {
type Error = ();
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
let remaining_len = self.data.len().saturating_sub(self.pos);
if remaining_len == 0 {
return Ok(0);
}
let to_copy = remaining_len.min(buf.len()).min(self.chunk_size);
if to_copy > 0 {
for i in 0..to_copy {
if let (Some(dest_byte), Some(src_byte)) =
(buf.get_mut(i), self.data.get(self.pos + i))
{
*dest_byte = *src_byte;
} else {
return Ok(i);
}
}
self.pos = self.pos.saturating_add(to_copy);
}
Ok(to_copy)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_full_slice_reader_basic() {
let data = b"hello world";
let mut reader = ChunkReader::full_slice(data);
let mut buf = [0u8; 5];
assert_eq!(reader.read(&mut buf).unwrap(), 5);
assert_eq!(&buf, b"hello");
let mut buf = [0u8; 10];
assert_eq!(reader.read(&mut buf).unwrap(), 6);
assert_eq!(&buf[..6], b" world");
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn test_full_slice_reader_empty() {
let mut reader = ChunkReader::full_slice(b"");
let mut buf = [0u8; 10];
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn test_chunk_reader_basic() {
let data = b"hello world";
let mut reader = ChunkReader::new(data, 3);
let mut buf = [0u8; 10];
assert_eq!(reader.read(&mut buf).unwrap(), 3);
assert_eq!(&buf[..3], b"hel");
assert_eq!(reader.read(&mut buf).unwrap(), 3);
assert_eq!(&buf[..3], b"lo ");
assert_eq!(reader.read(&mut buf).unwrap(), 3);
assert_eq!(&buf[..3], b"wor");
assert_eq!(reader.read(&mut buf).unwrap(), 2);
assert_eq!(&buf[..2], b"ld");
assert_eq!(reader.read(&mut buf).unwrap(), 0);
}
#[test]
fn test_chunk_reader_small_buffer() {
let data = b"hello";
let mut reader = ChunkReader::new(data, 10);
let mut buf = [0u8; 3];
assert_eq!(reader.read(&mut buf).unwrap(), 3);
assert_eq!(&buf, b"hel");
assert_eq!(reader.read(&mut buf).unwrap(), 2);
assert_eq!(&buf[..2], b"lo");
}
#[test]
fn test_chunk_reader_zero_chunk_size() {
let mut reader = ChunkReader::new(b"hello", 0);
let mut buf = [0u8; 10];
assert_eq!(reader.read(&mut buf).unwrap(), 1);
assert_eq!(buf[0], b'h');
}
}
#[cfg(test)]
mod integration_tests {
use super::*;
use crate::{Event, PullParser, StreamParser};
#[test]
fn test_full_slice_reader_with_stream_parser() {
let json = br#"{"name": "Alice", "age": 30, "active": true}"#;
let reader = ChunkReader::full_slice(json);
let mut buffer = [0u8; 128];
let mut parser = StreamParser::new(reader, &mut buffer);
assert_eq!(parser.next_event().unwrap(), Event::StartObject);
assert!(matches!(parser.next_event().unwrap(), Event::Key(_)));
assert!(matches!(parser.next_event().unwrap(), Event::String(_)));
assert!(matches!(parser.next_event().unwrap(), Event::Key(_)));
assert!(matches!(parser.next_event().unwrap(), Event::Number(_)));
assert!(matches!(parser.next_event().unwrap(), Event::Key(_)));
assert_eq!(parser.next_event().unwrap(), Event::Bool(true));
assert_eq!(parser.next_event().unwrap(), Event::EndObject);
assert_eq!(parser.next_event().unwrap(), Event::EndDocument);
}
#[test]
fn test_chunk_reader_with_stream_parser() {
let json = br#"[1, 2, 3, 4, 5]"#;
let reader = ChunkReader::new(json, 2); let mut buffer = [0u8; 64]; let mut parser = StreamParser::new(reader, &mut buffer);
assert_eq!(parser.next_event().unwrap(), Event::StartArray);
for i in 1..=5 {
let event = parser.next_event().unwrap();
if let Event::Number(num) = event {
let parsed: i32 = num.as_str().parse().unwrap();
assert_eq!(parsed, i);
} else {
panic!("Expected number, got: {:?}", event);
}
}
assert_eq!(parser.next_event().unwrap(), Event::EndArray);
assert_eq!(parser.next_event().unwrap(), Event::EndDocument);
}
#[test]
fn test_stress_test_tiny_chunks() {
let json = br#"{"escape": "hello\nworld", "unicode": "\u0041"}"#;
let reader = ChunkReader::new(json, 1);
let mut buffer = [0u8; 128];
let mut parser = StreamParser::new(reader, &mut buffer);
let mut event_count = 0;
while let Ok(event) = parser.next_event() {
event_count += 1;
if matches!(event, Event::EndDocument) {
break;
}
}
assert!(event_count > 5); }
fn test_delimiter_bug_helper(chunk_size: usize, buffer_size: usize) {
let json = br#"[1, 2, 3]"#;
let reader = ChunkReader::new(json, chunk_size);
let mut buffer = vec![0u8; buffer_size];
let mut parser = StreamParser::new(reader, &mut buffer);
assert_eq!(parser.next_event().unwrap(), Event::StartArray);
let mut number_count = 0;
loop {
let event = parser.next_event().unwrap();
match event {
Event::Number(num) => {
number_count += 1;
let num_str = num.as_str();
if num_str.contains(',') || num_str.contains(']') {
panic!("Number {} contains delimiter: '{}'", number_count, num_str);
}
let parsed: i32 = num_str
.parse()
.unwrap_or_else(|_| panic!("Failed to parse '{}' as number", num_str));
assert_eq!(
parsed, number_count,
"Number {} should be {}, got {}",
number_count, number_count, parsed
);
}
Event::EndArray => {
break;
}
Event::EndDocument => {
break;
}
_ => {}
}
}
if let Ok(Event::EndDocument) = parser.next_event() {}
assert_eq!(number_count, 3, "Should have processed exactly 3 numbers");
}
#[test]
fn test_delimiter_bug_tiny_chunks_small_buffer() {
test_delimiter_bug_helper(2, 64);
}
#[test]
fn test_delimiter_bug_tiny_chunks_medium_buffer() {
test_delimiter_bug_helper(2, 128);
}
#[test]
fn test_delimiter_bug_small_chunks_small_buffer() {
test_delimiter_bug_helper(3, 64);
}
#[test]
fn test_delimiter_bug_medium_chunks_small_buffer() {
test_delimiter_bug_helper(4, 64);
}
#[test]
fn test_delimiter_bug_large_chunks_small_buffer() {
test_delimiter_bug_helper(8, 64);
}
#[test]
fn test_delimiter_bug_tiny_chunks_large_buffer() {
test_delimiter_bug_helper(2, 256);
}
}