use std::io::Write;
use std::ops::Range;
use std::{io, mem};
use bytes::BufMut;
use num_bigint::Sign;
use num_traits::Zero;
use crate::binary::constants::v1_0::IVM;
use crate::binary::uint::DecodedUInt;
use crate::binary::var_uint::VarUInt;
use crate::raw_symbol_token_ref::{AsRawSymbolTokenRef, RawSymbolTokenRef};
use crate::result::{illegal_operation, IonResult};
use crate::types::{ContainerType, Decimal, SymbolId, Timestamp};
use crate::writer::IonWriter;
use crate::{Int, IonType};
use super::decimal::DecimalBinaryEncoder;
use super::timestamp::TimestampBinaryEncoder;
use super::uint;
pub struct RawBinaryWriterBuilder {
}
impl RawBinaryWriterBuilder {
pub fn new() -> Self {
RawBinaryWriterBuilder {}
}
pub fn build<W: Write>(self, out: W) -> IonResult<RawBinaryWriter<W>> {
let mut levels = Vec::with_capacity(INITIAL_ENCODING_LEVELS_CAPACITY);
levels.push(EncodingLevel::new(ContainerType::TopLevel, None, 0, 0));
let mut io_ranges = Vec::with_capacity(INITIAL_IO_RANGE_CAPACITY);
io_ranges.push(0usize..0);
let raw_binary_writer = RawBinaryWriter {
buffer: Vec::with_capacity(INITIAL_ENCODING_BUFFER_CAPACITY),
io_ranges,
levels,
out,
annotations_all_levels: Vec::with_capacity(INITIAL_ANNOTATIONS_CAPACITY),
num_annotations_current_value: 0,
field_id: None,
contiguous_encoding: Vec::with_capacity(INITIAL_ENCODING_BUFFER_CAPACITY),
};
Ok(raw_binary_writer)
}
}
impl Default for RawBinaryWriterBuilder {
fn default() -> Self {
RawBinaryWriterBuilder::new()
}
}
type IoRange = Range<usize>;
#[derive(Debug)]
struct EncodingLevel {
container_type: ContainerType,
field_id: Option<SymbolId>,
num_annotations: u8,
td_io_range_index: usize,
}
impl EncodingLevel {
fn new(
container_type: ContainerType,
field_id: Option<SymbolId>,
num_annotations: u8,
td_io_range_index: usize,
) -> EncodingLevel {
EncodingLevel {
container_type,
field_id,
num_annotations,
td_io_range_index,
}
}
fn calculate_final_size(&self, io_ranges: &mut [Range<usize>]) -> usize {
io_ranges[self.td_io_range_index..]
.iter()
.map(|r| r.len())
.sum()
}
}
#[derive(Debug)]
pub struct RawBinaryWriter<W: Write> {
buffer: Vec<u8>,
io_ranges: Vec<IoRange>,
levels: Vec<EncodingLevel>,
out: W,
field_id: Option<SymbolId>,
annotations_all_levels: Vec<SymbolId>,
num_annotations_current_value: u8,
contiguous_encoding: Vec<u8>,
}
pub(crate) const MAX_INLINE_LENGTH: usize = 13;
const IO_RANGES_PER_ANNOTATION_WRAPPER: usize = 3;
const INITIAL_ENCODING_BUFFER_CAPACITY: usize = 8 * 1024;
const INITIAL_ENCODING_LEVELS_CAPACITY: usize = 16;
const INITIAL_IO_RANGE_CAPACITY: usize = 128;
const INITIAL_ANNOTATIONS_CAPACITY: usize = 4;
impl<W: Write> RawBinaryWriter<W> {
#[inline]
fn encode_to_buffer(
&mut self,
mut encode_fn: impl FnMut(&mut Self) -> IonResult<()>,
) -> IonResult<IoRange> {
let start = self.buffer.len();
encode_fn(self)?;
let end = self.buffer.len();
Ok(start..end)
}
#[inline]
fn is_in_struct(&self) -> bool {
self.levels
.last()
.map(|level| level.container_type == ContainerType::Struct)
.unwrap_or(false)
}
#[inline]
fn extend_last_range(&mut self, number_of_bytes: usize) {
let last_range = self
.io_ranges
.last_mut()
.expect("io_ranges unexpectedly empty.");
last_range.end += number_of_bytes;
}
fn write_scalar(
&mut self,
mut write_fn: impl FnMut(&mut Vec<u8>) -> IonResult<()>,
) -> IonResult<()> {
if self.is_in_struct() {
let field_id = self.expect_field_id()? as u64;
let bytes_written = VarUInt::write_u64(&mut self.buffer, field_id)?;
self.extend_last_range(bytes_written);
self.field_id = None;
}
if self.has_annotations() {
return self.encode_annotated_scalar(write_fn);
}
let encoded_range = self.encode_to_buffer(|writer| write_fn(&mut writer.buffer))?;
self.extend_last_range(encoded_range.len());
Ok(())
}
fn encode_annotated_scalar(
&mut self,
mut scalar_write_fn: impl FnMut(&mut Vec<u8>) -> IonResult<()>,
) -> IonResult<()> {
let value_io_range: IoRange =
self.encode_to_buffer(|writer| scalar_write_fn(&mut writer.buffer))?;
let mut header_io_range: Range<usize> = 0..0;
let mut annotations_seq_length_io_range: Range<usize> = 0..0;
let mut annotations_seq_io_range: Range<usize> = 0..0;
self.encode_annotation_wrapper(
&mut header_io_range,
&mut annotations_seq_length_io_range,
&mut annotations_seq_io_range,
value_io_range.len(),
)?;
self.io_ranges.extend_from_slice(&[
header_io_range,
annotations_seq_length_io_range,
annotations_seq_io_range,
value_io_range,
]);
self.push_empty_io_range();
Ok(())
}
fn encode_annotation_wrapper(
&mut self,
header_io_range: &mut IoRange,
annotations_seq_length_io_range: &mut IoRange,
annotations_seq_io_range: &mut IoRange,
wrapped_value_length: usize,
) -> IonResult<()> {
let _ = mem::replace(
annotations_seq_io_range,
self.encode_to_buffer(|writer| {
let range = writer.current_value_annotations_range();
let annotations = &writer.annotations_all_levels[range];
for annotation_id in annotations {
VarUInt::write_u64(&mut writer.buffer, *annotation_id as u64)?;
}
Ok(())
})?,
);
let annotation_sequence_encoded_length = annotations_seq_io_range.len();
let _ = mem::replace(
annotations_seq_length_io_range,
self.encode_to_buffer(|writer| {
let _num_bytes = VarUInt::write_u64(
&mut writer.buffer,
annotation_sequence_encoded_length as u64,
)?;
Ok(())
})?,
);
let wrapper_length = annotations_seq_io_range.len()
+ annotations_seq_length_io_range.len()
+ wrapped_value_length;
let _ = mem::replace(
header_io_range,
self.encode_to_buffer(|writer| {
let type_descriptor: u8;
if wrapper_length <= MAX_INLINE_LENGTH {
type_descriptor = 0xE0 | wrapper_length as u8;
writer.buffer.push(type_descriptor);
} else {
type_descriptor = 0xEE; writer.buffer.push(type_descriptor);
VarUInt::write_u64(&mut writer.buffer, wrapper_length as u64)?;
}
Ok(())
})?,
);
self.clear_annotations();
Ok(())
}
#[inline]
fn current_value_annotations_range(&self) -> Range<usize> {
let end = self.annotations_all_levels.len();
let start = end - self.num_annotations_current_value as usize;
start..end
}
#[inline]
pub fn clear_annotations(&mut self) {
if self.num_annotations_current_value > 0 {
let new_length =
self.annotations_all_levels.len() - self.num_annotations_current_value as usize;
self.annotations_all_levels.truncate(new_length);
self.num_annotations_current_value = 0;
}
}
#[inline]
pub fn has_annotations(&self) -> bool {
self.num_annotations_current_value > 0
}
pub fn write_symbol_id(&mut self, symbol_id: SymbolId) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
const SYMBOL_BUFFER_SIZE: usize = mem::size_of::<u64>();
let mut buffer = [0u8; SYMBOL_BUFFER_SIZE];
let mut writer = io::Cursor::new(&mut buffer).writer();
let encoded_length = DecodedUInt::write_u64(&mut writer, symbol_id as u64)?;
let type_descriptor: u8;
if encoded_length <= MAX_INLINE_LENGTH {
type_descriptor = 0x70 | encoded_length as u8;
enc_buffer.push(type_descriptor);
} else {
type_descriptor = 0x7E;
enc_buffer.push(type_descriptor);
VarUInt::write_u64(enc_buffer, encoded_length as u64)?;
}
let raw_buffer = writer.into_inner().into_inner();
enc_buffer.extend_from_slice(&raw_buffer[..encoded_length]);
Ok(())
})
}
fn write_lob(enc_buffer: &mut Vec<u8>, value: &[u8], type_code: u8) -> IonResult<()> {
let encoded_length = value.len();
let type_descriptor: u8;
if encoded_length <= MAX_INLINE_LENGTH {
type_descriptor = type_code | encoded_length as u8;
enc_buffer.push(type_descriptor);
} else {
type_descriptor = type_code | 0x0E;
enc_buffer.push(type_descriptor);
VarUInt::write_u64(enc_buffer, encoded_length as u64)?;
}
enc_buffer.extend_from_slice(value);
Ok(())
}
fn push_empty_io_range(&mut self) {
let next_byte_index = self.buffer.len();
self.io_ranges.push(next_byte_index..next_byte_index);
}
pub fn set_field_id(&mut self, field_id: SymbolId) {
self.field_id = Some(field_id);
}
fn expect_field_id(&self) -> IonResult<usize> {
match self.field_id {
Some(field_id) => Ok(field_id),
None => {
illegal_operation("`set_field_id()` must be called before each field in a struct.")
}
}
}
fn encode_container_annotations(
&mut self,
td_io_range_index: usize,
container_size: usize,
) -> IonResult<()> {
let mut header_io_range: Range<usize> = 0..0;
let mut annotations_seq_length_io_range: Range<usize> = 0..0;
let mut annotations_seq_io_range: Range<usize> = 0..0;
self.encode_annotation_wrapper(
&mut header_io_range,
&mut annotations_seq_length_io_range,
&mut annotations_seq_io_range,
container_size,
)?;
let header_io_range_index = td_io_range_index - IO_RANGES_PER_ANNOTATION_WRAPPER;
let _ = mem::replace(&mut self.io_ranges[header_io_range_index], header_io_range);
let annotations_seq_length_io_range_index = header_io_range_index + 1;
let _ = mem::replace(
&mut self.io_ranges[annotations_seq_length_io_range_index],
annotations_seq_length_io_range,
);
let annotations_seq_io_range_index = header_io_range_index + 2;
let _ = mem::replace(
&mut self.io_ranges[annotations_seq_io_range_index],
annotations_seq_io_range,
);
Ok(())
}
pub fn output(&self) -> &W {
&self.out
}
pub fn output_mut(&mut self) -> &mut W {
&mut self.out
}
fn reserve_io_ranges_for_annotations(&mut self) {
self.push_empty_io_range();
self.push_empty_io_range();
self.push_empty_io_range();
}
pub fn add_annotation<A: AsRawSymbolTokenRef>(&mut self, annotation: A) {
let symbol_id = match annotation.as_raw_symbol_token_ref() {
RawSymbolTokenRef::SymbolId(symbol_id) => symbol_id,
RawSymbolTokenRef::Text(text) => panic!(
"The RawBinaryWriter can only accept symbol ID annotations, not text ('{text}')."
),
};
self.annotations_all_levels.push(symbol_id);
self.num_annotations_current_value += 1;
}
}
impl<W: Write> IonWriter for RawBinaryWriter<W> {
type Output = W;
fn ion_version(&self) -> (u8, u8) {
(1, 0)
}
fn write_ion_version_marker(&mut self, major: u8, minor: u8) -> IonResult<()> {
if self.depth() > 0 {
return illegal_operation("can only write an IVM at the top level");
}
if major == 1 && minor == 0 {
return Ok(self.out.write_all(&IVM)?);
}
illegal_operation("Only Ion 1.0 is supported.")
}
fn supports_text_symbol_tokens(&self) -> bool {
false
}
fn set_annotations<I, A>(&mut self, annotations: I)
where
A: AsRawSymbolTokenRef,
I: IntoIterator<Item = A>,
{
self.clear_annotations();
for annotation in annotations {
self.add_annotation(annotation);
}
}
fn write_null(&mut self, ion_type: IonType) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
let byte: u8 = match ion_type {
IonType::Null => 0x0F,
IonType::Bool => 0x1F,
IonType::Int => 0x2F,
IonType::Float => 0x4F,
IonType::Decimal => 0x5F,
IonType::Timestamp => 0x6F,
IonType::Symbol => 0x7F,
IonType::String => 0x8F,
IonType::Clob => 0x9F,
IonType::Blob => 0xAF,
IonType::List => 0xBF,
IonType::SExp => 0xCF,
IonType::Struct => 0xDF,
};
enc_buffer.push(byte);
Ok(())
})
}
fn write_bool(&mut self, value: bool) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
let byte: u8 = if value { 0x11 } else { 0x10 };
enc_buffer.push(byte);
Ok(())
})
}
fn write_i64(&mut self, value: i64) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
let magnitude: u64 = value.unsigned_abs();
let encoded = uint::encode_u64(magnitude);
let bytes_to_write = encoded.as_bytes();
let encoded_length = bytes_to_write.len();
let type_descriptor: u8 = if value >= 0 {
0x20 | (encoded_length as u8)
} else {
0x30 | (encoded_length as u8)
};
enc_buffer.push(type_descriptor);
enc_buffer.extend_from_slice(bytes_to_write);
Ok(())
})
}
fn write_int(&mut self, value: &Int) -> IonResult<()> {
let value = match value {
Int::I64(i) => return self.write_i64(*i),
Int::BigInt(i) => i,
};
self.write_scalar(|enc_buffer| {
if value.is_zero() {
enc_buffer.push(0x20);
return Ok(());
}
let (sign, magnitude_be_bytes) = value.to_bytes_be();
let mut type_descriptor: u8 = match sign {
Sign::Plus | Sign::NoSign => 0x20,
Sign::Minus => 0x30,
};
let encoded_length = magnitude_be_bytes.len();
if encoded_length <= 13 {
type_descriptor |= encoded_length as u8;
enc_buffer.push(type_descriptor);
} else {
type_descriptor |= 0xEu8;
enc_buffer.push(type_descriptor);
VarUInt::write_u64(enc_buffer, encoded_length as u64)?;
}
enc_buffer.extend_from_slice(magnitude_be_bytes.as_slice());
Ok(())
})
}
fn write_f32(&mut self, value: f32) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
if value == 0f32 && !value.is_sign_negative() {
enc_buffer.push(0x40);
return Ok(());
}
enc_buffer.push(0x44);
enc_buffer.extend_from_slice(&value.to_be_bytes());
Ok(())
})
}
fn write_f64(&mut self, value: f64) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
if value == 0f64 && !value.is_sign_negative() {
enc_buffer.push(0x40);
return Ok(());
}
enc_buffer.push(0x48);
enc_buffer.extend_from_slice(&value.to_be_bytes());
Ok(())
})
}
fn write_decimal(&mut self, value: &Decimal) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
let _ = enc_buffer.encode_decimal_value(value)?;
Ok(())
})
}
fn write_timestamp(&mut self, value: &Timestamp) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
let _ = enc_buffer.encode_timestamp_value(value)?;
Ok(())
})
}
fn write_symbol<A: AsRawSymbolTokenRef>(&mut self, value: A) -> IonResult<()> {
match value.as_raw_symbol_token_ref() {
RawSymbolTokenRef::SymbolId(sid) => self.write_symbol_id(sid),
RawSymbolTokenRef::Text(_text) => {
illegal_operation("The RawBinaryWriter cannot write text symbols.")
}
}
}
fn write_string<S: AsRef<str>>(&mut self, value: S) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
let text: &str = value.as_ref();
let encoded_length = text.len(); let type_descriptor: u8;
if encoded_length <= MAX_INLINE_LENGTH {
type_descriptor = 0x80 | encoded_length as u8;
enc_buffer.push(type_descriptor);
} else {
type_descriptor = 0x8E;
enc_buffer.push(type_descriptor);
VarUInt::write_u64(enc_buffer, encoded_length as u64)?;
}
enc_buffer.extend_from_slice(text.as_bytes());
Ok(())
})
}
fn write_clob<A: AsRef<[u8]>>(&mut self, value: A) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
let bytes: &[u8] = value.as_ref();
RawBinaryWriter::<W>::write_lob(enc_buffer, bytes, 0x90)
})
}
fn write_blob<A: AsRef<[u8]>>(&mut self, value: A) -> IonResult<()> {
self.write_scalar(|enc_buffer| {
let bytes: &[u8] = value.as_ref();
RawBinaryWriter::<W>::write_lob(enc_buffer, bytes, 0xA0)
})
}
fn step_in(&mut self, ion_type: IonType) -> IonResult<()> {
use IonType::*;
let container_type = match ion_type {
List => ContainerType::List,
SExp => ContainerType::SExpression,
Struct => ContainerType::Struct,
_ => return illegal_operation("Cannot step into a scalar Ion type."),
};
if self.is_in_struct() {
let field_id_io_range = self.encode_to_buffer(|writer| {
let field_id = writer.expect_field_id()? as u64;
VarUInt::write_u64(&mut writer.buffer, field_id)?;
Ok(())
})?;
self.extend_last_range(field_id_io_range.len());
}
if self.num_annotations_current_value > 0 {
self.reserve_io_ranges_for_annotations();
}
let header_io_range_index = self.io_ranges.len();
self.push_empty_io_range();
let new_encoding_level = EncodingLevel::new(
container_type,
self.field_id,
self.num_annotations_current_value,
header_io_range_index,
);
self.num_annotations_current_value = 0;
self.levels.push(new_encoding_level);
self.push_empty_io_range(); Ok(())
}
fn set_field_name<A: AsRawSymbolTokenRef>(&mut self, name: A) {
if self.parent_type() != Some(IonType::Struct) {
panic!("Attempted to set field name when the writer was not in a struct.");
}
match name.as_raw_symbol_token_ref() {
RawSymbolTokenRef::SymbolId(sid) => self.set_field_id(sid),
RawSymbolTokenRef::Text(text) => panic!(
"The RawBinaryWriter can only accept Symbol ID field names, not text ('{text}')."
),
}
}
fn parent_type(&self) -> Option<IonType> {
match self.levels.last().unwrap().container_type {
ContainerType::TopLevel => None,
ContainerType::Struct => Some(IonType::Struct),
ContainerType::List => Some(IonType::List),
ContainerType::SExpression => Some(IonType::SExp),
}
}
fn depth(&self) -> usize {
self.levels.len() - 1
}
fn step_out(&mut self) -> IonResult<()> {
if self.levels.len() <= 1 {
return illegal_operation(
"Cannot call step_out() unless the writer is positioned within a container.",
);
}
self.clear_annotations();
let container = self.levels.pop().unwrap();
self.num_annotations_current_value = container.num_annotations;
self.field_id = container.field_id;
let container_size = container.calculate_final_size(&mut self.io_ranges);
use crate::types::ContainerType::*;
let mut type_descriptor: u8 = match container.container_type {
List => 0xB0,
SExpression => 0xC0,
Struct => 0xD0,
_ => return illegal_operation("Cannot step into a scalar Ion type."),
};
let header_io_range = self.encode_to_buffer(|writer| {
if container_size <= MAX_INLINE_LENGTH {
type_descriptor |= container_size as u8;
writer.buffer.push(type_descriptor);
} else {
type_descriptor |= 0x0E; writer.buffer.push(type_descriptor);
VarUInt::write_u64(&mut writer.buffer, container_size as u64)?;
}
Ok(())
})?;
let container_size = container_size + header_io_range.len();
let td_io_range = self
.io_ranges
.get_mut(container.td_io_range_index)
.expect("Missing type descriptor IO range for {}");
let _ = mem::replace(td_io_range, header_io_range);
if container.num_annotations > 0 {
self.encode_container_annotations(container.td_io_range_index, container_size)?;
}
self.push_empty_io_range();
Ok(())
}
fn flush(&mut self) -> IonResult<()> {
if self.depth() > 0 {
return illegal_operation(
"Cannot call flush() while the writer is positioned within a container.",
);
}
for io_range in self.io_ranges.drain(..) {
self.contiguous_encoding
.extend_from_slice(&self.buffer[io_range]);
}
self.out.write_all(self.contiguous_encoding.as_slice())?;
self.contiguous_encoding.clear();
self.push_empty_io_range();
Ok(())
}
fn output(&self) -> &Self::Output {
&self.out
}
fn output_mut(&mut self) -> &mut Self::Output {
&mut self.out
}
}
#[cfg(test)]
mod writer_tests {
use std::fmt::Debug;
use crate::StreamItem;
use rstest::*;
use super::*;
use crate::raw_symbol_token::{local_sid_token, RawSymbolToken};
use crate::reader::{Reader, ReaderBuilder};
use crate::types::{Blob, Clob, Symbol};
use crate::IonReader;
use num_bigint::BigInt;
use num_traits::Float;
use std::convert::TryInto;
use std::str::FromStr;
type TestWriter<'a> = RawBinaryWriter<&'a mut Vec<u8>>;
type TestReader<'a> = Reader<'a>;
fn binary_writer_test(
mut write_fn: impl FnMut(&mut TestWriter) -> IonResult<()>,
mut read_fn: impl FnMut(&mut TestReader) -> IonResult<()>,
) -> IonResult<()> {
let mut buffer = vec![];
let mut writer = RawBinaryWriterBuilder::new().build(&mut buffer)?;
writer.write_ion_version_marker(1, 0)?;
write_fn(&mut writer)?;
writer.flush()?;
let data = buffer.as_slice();
let mut reader = ReaderBuilder::new().build(data)?;
read_fn(&mut reader)
}
fn binary_writer_scalar_test<T, U>(
values: &[T],
ion_type: IonType,
mut write_fn: impl FnMut(&mut TestWriter, &T) -> IonResult<()>,
mut read_fn: impl FnMut(&mut TestReader) -> IonResult<U>,
) -> IonResult<()>
where
T: Debug,
U: std::cmp::PartialEq<T> + Debug,
{
binary_writer_test(
|writer| {
for value in values {
write_fn(writer, value)?;
}
Ok(())
},
|reader| {
for value in values {
assert_eq!(reader.next()?, StreamItem::Value(ion_type));
let reader_value = read_fn(reader)?;
assert_eq!(
reader_value, *value,
"Value read back in (left) was not equal to the original value (right)"
);
}
Ok(())
},
)
}
#[test]
fn binary_writer_nulls() -> IonResult<()> {
let ion_types = &[
IonType::Null,
IonType::Bool,
IonType::Int,
IonType::Float,
IonType::Decimal,
IonType::Timestamp,
IonType::Symbol,
IonType::String,
IonType::Clob,
IonType::Blob,
IonType::List,
IonType::SExp,
IonType::Struct,
];
binary_writer_test(
|writer| {
for ion_type in ion_types {
writer.write_null(*ion_type)?;
}
Ok(())
},
|reader| {
for ion_type in ion_types {
assert_eq!(reader.next()?, StreamItem::Null(*ion_type));
}
Ok(())
},
)
}
#[test]
fn binary_writer_bools() -> IonResult<()> {
binary_writer_scalar_test(
&[true, false],
IonType::Bool,
|writer, v| writer.write_bool(*v),
|reader| reader.read_bool(),
)
}
#[test]
fn binary_writer_ints() -> IonResult<()> {
binary_writer_scalar_test(
&[-24_601, -17, -1, 0, 1, 17, 24_601],
IonType::Int,
|writer, v| writer.write_i64(*v),
|reader| reader.read_i64(),
)
}
#[test]
fn binary_writer_floats() -> IonResult<()> {
binary_writer_scalar_test(
&[-24.601, -1.7, -1.0, -0.0, 0.0, 1.0, 1.7, 24.601],
IonType::Float,
|writer, v| writer.write_f64(*v),
|reader| reader.read_f64(),
)
}
#[rstest]
#[case::year(Timestamp::with_year(2021).build().unwrap())]
#[case::year_month(Timestamp::with_year(2021).with_month(1).build().unwrap())]
#[case::year_month_day(Timestamp::with_ymd(2021, 1, 8).build().unwrap())]
#[case::ymd_hm_unknown(Timestamp::with_ymd(2021, 1, 8).with_hour_and_minute(14, 12).build_at_unknown_offset().unwrap())]
#[case::ymd_hm_est(Timestamp::with_ymd(2021, 1, 8).with_hour_and_minute(14, 12).build_at_offset(-5 * 60).unwrap())]
#[case::ymd_hms_unknown(Timestamp::with_ymd(2021, 1, 8).with_hms(14, 12, 36).build_at_unknown_offset().unwrap())]
#[case::ymd_hms_est(Timestamp::with_ymd(2021, 1, 8).with_hms(14, 12, 36).build_at_offset(-5 * 60).unwrap())]
#[case::ymd_hms_millis_unknown(Timestamp::with_ymd(2021, 1, 8).with_hms(14, 12, 36).with_milliseconds(888).build_at_unknown_offset().unwrap())]
#[case::ymd_hms_millis_est(Timestamp::with_ymd(2021, 1, 8).with_hms(14, 12, 36).with_milliseconds(888).build_at_offset(-5 * 60).unwrap())]
#[case::ymd_hms_nanos_unknown(Timestamp::with_ymd(2021, 1, 8).with_hms(14, 12, 36).with_nanoseconds(888888888).build_at_unknown_offset().unwrap())]
#[case::ymd_hms_nanos_est(Timestamp::with_ymd(2021, 1, 8).with_hms(14, 12, 36).with_nanoseconds(888888888).build_at_offset(-5 * 60).unwrap())]
fn binary_writer_timestamps(#[case] timestamp: Timestamp) -> IonResult<()> {
binary_writer_scalar_test(
&[timestamp],
IonType::Timestamp,
|writer, v| writer.write_timestamp(v),
|reader| reader.read_timestamp(),
)
}
#[rstest]
#[case(24.601)]
#[case(-24.601)]
#[case(1.7)]
#[case(-1.7)]
#[case(1.0)]
#[case(-1.0)]
#[case::positive_zero(0.0)]
#[case::negative_zero(f64::neg_zero())]
fn binary_writer_decimals(#[case] value: f64) -> IonResult<()> {
let decimal: Decimal = value.try_into().unwrap();
binary_writer_scalar_test(
&[decimal],
IonType::Decimal,
|writer, v| writer.write_decimal(v),
|reader| reader.read_decimal(),
)
}
#[test]
fn binary_writer_symbols() -> IonResult<()> {
let symbol_ids: Vec<RawSymbolToken> = [0, 5, 10, 31, 111, 556, 1024, 74_991, 111_448]
.iter()
.map(|sid| local_sid_token(*sid))
.collect();
binary_writer_scalar_test(
symbol_ids.as_slice(),
IonType::Symbol,
|writer, v| writer.write_symbol_id(v.local_sid().unwrap()),
|reader| reader.read_raw_symbol(),
)
}
#[test]
fn binary_writer_strings() -> IonResult<()> {
binary_writer_scalar_test(
&["", "foo", "bar", "baz", "quux", "Winnipeg", "😂😂😂"],
IonType::String,
|writer, v| writer.write_string(*v),
|reader| reader.read_string(),
)
}
#[test]
fn binary_writer_lobs() -> IonResult<()> {
let values: Vec<&[u8]> = ["", "foo", "bar", "baz", "quux", "Winnipeg", "😂😂😂"]
.iter()
.map(|s| s.as_bytes())
.collect();
let clobs: Vec<Clob> = values.iter().map(|b| Clob::from(*b)).collect();
let blobs: Vec<Blob> = values.iter().map(|b| Blob::from(*b)).collect();
binary_writer_scalar_test(
clobs.as_slice(),
IonType::Clob,
|writer, v| writer.write_clob(v),
|reader| reader.read_clob(),
)?;
binary_writer_scalar_test(
blobs.as_slice(),
IonType::Blob,
|writer, v| writer.write_blob(v),
|reader| reader.read_blob(),
)
}
fn expect_scalar<T: Debug, U: PartialEq<T> + Debug>(
reader: &mut TestReader,
ion_type: IonType,
mut read_fn: impl FnMut(&mut TestReader) -> IonResult<U>,
expected_value: T,
) {
let next = reader.next().unwrap_or_else(|_| {
panic!("Expected to read {expected_value:?}, but the stream was empty.")
});
assert_eq!(next, StreamItem::Value(ion_type));
let value = read_fn(reader)
.unwrap_or_else(|_| panic!("Failed to read in expected value: {expected_value:?}"));
assert_eq!(value, expected_value);
}
fn expect_bool(reader: &mut TestReader, value: bool) {
expect_scalar(reader, IonType::Bool, |r| r.read_bool(), value);
}
fn expect_integer(reader: &mut TestReader, value: i64) {
expect_scalar(reader, IonType::Int, |r| r.read_i64(), value);
}
fn expect_big_integer(reader: &mut TestReader, value: &BigInt) {
expect_scalar(
reader,
IonType::Int,
|r| r.read_int(),
Int::BigInt(value.clone()),
);
}
fn expect_float(reader: &mut TestReader, value: f64) {
expect_scalar(reader, IonType::Float, |r| r.read_f64(), value);
}
fn expect_symbol_id(reader: &mut TestReader, value: SymbolId) {
expect_scalar(
reader,
IonType::Symbol,
|r| r.read_raw_symbol(),
local_sid_token(value),
);
}
fn expect_string(reader: &mut TestReader, value: &str) {
expect_scalar(reader, IonType::String, |r| r.read_string(), value);
}
fn expect_null(reader: &mut TestReader) {
assert_eq!(
reader.next().expect("Failed to read null."),
StreamItem::Null(IonType::Null)
);
}
fn expect_container(reader: &mut TestReader, ion_type: IonType) {
assert_eq!(
reader.next().expect("Failed to read container."),
StreamItem::Value(ion_type)
);
}
fn expect_list(reader: &mut TestReader) {
expect_container(reader, IonType::List);
}
fn expect_s_expression(reader: &mut TestReader) {
expect_container(reader, IonType::SExp);
}
fn expect_struct(reader: &mut TestReader) {
expect_container(reader, IonType::Struct);
}
fn expect_field_name(reader: &TestReader, field_name: &str) {
assert!(reader.field_name().is_ok());
assert_eq!(reader.field_name().unwrap(), field_name);
}
fn expect_annotations(reader: &TestReader, annotations: &[&str]) {
assert_eq!(
reader
.annotations()
.map(|opt| opt.expect("Annotation with unknown text."))
.collect::<Vec<Symbol>>()
.as_slice(),
annotations
);
}
fn write_lst<W: Write>(writer: &mut RawBinaryWriter<W>, symbols: &[&str]) -> IonResult<()> {
writer.set_annotations([3]); writer.step_in(IonType::Struct)?;
writer.set_field_id(7); writer.step_in(IonType::List)?;
for symbol in symbols {
writer.write_string(symbol)?;
}
writer.step_out()?;
writer.step_out()?;
Ok(())
}
#[test]
fn binary_writer_large_integers() -> IonResult<()> {
let big_positive = BigInt::from_str("123456789123456789123456789").unwrap();
let very_big_positive =
BigInt::from_str("123456789123456789123456789123456789123456789").unwrap();
let big_negative = -big_positive.clone();
let very_big_negative = -very_big_positive.clone();
binary_writer_test(
|writer| {
writer.write_int(&Int::BigInt(BigInt::zero()))?;
writer.write_int(&Int::BigInt(big_positive.clone()))?;
writer.write_int(&Int::BigInt(very_big_positive.clone()))?;
writer.write_int(&Int::BigInt(big_negative.clone()))?;
writer.write_int(&Int::BigInt(very_big_negative.clone()))?;
Ok(())
},
|reader| {
expect_big_integer(reader, &BigInt::zero());
expect_big_integer(reader, &big_positive);
expect_big_integer(reader, &very_big_positive);
expect_big_integer(reader, &big_negative);
expect_big_integer(reader, &very_big_negative);
Ok(())
},
)
}
#[test]
fn binary_writer_mixed_scalars() -> IonResult<()> {
binary_writer_test(
|writer| {
writer.write_i64(42)?;
writer.write_string("Hello")?;
writer.write_symbol_id(12)?;
writer.write_f32(2.5)?;
writer.write_f64(7.5)?;
writer.write_bool(false)
},
|reader| {
expect_integer(reader, 42);
expect_string(reader, "Hello");
expect_symbol_id(reader, 12);
expect_float(reader, 2.5);
expect_float(reader, 7.5);
expect_bool(reader, false);
Ok(())
},
)
}
#[test]
fn binary_writer_annotated_scalars() -> IonResult<()> {
binary_writer_test(
|writer| {
write_lst(writer, &["foo", "bar", "baz", "quux", "quuz", "waldo"])?;
writer.set_annotations([10]);
writer.write_bool(true)?;
writer.set_annotations([11, 12]);
writer.write_i64(42)?;
writer.set_annotations([13, 14, 15]);
writer.write_string("Hello")
},
|reader| {
expect_bool(reader, true);
expect_annotations(reader, &["foo"]);
expect_integer(reader, 42);
expect_annotations(reader, &["bar", "baz"]);
expect_string(reader, "Hello");
expect_annotations(reader, &["quux", "quuz", "waldo"]);
Ok(())
},
)
}
#[test]
fn binary_writer_annotated_containers() -> IonResult<()> {
binary_writer_test(
|writer| {
write_lst(
writer,
&["foo", "bar", "baz", "quux", "quuz", "waldo", "gary"],
)?;
writer.set_annotations([10]);
writer.step_in(IonType::SExp)?;
writer.write_bool(true)?;
writer.step_out()?;
writer.set_annotations([11, 12]);
writer.step_in(IonType::List)?;
writer.write_i64(11)?;
writer.step_out()?;
writer.set_annotations([13, 14, 15]);
writer.step_in(IonType::Struct)?;
writer.set_field_id(16);
writer.write_string("foo")?;
writer.step_out()
},
|reader| {
expect_s_expression(reader);
expect_annotations(reader, &["foo"]);
reader.step_in()?;
expect_bool(reader, true);
reader.step_out()?;
expect_list(reader);
expect_annotations(reader, &["bar", "baz"]);
reader.step_in()?;
expect_integer(reader, 11);
reader.step_out()?;
expect_struct(reader);
expect_annotations(reader, &["quux", "quuz", "waldo"]);
reader.step_in()?;
expect_string(reader, "foo");
expect_field_name(reader, "gary");
reader.step_out()?;
Ok(())
},
)
}
#[test]
fn binary_writer_nested_annotated_containers() -> IonResult<()> {
binary_writer_test(
|writer| {
write_lst(writer, &["foo", "bar", "baz", "quux"])?;
writer.set_annotations([10]);
writer.step_in(IonType::Struct)?;
writer.set_field_id(11);
writer.set_annotations([12]);
writer.step_in(IonType::List)?;
writer.set_annotations([13]);
writer.write_string("quuz")?;
writer.step_out()?; writer.step_out() },
|reader| {
expect_struct(reader);
expect_annotations(reader, &["foo"]);
reader.step_in()?;
expect_list(reader);
expect_field_name(reader, "bar");
expect_annotations(reader, &["baz"]);
reader.step_in()?;
expect_string(reader, "quuz");
expect_annotations(reader, &["quux"]);
reader.step_out()?;
reader.step_out()?;
Ok(())
},
)
}
#[test]
fn binary_writer_list() -> IonResult<()> {
binary_writer_test(
|writer| {
writer.step_in(IonType::List)?;
writer.write_i64(42)?;
writer.write_string("Hello")?;
writer.step_out()
},
|reader| {
expect_list(reader);
reader.step_in()?;
expect_integer(reader, 42);
expect_string(reader, "Hello");
reader.step_out()
},
)
}
#[test]
fn binary_writer_nested_list() -> IonResult<()> {
binary_writer_test(
|writer| {
writer.step_in(IonType::List)?;
writer.write_i64(42)?;
writer.step_in(IonType::List)?;
writer.write_string("Hello")?;
writer.step_out()?;
writer.write_string("foo")?;
writer.step_out()
},
|reader| {
expect_list(reader);
reader.step_in()?;
expect_integer(reader, 42);
expect_list(reader);
reader.step_in()?;
expect_string(reader, "Hello");
reader.step_out()?;
expect_string(reader, "foo");
reader.step_out()
},
)
}
#[test]
fn binary_writer_nested_structs() -> IonResult<()> {
binary_writer_test(
|writer| {
write_lst(writer, &["foo", "bar", "baz", "quux"])?;
writer.step_in(IonType::Struct)?;
writer.set_field_id(10);
writer.write_bool(true)?;
writer.set_field_id(11);
writer.step_in(IonType::Struct)?;
writer.set_field_id(13);
writer.write_i64(7)?;
writer.step_out()?; writer.set_field_id(12);
writer.write_null(IonType::Null)?;
writer.step_out() },
|reader| {
expect_struct(reader);
reader.step_in()?;
expect_bool(reader, true);
expect_field_name(reader, "foo");
expect_struct(reader);
expect_field_name(reader, "bar");
reader.step_in()?;
expect_integer(reader, 7);
expect_field_name(reader, "quux");
reader.step_out()?;
expect_null(reader);
expect_field_name(reader, "baz");
reader.step_out()
},
)
}
}