use std::collections::BTreeMap;
use std::io::{self, Read as _};
use structio::beve::{self, Documents, Feed, Mode};
use structio::{ErrorCode, StreamError};
#[derive(Default, Debug, PartialEq, Clone)]
struct Rec {
id: u64,
tag: String,
ratio: f64,
tags: Vec<String>,
samples: Vec<f64>,
flags: Vec<bool>,
ok: bool,
}
structio::object!(Rec {
id,
tag,
ratio,
tags,
samples,
flags,
ok
});
#[derive(Default, Debug, PartialEq)]
struct Small {
id: u64,
}
structio::object!(Small { id });
#[derive(Default, Debug, PartialEq)]
struct Borrowed<'a> {
tag: &'a str,
}
structio::object!(['de] Borrowed<'de> { tag });
#[derive(Default, Debug)]
struct Any;
impl<'de> beve::Read<'de> for Any {
fn read<O: structio::Options>(
&mut self,
r: &mut beve::Reader<'de, O>,
) -> Result<(), ErrorCode> {
r.skip_value()
}
}
const fn records(n: u64) -> u64 {
if cfg!(miri) { n / 10 + 1 } else { n }
}
fn sample(i: u64) -> Rec {
Rec {
id: i,
tag: format!("a\u{20ac}b{i}"),
ratio: 1.5 * i as f64,
tags: vec![String::new(), "x".into(), format!("{i}{i}")],
samples: vec![0.5, 1.5, 2.5],
flags: (0..9).map(|b| (i + b).is_multiple_of(2)).collect(),
ok: i.is_multiple_of(2),
}
}
fn concatenated(n: u64) -> Vec<u8> {
(0..n).flat_map(|i| structio::to_beve(&sample(i))).collect()
}
struct Chunked<'a> {
data: &'a [u8],
chunk: usize,
}
impl io::Read for Chunked<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = self.chunk.min(buf.len()).min(self.data.len());
buf[..n].copy_from_slice(&self.data[..n]);
self.data = &self.data[n..];
Ok(n)
}
}
fn dribble<T>(mode: Mode, bytes: &[u8]) -> Vec<Result<T, StreamError>>
where
T: for<'de> beve::Read<'de> + Default,
{
let mut feed = Feed::new(mode);
let mut out = Vec::new();
for &b in bytes {
feed.push(&[b]);
while let Some(v) = feed.next_value::<T>() {
out.push(v);
}
}
feed.end();
while let Some(v) = feed.next_value::<T>() {
out.push(v);
}
out
}
#[test]
fn a_run_of_documents_reads_back_one_at_a_time() {
let n = records(50);
let bytes = concatenated(n);
let mut docs = Documents::values(&bytes[..]);
let got: Vec<Rec> = docs.iter::<Rec>().map(Result::unwrap).collect();
assert_eq!(got, (0..n).map(sample).collect::<Vec<_>>());
}
#[test]
fn a_document_arriving_one_byte_at_a_time_reads_the_same() {
let n = records(20);
let bytes = concatenated(n);
let got = dribble::<Rec>(Mode::Values, &bytes);
assert_eq!(got.len() as u64, n);
for (i, value) in got.into_iter().enumerate() {
assert_eq!(value.unwrap(), sample(i as u64));
}
}
#[test]
fn every_read_size_produces_the_same_values() {
let n = records(20);
let bytes = concatenated(n);
for chunk in 1..=17 {
let source = Chunked {
data: &bytes,
chunk,
};
let mut docs = Documents::values(source).read_size(chunk);
let got: Vec<Rec> = docs.iter::<Rec>().map(Result::unwrap).collect();
assert_eq!(got.len() as u64, n, "chunk {chunk}");
assert_eq!(got[0], sample(0), "chunk {chunk}");
}
}
#[test]
fn delimiters_between_documents_are_separators_rather_than_values() {
let mut bytes = Vec::new();
for i in 0..3u64 {
bytes.push(structio::beve::header::DELIMITER);
bytes.extend(structio::to_beve(&Small { id: i }));
}
bytes.push(structio::beve::header::DELIMITER);
let mut docs = Documents::values(&bytes[..]);
let got: Vec<u64> = docs.iter::<Small>().map(|r| r.unwrap().id).collect();
assert_eq!(got, [0, 1, 2]);
}
#[test]
fn a_stream_of_nothing_but_delimiters_holds_no_documents() {
let d = structio::beve::header::DELIMITER;
for bytes in [vec![d], vec![d, d, d]] {
let mut docs = Documents::values(&bytes[..]);
assert!(docs.next_value::<Any>().is_none(), "{bytes:02x?}");
assert!(
dribble::<Any>(Mode::Values, &bytes).is_empty(),
"{bytes:02x?}"
);
}
let bytes = [d, structio::beve::header::TRUE];
let got = dribble::<bool>(Mode::Values, &bytes);
assert_eq!(got.len(), 1);
assert!(got[0].as_ref().unwrap());
assert_eq!(
structio::validate_beve(&bytes).unwrap_err().code,
ErrorCode::InvalidHeader
);
}
#[test]
fn a_delimiter_where_a_value_belongs_is_a_framing_failure() {
let d = structio::beve::header::DELIMITER;
for wrap in &WRAPS {
let bytes = nested(wrap, 1, &[d]);
let mut feed = Feed::values();
feed.push(&bytes);
feed.end();
let err = feed.next_value::<Any>().unwrap().unwrap_err();
assert_eq!(
err.as_parse().unwrap().code,
ErrorCode::InvalidHeader,
"{}",
wrap.name
);
assert_eq!(feed.offset(), 0, "{}", wrap.name);
assert_eq!(
structio::validate_beve(&bytes).unwrap_err().code,
ErrorCode::InvalidHeader,
"{}",
wrap.name
);
}
let bytes = [
structio::beve::header::GENERIC_ARRAY,
2 << 2,
structio::beve::header::NULL,
d,
];
let got = dribble::<Any>(Mode::Array, &bytes);
assert_eq!(got.len(), 2);
assert!(got[0].is_ok());
assert_eq!(
got[1].as_ref().unwrap_err().as_parse().unwrap().code,
ErrorCode::InvalidHeader
);
}
#[test]
fn a_generic_array_claiming_more_elements_than_it_has_ends_in_an_error() {
let mut bytes = structio::to_beve(&vec![Small { id: 1 }, Small { id: 2 }]);
bytes[1] = 3 << 2;
let mut docs = Documents::array(&bytes[..]);
let got: Vec<Result<Small, _>> = docs.iter::<Small>().collect();
assert_eq!(got.len(), 3);
assert_eq!(
got[2].as_ref().unwrap_err().as_parse().unwrap().code,
ErrorCode::UnexpectedEnd
);
}
#[test]
fn an_empty_stream_is_no_values_and_no_error() {
for mode in [Mode::Values, Mode::Array] {
let mut docs = small(Documents::new(&b""[..], mode));
assert!(docs.next_value::<Small>().is_none(), "{mode:?}");
assert!(structio::from_beve::<Small>(b"").is_err());
}
}
#[test]
fn a_generic_array_hands_out_its_elements() {
let n = records(50);
let all: Vec<Rec> = (0..n).map(sample).collect();
let bytes = structio::to_beve(&all);
let mut docs = Documents::array(&bytes[..]);
let got: Vec<Rec> = docs.iter::<Rec>().map(Result::unwrap).collect();
assert_eq!(got, all);
}
#[test]
fn a_generic_array_arriving_one_byte_at_a_time_reads_the_same() {
let n = records(20);
let all: Vec<Rec> = (0..n).map(sample).collect();
let bytes = structio::to_beve(&all);
let got = dribble::<Rec>(Mode::Array, &bytes);
assert_eq!(got.len(), all.len());
for (value, want) in got.into_iter().zip(&all) {
assert_eq!(&value.unwrap(), want);
}
}
#[test]
fn a_typed_numeric_array_hands_out_its_elements() {
let all: Vec<f64> = (0..100).map(|i| i as f64 * 0.25).collect();
let bytes = structio::to_beve(&all);
let mut docs = Documents::array(&bytes[..]);
let got: Vec<f64> = docs.iter::<f64>().map(Result::unwrap).collect();
assert_eq!(got, all);
}
#[test]
fn a_typed_array_of_every_width_hands_out_its_elements() {
assert_eq!(
elements::<u8>(&structio::to_beve(&vec![1u8, 2, 255])),
[1, 2, 255]
);
assert_eq!(
elements::<i64>(&structio::to_beve(&vec![-1i8, 0, 127])),
[-1, 0, 127]
);
assert_eq!(
elements::<u32>(&structio::to_beve(&vec![1u16, 65535])),
[1, 65535]
);
assert_eq!(
elements::<i64>(&structio::to_beve(&vec![i32::MIN, 7])),
[i32::MIN as i64, 7]
);
assert_eq!(
elements::<f64>(&structio::to_beve(&vec![1.5f32, -2.5])),
[1.5, -2.5]
);
assert_eq!(
elements::<u64>(&structio::to_beve(&vec![u64::MAX, 0])),
[u64::MAX, 0]
);
}
fn elements<T>(bytes: &[u8]) -> Vec<T>
where
T: for<'de> beve::Read<'de> + Default,
{
small(Documents::array(bytes))
.iter::<T>()
.map(Result::unwrap)
.collect()
}
fn small<R: io::Read>(docs: Documents<R>) -> Documents<R> {
docs.read_size(64)
}
#[test]
fn a_packed_boolean_array_hands_out_its_bits() {
for n in 0..40usize {
let all: Vec<bool> = (0..n).map(|i| i % 3 == 0).collect();
let bytes = structio::to_beve(&all);
assert_eq!(elements::<bool>(&bytes), all, "{n} booleans");
}
}
#[test]
fn a_typed_string_array_hands_out_its_strings() {
let all: Vec<String> = vec!["".into(), "a".into(), "\u{20ac}\u{1f600}".into()];
let bytes = structio::to_beve(&all);
assert_eq!(elements::<String>(&bytes), all);
}
fn aligned(values: &[f64]) -> Vec<u8> {
let mut bytes = vec![
structio::beve::header::ALIGNED_ARRAY,
structio::beve::header::array_of(structio::beve::header::CAT_FLOAT, 3),
];
let mut size = [0u8; 8];
let used = structio::beve::header::encode_size(values.len() as u64, &mut size);
bytes.extend_from_slice(&size[..used]);
bytes.push(5);
bytes.extend_from_slice(&[0; 5]);
for v in values {
bytes.extend_from_slice(&v.to_le_bytes());
}
bytes
}
#[test]
fn an_aligned_typed_array_hands_out_its_elements() {
let values = [1.5f64, 2.5, 3.5];
let bytes = aligned(&values);
assert!(structio::validate_beve(&bytes).is_ok());
assert_eq!(elements::<f64>(&bytes), values);
}
#[test]
fn a_typed_array_arriving_one_byte_at_a_time_reads_the_same() {
for bytes in [
structio::to_beve(&(0..30).map(|i| i as f64).collect::<Vec<f64>>()),
structio::to_beve(&(0..30).map(|i| i % 5 == 0).collect::<Vec<bool>>()),
structio::to_beve(&(0..30).map(|i| i.to_string()).collect::<Vec<String>>()),
] {
let got = dribble::<Any>(Mode::Array, &bytes);
assert_eq!(got.len(), 30, "{bytes:02x?}");
}
}
#[test]
fn an_empty_array_is_no_values_and_no_error() {
for bytes in [
structio::to_beve(&Vec::<Rec>::new()),
structio::to_beve(&Vec::<f64>::new()),
structio::to_beve(&Vec::<bool>::new()),
structio::to_beve(&Vec::<String>::new()),
] {
let mut docs = small(Documents::array(&bytes[..]));
assert!(docs.next_value::<Small>().is_none(), "{bytes:02x?}");
}
}
#[test]
fn an_array_claiming_more_elements_than_it_has_ends_in_an_error() {
let mut bytes = structio::to_beve(&vec![1u32, 2, 3]);
bytes[1] = 4 << 2;
let mut docs = Documents::array(&bytes[..]);
let got: Vec<Result<u32, _>> = docs.iter::<u32>().collect();
assert_eq!(got.len(), 4);
assert_eq!(*got[0].as_ref().unwrap(), 1);
assert_eq!(
got[3].as_ref().unwrap_err().as_parse().unwrap().code,
ErrorCode::UnexpectedEnd
);
}
#[test]
fn a_top_level_value_that_is_not_an_array_is_refused() {
let bytes = structio::to_beve(&Small { id: 1 });
let mut docs = Documents::array(&bytes[..]);
let err = docs.next_value::<Small>().unwrap().unwrap_err();
assert_eq!(err.as_parse().unwrap().code, ErrorCode::ExpectedArray);
}
#[test]
fn bytes_after_the_array_are_trailing_content() {
let mut bytes = structio::to_beve(&vec![1u32, 2, 3]);
bytes.extend(structio::to_beve(&Small { id: 9 }));
let mut docs = Documents::array(&bytes[..]);
let got: Vec<Result<u32, _>> = docs.iter::<u32>().collect();
assert_eq!(got.len(), 4);
assert_eq!(
got[3].as_ref().unwrap_err().as_parse().unwrap().code,
ErrorCode::TrailingContent
);
}
#[test]
fn the_window_stays_bounded_over_a_long_stream() {
let n = records(2000);
let all: Vec<Small> = (0..n).map(|id| Small { id }).collect();
let bytes = structio::to_beve(&all);
assert!(bytes.len() > 8000 || cfg!(miri));
let mut docs = Documents::array(&bytes[..]).read_size(64);
let mut peak = 0;
let mut count = 0;
while let Some(value) = docs.next_value_into(&mut Small::default()) {
value.unwrap();
peak = peak.max(docs.buffered());
count += 1;
}
assert_eq!(count, n);
assert!(peak < 1024, "buffered {peak} bytes at peak");
}
#[test]
fn a_typed_array_larger_than_the_window_streams_anyway() {
let n = if cfg!(miri) { 200 } else { 20_000 };
let all: Vec<f64> = (0..n).map(|i| i as f64).collect();
let bytes = structio::to_beve(&all);
let mut docs = Documents::array(&bytes[..]).read_size(64);
let mut peak = 0;
let mut sum = 0.0;
while let Some(value) = docs.next_value::<f64>() {
sum += value.unwrap();
peak = peak.max(docs.buffered());
}
assert_eq!(sum, all.iter().sum::<f64>());
assert!(peak < 1024, "buffered {peak} bytes at peak");
}
#[test]
fn a_borrowed_field_points_into_the_window() {
let bytes = structio::to_beve(&vec![Borrowed { tag: "first" }, Borrowed { tag: "second" }]);
let mut docs = Documents::array(&bytes[..]);
let first: Borrowed = docs.next_value().unwrap().unwrap();
assert_eq!(first.tag, "first");
let second: Borrowed = docs.next_value().unwrap().unwrap();
assert_eq!(second.tag, "second");
}
#[test]
fn reading_into_an_existing_value_keeps_its_allocations() {
let n = records(30);
let all: Vec<Rec> = (0..n).map(sample).collect();
let bytes = structio::to_beve(&all);
let mut docs = Documents::array(&bytes[..]);
let mut value = Rec::default();
let mut count = 0;
while let Some(result) = docs.next_value_into(&mut value) {
result.unwrap();
assert_eq!(value, all[count]);
count += 1;
}
assert_eq!(count as u64, n);
}
#[test]
fn the_offset_tracks_the_stream_rather_than_the_window() {
let bytes = concatenated(records(30));
let mut docs = Documents::values(&bytes[..]).read_size(7);
assert_eq!(docs.offset(), 0);
let one = structio::to_beve(&sample(0));
docs.next_value_into(&mut Rec::default()).unwrap().unwrap();
assert_eq!(docs.offset(), one.len());
docs.next_value_into(&mut Rec::default()).unwrap().unwrap();
assert_eq!(
docs.offset(),
one.len() + structio::to_beve(&sample(1)).len()
);
}
#[test]
fn into_parts_hands_back_what_was_read_but_not_used() {
let bytes = concatenated(3);
let mut docs = Documents::values(&bytes[..]);
docs.next_value_into(&mut Rec::default()).unwrap().unwrap();
let (rest, unread) = docs.into_parts();
let mut tail = unread;
let mut remaining = Vec::new();
let mut rest = rest;
rest.read_to_end(&mut remaining).unwrap();
tail.extend(remaining);
assert_eq!(tail, bytes[structio::to_beve(&sample(0)).len()..]);
}
#[test]
fn a_record_that_does_not_match_the_type_is_reported_and_skipped() {
let mut bytes = vec![structio::beve::header::GENERIC_ARRAY];
let mut size = [0u8; 8];
let used = structio::beve::header::encode_size(3, &mut size);
bytes.extend_from_slice(&size[..used]);
bytes.extend(structio::to_beve(&Small { id: 1 }));
bytes.extend(structio::to_beve(&String::from("not a record")));
bytes.extend(structio::to_beve(&Small { id: 3 }));
assert!(structio::validate_beve(&bytes).is_ok());
let mut docs = Documents::array(&bytes[..]);
let got: Vec<Result<Small, _>> = docs.iter::<Small>().collect();
assert_eq!(got.len(), 3);
assert_eq!(got[0].as_ref().unwrap().id, 1);
assert_eq!(
got[1].as_ref().unwrap_err().as_parse().unwrap().code,
ErrorCode::ExpectedObject
);
assert_eq!(got[2].as_ref().unwrap().id, 3);
}
#[test]
fn a_truncated_document_is_an_unexpected_end() {
let bytes = structio::to_beve(&sample(0));
for cut in 1..bytes.len() {
let mut docs = small(Documents::values(&bytes[..cut]));
let err = docs
.next_value_into(&mut Rec::default())
.unwrap()
.unwrap_err();
assert_eq!(
err.as_parse().unwrap().code,
ErrorCode::UnexpectedEnd,
"cut at {cut}"
);
assert!(docs.next_value_into(&mut Rec::default()).is_none());
}
}
#[test]
fn a_framing_failure_is_reported_once_and_ends_the_stream() {
let bytes = [0b0000_0111u8, 0, 0, 0];
let mut docs = Documents::values(&bytes[..]);
let err = docs.next_value::<Small>().unwrap().unwrap_err();
assert_eq!(err.as_parse().unwrap().code, ErrorCode::InvalidHeader);
assert!(docs.next_value::<Small>().is_none());
assert_eq!(docs.buffered(), 0);
}
#[test]
fn a_length_the_producer_never_delivers_hits_the_limit() {
let mut bytes = vec![structio::beve::header::STRING];
let mut size = [0u8; 8];
let used = structio::beve::header::encode_size(1 << 20, &mut size);
bytes.extend_from_slice(&size[..used]);
let mut feed = Feed::values().max_value(4096);
feed.push(&bytes);
feed.push(&vec![b'x'; 8192]);
let err = feed.next_value::<String>().unwrap().unwrap_err();
assert_eq!(err.as_parse().unwrap().code, ErrorCode::DocumentTooLarge);
}
#[test]
fn a_limit_does_not_stop_a_stream_of_small_values() {
let bytes = concatenated(records(30));
let largest = (0..records(30))
.map(|i| structio::to_beve(&sample(i)).len())
.max()
.unwrap();
let mut docs = Documents::values(&bytes[..]).max_value(largest);
let got: Vec<Rec> = docs.iter::<Rec>().map(Result::unwrap).collect();
assert_eq!(got.len() as u64, records(30));
}
#[test]
fn pushing_at_a_dead_feed_cannot_grow_it() {
let mut feed = Feed::values();
feed.push(&[0b0000_0111]);
assert!(feed.next_value::<Small>().unwrap().is_err());
for _ in 0..100 {
feed.push(&vec![0u8; 1024]);
}
assert_eq!(feed.buffered(), 0);
assert!(feed.next_value::<Small>().is_none());
}
struct Wrap {
name: &'static str,
open: &'static [u8],
close: &'static [u8],
}
const EXTENSION: u8 = structio::beve::header::TY_EXTENSION;
const TYPE_TAG: u8 = (structio::beve::header::EXT_TYPE_TAG << 3) | EXTENSION;
const MATRIX: u8 = (structio::beve::header::EXT_MATRIX << 3) | EXTENSION;
const COMPLEX: u8 = (structio::beve::header::EXT_COMPLEX << 3) | EXTENSION;
const WRAPS: [Wrap; 4] = [
Wrap {
name: "generic array",
open: &[structio::beve::header::GENERIC_ARRAY, 1 << 2],
close: &[],
},
Wrap {
name: "object",
open: &[structio::beve::header::OBJECT, 1 << 2, 0],
close: &[],
},
Wrap {
name: "type tag",
open: &[TYPE_TAG, 0],
close: &[],
},
Wrap {
name: "matrix",
open: &[MATRIX, 0],
close: &[structio::beve::header::NULL],
},
];
fn depths() -> Vec<usize> {
let limit = structio::beve::reader::MAX_DEPTH as usize;
if cfg!(miri) {
vec![0, 1, 2, limit - 2, limit - 1, limit, limit + 1]
} else {
(0..limit + 2).collect()
}
}
fn nested(wrap: &Wrap, depth: usize, inner: &[u8]) -> Vec<u8> {
let mut bytes = Vec::new();
for _ in 0..depth {
bytes.extend_from_slice(wrap.open);
}
bytes.extend_from_slice(inner);
for _ in 0..depth {
bytes.extend_from_slice(wrap.close);
}
bytes
}
fn frames(bytes: &[u8]) -> bool {
let mut feed = Feed::values();
feed.push(bytes);
feed.end();
let read = feed.next_value::<Any>();
let framed = feed.offset() == bytes.len();
if framed {
assert!(
read.is_some_and(|r| r.is_ok()),
"framed but not readable: {bytes:02x?}"
);
}
framed
}
#[test]
fn nesting_past_the_limit_is_a_framing_failure() {
let deepest = structio::beve::reader::MAX_DEPTH as usize;
let null = [structio::beve::header::NULL];
let mut feed = Feed::values();
feed.push(&nested(&WRAPS[0], deepest + 1, &null));
feed.end();
let err = feed.next_value::<Small>().unwrap().unwrap_err();
assert_eq!(err.as_parse().unwrap().code, ErrorCode::ExceededMaxDepth);
assert_eq!(feed.offset(), 0);
}
#[test]
fn the_splitter_frames_exactly_what_the_reader_reads() {
let inners = [
vec![structio::beve::header::NULL],
structio::to_beve(&vec![1u8, 2, 3]),
];
for wrap in &WRAPS {
for inner in &inners {
for depth in depths() {
let bytes = nested(wrap, depth, inner);
assert_eq!(
frames(&bytes),
structio::validate_beve(&bytes).is_ok(),
"{depth} of {} around {inner:02x?}",
wrap.name
);
}
}
}
}
#[test]
fn the_extensions_are_framed_even_though_they_are_not_read() {
let pair = structio::beve::header::number(structio::beve::header::CAT_FLOAT, 3) & !0b111;
let mut docs: Vec<Vec<u8>> = vec![
vec![TYPE_TAG, 0, structio::beve::header::TRUE],
[&[COMPLEX, pair][..], &[0; 16]].concat(),
[&[COMPLEX, pair | 1, 2 << 2][..], &[0; 32]].concat(),
];
docs.push(
[
&[MATRIX, 0][..],
&structio::to_beve(&vec![2u32, 3]),
&structio::to_beve(&vec![1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0]),
]
.concat(),
);
for bytes in docs {
assert!(structio::validate_beve(&bytes).is_ok(), "{bytes:02x?}");
assert!(frames(&bytes), "{bytes:02x?}");
assert_eq!(
dribble::<Any>(Mode::Values, &bytes).len(),
1,
"{bytes:02x?}"
);
}
}
#[test]
fn an_io_failure_is_an_io_error_rather_than_a_parse_error() {
struct Broken;
impl io::Read for Broken {
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new(io::ErrorKind::ConnectionReset, "gone"))
}
}
let mut docs = Documents::values(Broken);
let err = docs
.next_value_into(&mut Small::default())
.unwrap()
.unwrap_err();
assert!(matches!(err, StreamError::Io(_)));
assert_eq!(err.as_io().unwrap().kind(), io::ErrorKind::ConnectionReset);
}
#[test]
fn an_interrupted_read_is_retried() {
struct Flaky<'a> {
data: &'a [u8],
interrupt: bool,
}
impl io::Read for Flaky<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.interrupt = !self.interrupt;
if self.interrupt {
return Err(io::Error::new(io::ErrorKind::Interrupted, "again"));
}
let n = 1.min(buf.len()).min(self.data.len());
buf[..n].copy_from_slice(&self.data[..n]);
self.data = &self.data[n..];
Ok(n)
}
}
let bytes = concatenated(3);
let mut docs = Documents::values(Flaky {
data: &bytes,
interrupt: false,
});
let got: Vec<Rec> = docs.iter::<Rec>().map(Result::unwrap).collect();
assert_eq!(got.len(), 3);
}
#[test]
fn every_document_shape_streams_to_what_it_slurps_to() {
let mut docs: Vec<Vec<u8>> = vec![
structio::to_beve(&()),
structio::to_beve(&true),
structio::to_beve(&0u8),
structio::to_beve(&-1i64),
structio::to_beve(&1.5f32),
structio::to_beve(&f64::NAN),
structio::to_beve(&"text"),
structio::to_beve(&String::new()),
structio::to_beve(&vec![1u8, 2, 3]),
structio::to_beve(&vec![true, false, true]),
structio::to_beve(&vec!["a".to_string(), "b".into()]),
structio::to_beve(&Vec::<Rec>::new()),
structio::to_beve(&sample(7)),
structio::to_beve(&vec![sample(1), sample(2)]),
aligned(&[1.5, 2.5, 3.5]),
];
let mut map: BTreeMap<u16, Vec<f64>> = BTreeMap::new();
map.insert(1, vec![1.0]);
map.insert(65535, vec![]);
docs.push(structio::to_beve(&map));
let mut names: BTreeMap<String, Small> = BTreeMap::new();
names.insert("a".into(), Small { id: 1 });
docs.push(structio::to_beve(&names));
for bytes in docs {
assert!(structio::validate_beve(&bytes).is_ok(), "{bytes:02x?}");
assert!(frames(&bytes), "{bytes:02x?}");
let got = dribble::<Any>(Mode::Values, &bytes);
assert_eq!(got.len(), 1, "{bytes:02x?}");
assert!(got[0].is_ok(), "{bytes:02x?}");
}
}