From e3f0eaff67e736f1d57c622e056f38d49638487b Mon Sep 17 00:00:00 2001 From: Zack Slayton Date: Tue, 2 Apr 2024 15:10:23 -0400 Subject: [PATCH] Drops closure-based container writers --- benches/write_many_structs.rs | 234 ++++----- examples/write_log_events.rs | 70 ++- src/binary/var_uint.rs | 49 +- src/element/annotations.rs | 6 + src/lazy/encoder/annotate.rs | 26 +- .../encoder/binary/v1_0/container_writers.rs | 269 ++++++++-- src/lazy/encoder/binary/v1_0/value_writer.rs | 493 +++++------------- src/lazy/encoder/binary/v1_0/writer.rs | 20 +- .../encoder/binary/v1_1/container_writers.rs | 355 +++++++++---- src/lazy/encoder/binary/v1_1/value_writer.rs | 412 +++++---------- src/lazy/encoder/binary/v1_1/writer.rs | 28 +- src/lazy/encoder/container_fn.rs | 56 -- src/lazy/encoder/mod.rs | 70 ++- src/lazy/encoder/text/mod.rs | 36 +- src/lazy/encoder/text/value_writer.rs | 240 +++++---- src/lazy/encoder/value_writer.rs | 285 ++++++---- src/lazy/encoder/write_as_ion.rs | 120 ++--- src/lazy/never.rs | 63 ++- src/raw_symbol_token_ref.rs | 24 + 19 files changed, 1468 insertions(+), 1388 deletions(-) delete mode 100644 src/lazy/encoder/container_fn.rs diff --git a/benches/write_many_structs.rs b/benches/write_many_structs.rs index fbb51418..8151b952 100644 --- a/benches/write_many_structs.rs +++ b/benches/write_many_structs.rs @@ -3,119 +3,113 @@ use ion_rs::lazy::encoder::binary::v1_0::writer::LazyRawBinaryWriter_1_0; use nom::AsBytes; use ion_rs::lazy::encoder::binary::v1_1::writer::LazyRawBinaryWriter_1_1; -use ion_rs::lazy::encoder::value_writer::{AnnotatableValueWriter, SequenceWriter}; -use ion_rs::lazy::encoder::value_writer::{StructWriter, ValueWriter}; -use ion_rs::RawSymbolTokenRef; +use ion_rs::lazy::encoder::value_writer::StructWriter; +use ion_rs::lazy::encoder::value_writer::{SequenceWriter, ValueWriter}; +use ion_rs::{IonResult, RawSymbolTokenRef}; -fn write_struct_with_string_values(value_writer: impl ValueWriter) { - value_writer - .write_struct(|fields| { - fields - // $10 = timestamp - .write(10, black_box(1670446800245i64))? - // $11 = threadId - .write(11, black_box(418))? - // $12 = threadName - .write(12, black_box("scheduler-thread-6"))? - // $13 = loggerName - .write(13, black_box("com.example.organization.product.component.ClassName"))? - // $14 = logLevel - .write(14, black_box("INFO"))? - // $15 = format - .write(15, black_box("Request status: {} Client ID: {} Client Host: {} Client Region: {} Timestamp: {}"))? - // $16 = parameters - .write(16, &[ - black_box("SUCCESS"), - black_box("example-client-1"), - black_box("aws-us-east-5f-18b4fa"), - black_box("region 4"), - black_box("2022-12-07T20:59:59.744000Z"), - ])?; - Ok(()) - }).unwrap(); +fn write_struct_with_string_values(value_writer: impl ValueWriter) -> IonResult<()> { + let mut struct_ = value_writer.struct_writer()?; + struct_ + // $10 = timestamp + .write(10, black_box(1670446800245i64))? + // $11 = threadId + .write(11, black_box(418))? + // $12 = threadName + .write(12, black_box("scheduler-thread-6"))? + // $13 = loggerName + .write( + 13, + black_box("com.example.organization.product.component.ClassName"), + )? + // $14 = logLevel + .write(14, black_box("INFO"))? + // $15 = format + .write( + 15, + black_box( + "Request status: {} Client ID: {} Client Host: {} Client Region: {} Timestamp: {}", + ), + )? + // $16 = parameters + .write( + 16, + &[ + black_box("SUCCESS"), + black_box("example-client-1"), + black_box("aws-us-east-5f-18b4fa"), + black_box("region 4"), + black_box("2022-12-07T20:59:59.744000Z"), + ], + )?; + struct_.end() } -fn write_struct_with_symbol_values(value_writer: impl ValueWriter) { - value_writer - .write_struct(|fields| { - fields - // $10 = timestamp - .write(10, black_box(1670446800245i64))? - // $11 = threadId - .write(11, black_box(418))? - // $12 = threadName, $17 = scheduler-thread-6 - .write(12, symbol_id(black_box(17)))? - // $13 = loggerName, $18 = com.example.organization.product.component.ClassName - .write(13, symbol_id(black_box(18)))? - // $14 = logLevel, $19 = INFO - .write(14, symbol_id(black_box(19)))? - // $15 = format, $20 = Request status: {} Client ID: {} Client Host: {} Client Region: {} Timestamp: {} - .write(15, symbol_id(black_box(20)))? - // $16 = parameters - .write( - 16, - &[ - // $21 = SUCCESS - symbol_id(black_box(21)), - // $22 = example-client-1 - symbol_id(black_box(22)), - // $23 = aws-us-east-5f-18b4fa - symbol_id(black_box(23)), - // $24 = region 4 - symbol_id(black_box(24)), - // $25 = 2022-12-07T20:59:59.744000Z (string, not timestamp) - symbol_id(black_box(25)), - ], - )?; - Ok(()) - }) - .unwrap(); +fn write_struct_with_symbol_values(value_writer: impl ValueWriter) -> IonResult<()> { + let mut struct_ = value_writer.struct_writer()?; + struct_ + // $10 = timestamp + .write(10, black_box(1670446800245i64))? + // $11 = threadId + .write(11, black_box(418))? + // $12 = threadName, $17 = scheduler-thread-6 + .write(12, symbol_id(black_box(17)))? + // $13 = loggerName, $18 = com.example.organization.product.component.ClassName + .write(13, symbol_id(black_box(18)))? + // $14 = logLevel, $19 = INFO + .write(14, symbol_id(black_box(19)))? + // $15 = format, $20 = Request status: {} Client ID: {} Client Host: {} Client Region: {} Timestamp: {} + .write(15, symbol_id(black_box(20)))? + // $16 = parameters + .write( + 16, + &[ + // $21 = SUCCESS + symbol_id(black_box(21)), + // $22 = example-client-1 + symbol_id(black_box(22)), + // $23 = aws-us-east-5f-18b4fa + symbol_id(black_box(23)), + // $24 = region 4 + symbol_id(black_box(24)), + // $25 = 2022-12-07T20:59:59.744000Z (string, not timestamp) + symbol_id(black_box(25)), + ], + )?; + struct_.end() } -fn write_eexp_with_symbol_values(value_writer: impl ValueWriter) { - value_writer - .write_eexp(0, |args| { - args.write(black_box(1670446800245i64))? // timestamp - .write(black_box(418))? // thread_id - // These are still strings because they're so short that using symbols to represent - // them wouldn't be beneficial. - .write(black_box("6"))? // thread_name - .write(black_box("1"))? // client_num - .write(symbol_id(black_box(10)))? // host_id: "18b4fa" ($10) - .value_writer() - .without_annotations() - .write_eexp(1, |args| { - args - // $11 = region 4 - .write(symbol_id(black_box(11)))? - // $12 = "2022-12-07T20:59:59.744000Z" (string, not timestamp) - .write(symbol_id(black_box(12)))?; - Ok(()) - }) - .unwrap(); - Ok(()) - }) - .unwrap(); +fn write_eexp_with_symbol_values(value_writer: impl ValueWriter) -> IonResult<()> { + let mut eexp = value_writer.eexp_writer(0)?; + eexp.write(black_box(1670446800245i64))? // timestamp + .write(black_box(418))? // thread_id + // These are still strings because they're so short that using symbols to represent + // them wouldn't be beneficial. + .write(black_box("6"))? // thread_name + .write(black_box("1"))? // client_num + .write(symbol_id(black_box(10)))?; // host_id: "18b4fa" ($10) + let mut nested_eexp = eexp.eexp_writer(1)?; + nested_eexp + // $11 = region 4 + .write(symbol_id(black_box(11)))? + // $12 = "2022-12-07T20:59:59.744000Z" (string, not timestamp) + .write(symbol_id(black_box(12)))?; + nested_eexp.end()?; + eexp.end() } -fn write_eexp_with_string_values(value_writer: impl ValueWriter) { - value_writer - .write_eexp(0, |args| { - args.write(black_box(1670446800245i64))? // timestamp - .write(black_box(418))? // thread_id - .write(black_box("6"))? // thread_name - .write(black_box("1"))? // client_num - .write(black_box("18b4fa"))? // host_id - .value_writer() - .without_annotations() - .write_eexp(1, |args| { - args.write(black_box("region 4"))? - .write(black_box("2022-12-07T20:59:59.744000Z"))?; - Ok(()) - })?; - Ok(()) - }) - .unwrap(); +fn write_eexp_with_string_values(value_writer: impl ValueWriter) -> IonResult<()> { + let mut eexp = value_writer.eexp_writer(0)?; + eexp.write(black_box(1670446800245i64))? // timestamp + .write(black_box(418))? // thread_id + .write(black_box("6"))? // thread_name + .write(black_box("1"))? // client_num + .write(black_box("18b4fa"))?; // host_id + let mut nested_eexp = eexp.eexp_writer(1)?; + nested_eexp + .write(black_box("region 4"))? + .write(black_box("2022-12-07T20:59:59.744000Z"))?; + nested_eexp.end()?; + eexp.end() } fn symbol_id(sid: usize) -> RawSymbolTokenRef<'static> { @@ -130,7 +124,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter(|| { buffer.clear(); let mut writer = LazyRawBinaryWriter_1_0::new(&mut buffer).unwrap(); - write_struct_with_string_values(writer.value_writer().without_annotations()); + write_struct_with_string_values(writer.value_writer()).unwrap(); writer.flush().unwrap(); black_box(buffer.as_bytes()); }); @@ -148,7 +142,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter(|| { buffer.clear(); let mut writer = LazyRawBinaryWriter_1_0::new(&mut buffer).unwrap(); - write_struct_with_symbol_values(writer.value_writer().without_annotations()); + write_struct_with_symbol_values(writer.value_writer()).unwrap(); writer.flush().unwrap(); black_box(buffer.as_bytes()); @@ -165,7 +159,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter(|| { buffer.clear(); let mut writer = LazyRawBinaryWriter_1_1::new(&mut buffer).unwrap(); - write_struct_with_string_values(writer.value_writer().without_annotations()); + write_struct_with_string_values(writer.value_writer()).unwrap(); writer.flush().unwrap(); black_box(buffer.as_bytes()); }); @@ -179,7 +173,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter(|| { buffer.clear(); let mut writer = LazyRawBinaryWriter_1_1::new(&mut buffer).unwrap(); - write_struct_with_symbol_values(writer.value_writer().without_annotations()); + write_struct_with_symbol_values(writer.value_writer()).unwrap(); writer.flush().unwrap(); black_box(buffer.as_bytes()); @@ -194,12 +188,8 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter(|| { buffer.clear(); let mut writer = LazyRawBinaryWriter_1_1::new(&mut buffer).unwrap(); - write_struct_with_string_values( - writer - .value_writer() - .with_delimited_containers() - .without_annotations(), - ); + write_struct_with_string_values(writer.value_writer().with_delimited_containers()) + .unwrap(); writer.flush().unwrap(); black_box(buffer.as_bytes()); }); @@ -216,12 +206,8 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter(|| { buffer.clear(); let mut writer = LazyRawBinaryWriter_1_1::new(&mut buffer).unwrap(); - write_struct_with_symbol_values( - writer - .value_writer() - .with_delimited_containers() - .without_annotations(), - ); + write_struct_with_symbol_values(writer.value_writer().with_delimited_containers()) + .unwrap(); writer.flush().unwrap(); black_box(buffer.as_bytes()); @@ -236,7 +222,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter(|| { buffer.clear(); let mut writer = LazyRawBinaryWriter_1_1::new(&mut buffer).unwrap(); - write_eexp_with_string_values(writer.value_writer().without_annotations()); + write_eexp_with_string_values(writer.value_writer()).unwrap(); writer.flush().unwrap(); black_box(buffer.as_bytes()); }); @@ -253,7 +239,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { b.iter(|| { buffer.clear(); let mut writer = LazyRawBinaryWriter_1_1::new(&mut buffer).unwrap(); - write_eexp_with_symbol_values(writer.value_writer().without_annotations()); + write_eexp_with_symbol_values(writer.value_writer()).unwrap(); writer.flush().unwrap(); black_box(buffer.as_bytes()); }); diff --git a/examples/write_log_events.rs b/examples/write_log_events.rs index fba70191..aacbb251 100644 --- a/examples/write_log_events.rs +++ b/examples/write_log_events.rs @@ -19,7 +19,7 @@ mod example { use ion_rs::lazy::encoder::binary::v1_0::writer::LazyRawBinaryWriter_1_0; use ion_rs::lazy::encoder::binary::v1_1::writer::LazyRawBinaryWriter_1_1; use ion_rs::lazy::encoder::value_writer::{SequenceWriter, StructWriter, ValueWriter}; - use ion_rs::lazy::encoder::write_as_ion::WriteAsIonValue; + use ion_rs::lazy::encoder::write_as_ion::WriteAsIon; use ion_rs::*; use std::env::args; @@ -165,11 +165,11 @@ mod example { // ===== Serialization logic for the above types ===== - impl WriteAsIonValue for Parameter { - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { + impl WriteAsIon for Parameter { + fn write_as_ion(&self, writer: V) -> IonResult<()> { match self { - Parameter::Int(i) => i.write_as_ion_value(writer), - Parameter::String(s) => s.as_str().write_as_ion_value(writer), + Parameter::Int(i) => i.write_as_ion(writer), + Parameter::String(s) => s.as_str().write_as_ion(writer), } } } @@ -184,22 +184,21 @@ mod example { // field name/value pair. In the case of recurring strings, we take the liberty of writing // out symbol IDs instead of the full text; this silent type coercion from string to symbol // is technically data loss, but results in a much more compact encoding. - impl<'a, 'b> WriteAsIonValue for SerializeWithoutMacros<'a, 'b> { - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { + impl<'a, 'b> WriteAsIon for SerializeWithoutMacros<'a, 'b> { + fn write_as_ion(&self, writer: V) -> IonResult<()> { let event = self.0; - writer.write_struct(|fields| { - fields - // v--- Each field name is a symbol ID - .write(10, &event.timestamp)? - .write(11, event.thread_id)? - .write(12, &event.thread_name)? - // v--- The fixed strings from the log statement are also SIDs - .write(13, RawSymbolToken::SymbolId(17))? // logger name - .write(14, RawSymbolToken::SymbolId(18))? // log level - .write(15, RawSymbolToken::SymbolId(19))? // format - .write(16, &event.parameters)?; - Ok(()) - }) + let mut struct_ = writer.struct_writer()?; + struct_ + // v--- Each field name is a symbol ID + .write(10, &event.timestamp)? + .write(11, event.thread_id)? + .write(12, &event.thread_name)? + // v--- The fixed strings from the log statement are also SIDs + .write(13, RawSymbolToken::SymbolId(17))? // logger name + .write(14, RawSymbolToken::SymbolId(18))? // log level + .write(15, RawSymbolToken::SymbolId(19))? // format + .write(16, &event.parameters)?; + struct_.end() } } @@ -208,28 +207,27 @@ mod example { // behavior for the thread name. struct ThreadName<'a>(&'a str); - impl<'a> WriteAsIonValue for ThreadName<'a> { - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { + impl<'a> WriteAsIon for ThreadName<'a> { + fn write_as_ion(&self, writer: V) -> IonResult<()> { // ID 12 chosen arbitrarily, but aligns with Ion 1.0 encoding above - writer.write_eexp(12, |args| { + let mut eexp = writer.eexp_writer(12)?; + eexp // Ignore the part of the thread name that starts with the recurring prefix. - args.write(&self.0[THREAD_NAME_PREFIX.len()..])?; - Ok(()) - }) + .write(&self.0[THREAD_NAME_PREFIX.len()..])?; + eexp.end() } } - impl<'a, 'b> WriteAsIonValue for SerializeWithMacros<'a, 'b> { - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { + impl<'a, 'b> WriteAsIon for SerializeWithMacros<'a, 'b> { + fn write_as_ion(&self, writer: V) -> IonResult<()> { let event = self.0; - writer.write_eexp(event.statement.index, |args| { - args.write(&event.timestamp)? - .write(event.thread_id)? - // Wrap the thread name in the `ThreadName` wrapper to change its serialization. - .write(ThreadName(&event.thread_name))? - .write(&event.parameters)?; - Ok(()) - }) + let mut eexp = writer.eexp_writer(event.statement.index)?; + eexp.write(&event.timestamp)? + .write(event.thread_id)? + // Wrap the thread name in the `ThreadName` wrapper to change its serialization. + .write(ThreadName(&event.thread_name))? + .write(&event.parameters)?; + eexp.end() } } diff --git a/src/binary/var_uint.rs b/src/binary/var_uint.rs index 43db765d..372cb81b 100644 --- a/src/binary/var_uint.rs +++ b/src/binary/var_uint.rs @@ -1,4 +1,5 @@ use crate::result::IonResult; +use num_integer::Integer; use std::io::Write; use std::mem; @@ -31,18 +32,30 @@ impl VarUInt { } } + /// Returns the number of bytes needed to encode `value` as a `VarUInt`. + pub fn encoded_size_of(value: u64) -> usize { + let leading_zeros = value.leading_zeros() as usize; + let bits_used = dbg!(usize::BITS as usize - leading_zeros); + let (full_bytes, remaining_bits) = dbg!(bits_used.div_rem(&BITS_PER_ENCODED_BYTE)); + match (full_bytes, remaining_bits) { + (0, 0) => 1, + (_, 0) => full_bytes, + _ => full_bytes + 1, + } + } + /// Encodes the given unsigned int value as a VarUInt and writes it to the /// sink, returning the number of bytes written. pub fn write_u64(sink: &mut W, mut magnitude: u64) -> IonResult { // A u64 is 8 bytes of data. The VarUInt encoding will add a continuation bit to every byte, // growing the data size by 8 more bits. Therefore, the largest encoded size of a u64 is // 9 bytes. - const VAR_UINT_BUFFER_SIZE: usize = 9; + const VAR_UINT_BUFFER_SIZE: usize = 10; // Create a buffer to store the encoded value. #[rustfmt::skip] let mut buffer: [u8; VAR_UINT_BUFFER_SIZE] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0b1000_0000 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0b1000_0000 // ^-- Set the 'end' flag of the final byte to 1 ]; @@ -123,4 +136,36 @@ mod tests { var_uint_encoding_test(400_600, &[0b0001_1000, 0b0011_1001, 0b1101_1000])?; Ok(()) } + + #[test] + fn test_write_var_uint_for_u64_max() -> IonResult<()> { + var_uint_encoding_test( + u64::MAX, + &[0x01, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF], + )?; + Ok(()) + } + + #[test] + fn encoded_size_calculation() -> IonResult<()> { + let mut values: Vec = Vec::new(); + for num_bytes in 0..=63u32 { + values.push(2u64.pow(num_bytes) - 1); + values.push(2u64.pow(num_bytes)) + } + values.push(u64::MAX); + + let mut buffer = vec![]; + for value in values { + buffer.clear(); + VarUInt::write_u64(&mut buffer, value)?; + let encoded_length = buffer.len(); + let calculated_length = VarUInt::encoded_size_of(value); + assert_eq!( + encoded_length, calculated_length, + "encoded length {encoded_length} != calculated length {calculated_length} for value {value}" + ); + } + Ok(()) + } } diff --git a/src/element/annotations.rs b/src/element/annotations.rs index 26f83d16..b2e89418 100644 --- a/src/element/annotations.rs +++ b/src/element/annotations.rs @@ -105,6 +105,12 @@ impl Annotations { } } +impl AsRef<[Symbol]> for Annotations { + fn as_ref(&self) -> &[Symbol] { + self.symbols.as_slice() + } +} + impl From> for Annotations { fn from(value: Vec) -> Self { Annotations::new(value) diff --git a/src/lazy/encoder/annotate.rs b/src/lazy/encoder/annotate.rs index 6ba5c710..5ccc16bf 100644 --- a/src/lazy/encoder/annotate.rs +++ b/src/lazy/encoder/annotate.rs @@ -1,5 +1,5 @@ -use crate::lazy::encoder::value_writer::AnnotatableValueWriter; -use crate::lazy::encoder::write_as_ion::{WriteAsIon, WriteAsIonValue}; +use crate::lazy::encoder::value_writer::ValueWriter; +use crate::lazy::encoder::write_as_ion::WriteAsIon; use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; use crate::IonResult; @@ -42,7 +42,7 @@ pub trait Annotate { // Any Rust value that can be serialized as an Ion value can call `annotate`. impl Annotate for T where - T: ?Sized + WriteAsIonValue, + T: ?Sized + WriteAsIon, { fn annotated_with<'a, A: AsRawSymbolTokenRef>( &'a self, @@ -56,24 +56,14 @@ where } // The `Annotated` struct implements `WriteAsIon` by serializing its sequence of annotations -// and then invoking the inner value's implementation of `WriteAsIonValue`. +// and then invoking the inner value's implementation of `WriteAsIon`. impl<'annotations, T, A> WriteAsIon for Annotated<'annotations, T, A> where - T: WriteAsIonValue, + T: WriteAsIon, A: AsRawSymbolTokenRef, { - fn write_as_ion(&self, writer: V) -> IonResult<()> { - let value_writer = writer.with_annotations(self.annotations); - self.value.write_as_ion_value(value_writer) - } -} - -impl<'annotations, T, A> WriteAsIon for &Annotated<'annotations, T, A> -where - T: WriteAsIonValue, - A: AsRawSymbolTokenRef, -{ - fn write_as_ion(&self, writer: V) -> IonResult<()> { - (*self).write_as_ion(writer) + fn write_as_ion(&self, writer: V) -> IonResult<()> { + let value_writer = ::with_annotations(writer, self.annotations); + self.value.write_as_ion(value_writer) } } diff --git a/src/lazy/encoder/binary/v1_0/container_writers.rs b/src/lazy/encoder/binary/v1_0/container_writers.rs index ca6a21df..7836b9f7 100644 --- a/src/lazy/encoder/binary/v1_0/container_writers.rs +++ b/src/lazy/encoder/binary/v1_0/container_writers.rs @@ -1,14 +1,15 @@ use bumpalo::collections::Vec as BumpVec; use bumpalo::Bump as BumpAllocator; +use ice_code::ice as cold_path; use crate::binary::var_uint::VarUInt; -use crate::lazy::encoder::binary::v1_0::value_writer::BinaryAnnotatableValueWriter_1_0; -use crate::lazy::encoder::value_writer::internal::MakeValueWriter; +use crate::lazy::encoder::binary::v1_0::value_writer::{BinaryValueWriter_1_0, MAX_INLINE_LENGTH}; +use crate::lazy::encoder::value_writer::internal::{FieldEncoder, MakeValueWriter}; use crate::lazy::encoder::value_writer::{SequenceWriter, StructWriter}; use crate::lazy::encoder::write_as_ion::WriteAsIon; use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; -use crate::result::EncodingError; -use crate::{IonError, IonResult, RawSymbolTokenRef}; +use crate::result::{EncodingError, IonFailure}; +use crate::{IonError, IonResult, RawSymbolTokenRef, SymbolId}; /// A helper type that holds fields and logic that is common to [`BinaryListWriter_1_0`], /// [`BinarySExpWriter_1_0`], and [`BinaryStructWriter_1_0`]. @@ -20,7 +21,10 @@ pub struct BinaryContainerWriter_1_0<'value, 'top> { // The buffer containing the parent's encoded body. When this list writer is finished encoding // its own data, a header will be written to the parent and then the list body will be copied // over. - buffer: &'value mut BumpVec<'top, u8>, + child_values_buffer: BumpVec<'top, u8>, + parent_buffer: &'value mut BumpVec<'top, u8>, + // In binary Ion 1.0, only symbol IDs can be used as annotations. + annotations: Option>, } impl<'value, 'top> BinaryContainerWriter_1_0<'value, 'top> { @@ -32,25 +36,123 @@ impl<'value, 'top> BinaryContainerWriter_1_0<'value, 'top> { Self { type_code, allocator, - buffer, + child_values_buffer: BumpVec::with_capacity_in( + DEFAULT_CONTAINER_BUFFER_SIZE, + allocator, + ), + parent_buffer: buffer, + annotations: None, + } + } + + pub fn with_annotations>( + mut self, + annotations: I, + ) -> IonResult { + let iterator = annotations.into_iter(); + let mut symbol_ids = BumpVec::with_capacity_in(iterator.size_hint().0, self.allocator); + for annotation in iterator { + match annotation.as_raw_symbol_token_ref() { + RawSymbolTokenRef::SymbolId(symbol_id) => symbol_ids.push(symbol_id), + RawSymbolTokenRef::Text(text) => { + return cold_path! { + IonResult::encoding_error( + format!("binary Ion 1.0 does not support text annotation literals (received '{}')", text.as_ref()) + ) + }; + } + } } + self.annotations = Some(symbol_ids); + Ok(self) } pub fn write(&mut self, value: V) -> IonResult<&mut Self> { - let annotated_value_writer = - BinaryAnnotatableValueWriter_1_0::new(self.allocator, self.buffer); - value.write_as_ion(annotated_value_writer)?; + let value_writer = + BinaryValueWriter_1_0::new(self.allocator, &mut self.child_values_buffer); + value.write_as_ion(value_writer)?; Ok(self) } - pub fn buffer(&self) -> &[u8] { - self.buffer.as_slice() + pub fn child_values_buffer(&self) -> &[u8] { + self.child_values_buffer.as_slice() } -} -pub struct BinaryContainerValuesWriter_1_0<'value> { - allocator: &'value BumpAllocator, - buffer: BumpVec<'value, u8>, + pub fn end(mut self) -> IonResult<()> { + match self.annotations.take() { + None => self.write_unannotated_value_to_parent_buffer(), + Some(annotations) => self.write_annotated_value_to_parent_buffer(annotations), + } + } + + /// Encodes the container's annotations wrapper and copies the completed value to the parent + /// encoding buffer. + pub fn write_annotated_value_to_parent_buffer( + &mut self, + annotations: BumpVec<'top, SymbolId>, + ) -> IonResult<()> { + // The sequence of VarUInt-encoded symbol IDs + let mut encoded_sequence = BumpVec::new_in(self.allocator); + for annotation in annotations { + VarUInt::write_u64(&mut encoded_sequence, annotation as u64)?; + } + + // The VarUInt-encoded length of the above sequence + let mut encoded_sequence_length = BumpVec::new_in(self.allocator); + VarUInt::write_u64(&mut encoded_sequence_length, encoded_sequence.len() as u64)?; + + // The encoded body of the container + let container_body_length = self.child_values_buffer.len(); + // The method `write_annotated_value_to_parent_buffer` encodes the container's header + // (including the length) to the parent buffer. In this step, we calculate how big that + // header will be, so we can factor it into the annotations wrapper's declared length. + let container_header_length = match container_body_length { + 0..=MAX_INLINE_LENGTH => 1, + length => 1 + VarUInt::encoded_size_of(length as u64), + }; + + let envelope_length = encoded_sequence_length.len() + + encoded_sequence.len() + + container_header_length + + container_body_length; + match envelope_length { + 0..=MAX_INLINE_LENGTH => self.parent_buffer.push(0xE0u8 | envelope_length as u8), + _ => { + self.parent_buffer.push(0xEE); // Annotations wrapper w/VarUInt-encoded length + VarUInt::write_u64(self.parent_buffer, envelope_length as u64)?; + } + } + + self.parent_buffer.extend_from_slices_copy(&[ + encoded_sequence_length.as_slice(), + encoded_sequence.as_slice(), + ]); + + // At this point, the parent container has the complete encoding of the annotations wrapper + // that prefixes the encoding of the value itself. Now we delegate the value's encoding + // to `write_unannotated...` to handle. + self.write_unannotated_value_to_parent_buffer() + } + + /// Encodes the container's header, then copies the header and body bytes to the parent buffer. + pub fn write_unannotated_value_to_parent_buffer(&mut self) -> IonResult<()> { + // Write the appropriate opcode for a container of this length. + let encoded_length = self.child_values_buffer.len(); + match encoded_length { + 0..=MAX_INLINE_LENGTH => { + let opcode = self.type_code | encoded_length as u8; + self.parent_buffer.push(opcode); + } + _ => { + let opcode = self.type_code | 0x0E; // Container w/VarUInt length + self.parent_buffer.push(opcode); + VarUInt::write_u64(self.parent_buffer, encoded_length as u64)?; + } + } + self.parent_buffer + .extend_from_slice_copy(self.child_values_buffer.as_slice()); + Ok(()) + } } // This value was chosen somewhat arbitrarily and can be modified as needed. Choosing a value that @@ -63,43 +165,73 @@ pub struct BinaryListWriter_1_0<'value, 'top> { } impl<'value, 'top> BinaryListWriter_1_0<'value, 'top> { - pub fn new(container_writer: BinaryContainerWriter_1_0<'value, 'top>) -> Self { + pub(crate) fn with_container_writer( + container_writer: BinaryContainerWriter_1_0<'value, 'top>, + ) -> Self { Self { container_writer } } - pub fn buffer(&self) -> &[u8] { - self.container_writer.buffer() + pub(crate) fn new( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const LIST_TYPE_CODE: u8 = 0xB0; + BinaryListWriter_1_0::with_container_writer(BinaryContainerWriter_1_0::new( + LIST_TYPE_CODE, + allocator, + buffer, + )) + } + + pub(crate) fn with_annotations>( + mut self, + annotations: I, + ) -> IonResult { + self.container_writer = self.container_writer.with_annotations(annotations)?; + Ok(self) + } + + pub(crate) fn child_values_buffer(&self) -> &[u8] { + self.container_writer.child_values_buffer() } } impl<'value, 'top> MakeValueWriter for BinaryListWriter_1_0<'value, 'top> { - type ValueWriter<'a> = BinaryAnnotatableValueWriter_1_0<'a, 'top> where Self: 'a; + type ValueWriter<'a> = BinaryValueWriter_1_0<'a, 'top> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { - BinaryAnnotatableValueWriter_1_0::new( + BinaryValueWriter_1_0::new( self.container_writer.allocator, - self.container_writer.buffer, + &mut self.container_writer.child_values_buffer, ) } } impl<'value, 'top> SequenceWriter for BinaryListWriter_1_0<'value, 'top> { - // All default methods + type End = (); + + fn end(self) -> IonResult { + self.container_writer.end() + } } impl<'value, 'top> MakeValueWriter for BinarySExpWriter_1_0<'value, 'top> { - type ValueWriter<'a> = BinaryAnnotatableValueWriter_1_0<'a, 'top> where Self: 'a; + type ValueWriter<'a> = BinaryValueWriter_1_0<'a, 'top> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { - BinaryAnnotatableValueWriter_1_0::new( + BinaryValueWriter_1_0::new( self.container_writer.allocator, - self.container_writer.buffer, + &mut self.container_writer.child_values_buffer, ) } } impl<'value, 'top> SequenceWriter for BinarySExpWriter_1_0<'value, 'top> { - // All default methods + type End = (); + + fn end(self) -> IonResult { + self.container_writer.end() + } } pub struct BinarySExpWriter_1_0<'value, 'top> { @@ -107,14 +239,31 @@ pub struct BinarySExpWriter_1_0<'value, 'top> { } impl<'value, 'top> BinarySExpWriter_1_0<'value, 'top> { - pub fn new(sequence_writer: BinaryContainerWriter_1_0<'value, 'top>) -> Self { - Self { - container_writer: sequence_writer, - } + pub(crate) fn with_container_writer( + container_writer: BinaryContainerWriter_1_0<'value, 'top>, + ) -> Self { + Self { container_writer } + } + + pub(crate) fn new( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const SEXP_TYPE_CODE: u8 = 0xC0; + let container_writer = BinaryContainerWriter_1_0::new(SEXP_TYPE_CODE, allocator, buffer); + Self::with_container_writer(container_writer) + } + + pub(crate) fn with_annotations>( + mut self, + annotations: I, + ) -> IonResult { + self.container_writer = self.container_writer.with_annotations(annotations)?; + Ok(self) } pub fn buffer(&self) -> &[u8] { - self.container_writer.buffer() + self.container_writer.child_values_buffer() } } @@ -123,23 +272,41 @@ pub struct BinaryStructWriter_1_0<'value, 'top> { } impl<'value, 'top> BinaryStructWriter_1_0<'value, 'top> { - pub fn new(container: BinaryContainerWriter_1_0<'value, 'top>) -> Self { + pub(crate) fn with_container_writer( + container: BinaryContainerWriter_1_0<'value, 'top>, + ) -> Self { Self { container_writer: container, } } + pub(crate) fn new( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const STRUCT_TYPE_CODE: u8 = 0xD0; + Self::with_container_writer(BinaryContainerWriter_1_0::new( + STRUCT_TYPE_CODE, + allocator, + buffer, + )) + } + + pub(crate) fn with_annotations>( + mut self, + annotations: I, + ) -> IonResult { + self.container_writer = self.container_writer.with_annotations(annotations)?; + Ok(self) + } + pub fn buffer(&self) -> &[u8] { - self.container_writer.buffer() + self.container_writer.child_values_buffer() } } -impl<'value, 'top> StructWriter for BinaryStructWriter_1_0<'value, 'top> { - fn write( - &mut self, - name: A, - value: V, - ) -> IonResult<&mut Self> { +impl<'value, 'top> FieldEncoder for BinaryStructWriter_1_0<'value, 'top> { + fn encode_field_name(&mut self, name: impl AsRawSymbolTokenRef) -> IonResult<()> { // Write the field name let sid = match name.as_raw_symbol_token_ref() { RawSymbolTokenRef::SymbolId(sid) => sid, @@ -149,10 +316,26 @@ impl<'value, 'top> StructWriter for BinaryStructWriter_1_0<'value, 'top> { )))); } }; - VarUInt::write_u64(&mut self.container_writer.buffer, sid as u64)?; + VarUInt::write_u64(&mut self.container_writer.child_values_buffer, sid as u64)?; + Ok(()) + } +} - // Write the field value - self.container_writer.write(value)?; - Ok(self) +impl<'value, 'top> MakeValueWriter for BinaryStructWriter_1_0<'value, 'top> { + type ValueWriter<'a> = BinaryValueWriter_1_0<'a, 'top> + where + Self: 'a; + + fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { + BinaryValueWriter_1_0::new( + self.container_writer.allocator, + &mut self.container_writer.child_values_buffer, + ) + } +} + +impl<'value, 'top> StructWriter for BinaryStructWriter_1_0<'value, 'top> { + fn end(self) -> IonResult<()> { + self.container_writer.end() } } diff --git a/src/lazy/encoder/binary/v1_0/value_writer.rs b/src/lazy/encoder/binary/v1_0/value_writer.rs index 93556656..f01afda1 100644 --- a/src/lazy/encoder/binary/v1_0/value_writer.rs +++ b/src/lazy/encoder/binary/v1_0/value_writer.rs @@ -3,7 +3,6 @@ use std::mem; use bumpalo::collections::Vec as BumpVec; use bumpalo::Bump as BumpAllocator; use bytes::BufMut; -use delegate::delegate; use num_bigint::Sign; use num_traits::Zero; @@ -13,13 +12,11 @@ use crate::binary::uint; use crate::binary::uint::DecodedUInt; use crate::binary::var_uint::VarUInt; use crate::lazy::encoder::binary::v1_0::container_writers::{ - BinaryContainerWriter_1_0, BinaryListWriter_1_0, BinarySExpWriter_1_0, BinaryStructWriter_1_0, + BinaryListWriter_1_0, BinarySExpWriter_1_0, BinaryStructWriter_1_0, }; -use crate::lazy::encoder::container_fn::{ListFn, MacroArgsFn, SExpFn, StructFn}; use crate::lazy::encoder::private::Sealed; -use crate::lazy::encoder::value_writer::{ - delegate_value_writer_to, AnnotatableValueWriter, ValueWriter, -}; +use crate::lazy::encoder::value_writer::delegate_value_writer_to_self; +use crate::lazy::encoder::value_writer::ValueWriter; use crate::lazy::never::Never; use crate::lazy::text::raw::v1_1::reader::MacroIdRef; use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; @@ -67,7 +64,7 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { self.encoding_buffer.as_slice() } - pub fn write_symbol_id(&mut self, symbol_id: SymbolId) -> IonResult<()> { + pub fn write_symbol_id(mut self, symbol_id: SymbolId) -> IonResult<()> { const SYMBOL_BUFFER_SIZE: usize = mem::size_of::(); let mut buffer = [0u8; SYMBOL_BUFFER_SIZE]; let mut writer = std::io::Cursor::new(&mut buffer).writer(); @@ -87,7 +84,7 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { Ok(()) } - pub fn write_lob(&mut self, value: &[u8], type_code: u8) -> IonResult<()> { + pub fn write_lob(mut self, value: &[u8], type_code: u8) -> IonResult<()> { let encoded_length = value.len(); let type_descriptor: u8; if encoded_length <= MAX_INLINE_LENGTH { @@ -102,7 +99,7 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { Ok(()) } - pub fn write_null(&mut self, ion_type: IonType) -> IonResult<()> { + pub fn write_null(mut self, ion_type: IonType) -> IonResult<()> { let byte: u8 = match ion_type { IonType::Null => 0x0F, IonType::Bool => 0x1F, @@ -122,13 +119,13 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { Ok(()) } - pub fn write_bool(&mut self, value: bool) -> IonResult<()> { + pub fn write_bool(mut self, value: bool) -> IonResult<()> { let byte: u8 = if value { 0x11 } else { 0x10 }; self.push_byte(byte); Ok(()) } - pub fn write_i64(&mut self, value: i64) -> IonResult<()> { + pub fn write_i64(mut self, value: i64) -> IonResult<()> { // Get the absolute value of the i64 and store it in a u64. let magnitude: u64 = value.unsigned_abs(); let encoded = uint::encode_u64(magnitude); @@ -148,7 +145,7 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { Ok(()) } - pub fn write_int(&mut self, value: &Int) -> IonResult<()> { + pub fn write_int(mut self, value: &Int) -> IonResult<()> { // If the `value` is an `i64`, use `write_i64` and return. let value = match &value.data { IntData::I64(i) => return self.write_i64(*i), @@ -183,7 +180,7 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { Ok(()) } - pub fn write_f32(&mut self, value: f32) -> IonResult<()> { + pub fn write_f32(mut self, value: f32) -> IonResult<()> { if value == 0f32 && !value.is_sign_negative() { self.push_byte(0x40); return Ok(()); @@ -194,7 +191,7 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { Ok(()) } - pub fn write_f64(&mut self, value: f64) -> IonResult<()> { + pub fn write_f64(mut self, value: f64) -> IonResult<()> { if value == 0f64 && !value.is_sign_negative() { self.push_byte(0x40); return Ok(()); @@ -205,17 +202,17 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { Ok(()) } - pub fn write_decimal(&mut self, value: &Decimal) -> IonResult<()> { + pub fn write_decimal(self, value: &Decimal) -> IonResult<()> { let _encoded_size = self.encoding_buffer.encode_decimal_value(value)?; Ok(()) } - pub fn write_timestamp(&mut self, value: &Timestamp) -> IonResult<()> { + pub fn write_timestamp(self, value: &Timestamp) -> IonResult<()> { let _ = self.encoding_buffer.encode_timestamp_value(value)?; Ok(()) } - pub fn write_string>(&mut self, value: A) -> IonResult<()> { + pub fn write_string>(mut self, value: A) -> IonResult<()> { let text: &str = value.as_ref(); let encoded_length = text.len(); // The number of utf8 bytes @@ -232,7 +229,7 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { Ok(()) } - pub fn write_symbol(&mut self, value: A) -> IonResult<()> { + pub fn write_symbol(self, value: A) -> IonResult<()> { match value.as_raw_symbol_token_ref() { RawSymbolTokenRef::SymbolId(sid) => self.write_symbol_id(sid), RawSymbolTokenRef::Text(text) => IonResult::illegal_operation(format!( @@ -241,131 +238,36 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { } } - pub fn write_clob>(&mut self, value: A) -> IonResult<()> { + pub fn write_clob>(self, value: A) -> IonResult<()> { let bytes: &[u8] = value.as_ref(); // The clob type descriptor's high nibble is type code 9 self.write_lob(bytes, 0x90) } - pub fn write_blob>(&mut self, value: A) -> IonResult<()> { + pub fn write_blob>(self, value: A) -> IonResult<()> { let bytes: &[u8] = value.as_ref(); // The blob type descriptor's high nibble is type code 10 (0xA) self.write_lob(bytes, 0xA0) } - fn sexp_writer(&mut self) -> BinarySExpWriter_1_0<'_, 'top> { - const SEXP_TYPE_CODE: u8 = 0xC0; - BinarySExpWriter_1_0::new(BinaryContainerWriter_1_0::new( - SEXP_TYPE_CODE, + fn list_writer(self) -> IonResult> { + Ok(BinaryListWriter_1_0::new( self.allocator, self.encoding_buffer, )) } - fn struct_writer(&mut self) -> BinaryStructWriter_1_0<'_, 'top> { - const STRUCT_TYPE_CODE: u8 = 0xD0; - BinaryStructWriter_1_0::new(BinaryContainerWriter_1_0::new( - STRUCT_TYPE_CODE, + fn sexp_writer(self) -> IonResult> { + Ok(BinarySExpWriter_1_0::new( self.allocator, self.encoding_buffer, )) } - fn write_list(&mut self, list_fn: impl ListFn) -> IonResult<()> { - const LIST_TYPE_CODE: u8 = 0xB0; - let child_encoding_buffer = self.allocator.alloc_with(|| { - BumpVec::with_capacity_in(DEFAULT_CONTAINER_BUFFER_SIZE, self.allocator) - }); - // Create a BinaryListWriter_1_0 to pass to the user's closure. - let mut list_writer = BinaryListWriter_1_0::new(BinaryContainerWriter_1_0::new( - LIST_TYPE_CODE, - self.allocator, - child_encoding_buffer, - )); - // Pass it to the closure, allowing the user to encode child values. - list_fn(&mut list_writer)?; - // Write the appropriate opcode for a list of this length. - let encoded_length = list_writer.buffer().len(); - match encoded_length { - 0..=15 => { - let opcode = 0xB0 | encoded_length as u8; - self.push_byte(opcode); - } - _ => { - let opcode = 0xBE; // List w/VarUInt length - self.push_byte(opcode); - VarUInt::write_u64(self.encoding_buffer, encoded_length as u64)?; - } - } - self.push_bytes(list_writer.buffer()); - Ok(()) - } - fn write_sexp(&mut self, sexp_fn: impl SExpFn) -> IonResult<()> { - const SEXP_TYPE_CODE: u8 = 0xC0; - let child_encoding_buffer = self.allocator.alloc_with(|| { - BumpVec::with_capacity_in(DEFAULT_CONTAINER_BUFFER_SIZE, self.allocator) - }); - // Create a BinarySExpWriter_1_0 to pass to the user's closure. - let mut sexp_writer = BinarySExpWriter_1_0::new(BinaryContainerWriter_1_0::new( - SEXP_TYPE_CODE, - self.allocator, - child_encoding_buffer, - )); - // Pass it to the closure, allowing the user to encode child values. - sexp_fn(&mut sexp_writer)?; - // Write the appropriate opcode for a sexp of this length. - let encoded_length = sexp_writer.buffer().len(); - match encoded_length { - 0..=15 => { - let opcode = 0xC0 | encoded_length as u8; - self.push_byte(opcode); - } - _ => { - let opcode = 0xCE; // SExp w/VarUInt length - self.push_byte(opcode); - VarUInt::write_u64(self.encoding_buffer, encoded_length as u64)?; - } - } - self.push_bytes(sexp_writer.buffer()); - Ok(()) - } - fn write_struct(&mut self, struct_fn: impl StructFn) -> IonResult<()> { - const STRUCT_TYPE_CODE: u8 = 0xD0; - let child_encoding_buffer = self.allocator.alloc_with(|| { - BumpVec::with_capacity_in(DEFAULT_CONTAINER_BUFFER_SIZE, self.allocator) - }); - // Create a BinarySExpWriter_1_0 to pass to the user's closure. - let mut struct_writer = BinaryStructWriter_1_0::new(BinaryContainerWriter_1_0::new( - STRUCT_TYPE_CODE, + fn struct_writer(self) -> IonResult> { + Ok(BinaryStructWriter_1_0::new( self.allocator, - child_encoding_buffer, - )); - // Pass it to the closure, allowing the user to encode child values. - struct_fn(&mut struct_writer)?; - // Write the appropriate opcode for a struct of this length. - let encoded_length = struct_writer.buffer().len(); - match encoded_length { - 0..=15 => { - let opcode = 0xD0 | encoded_length as u8; - self.push_byte(opcode); - } - _ => { - let opcode = 0xDE; // Struct w/VarUInt length - self.push_byte(opcode); - VarUInt::write_u64(self.encoding_buffer, encoded_length as u64)?; - } - } - self.push_bytes(struct_writer.buffer()); - Ok(()) - } - fn write_eexp<'macro_id>( - &mut self, - macro_id: impl Into>, - _macro_fn: impl MacroArgsFn, - ) -> IonResult<()> { - let id = macro_id.into(); - IonResult::encoding_error(format!( - "attempted to call macro {id:?}; macros are not supported in Ion 1.0" + self.encoding_buffer, )) } } @@ -373,83 +275,41 @@ impl<'value, 'top> BinaryValueWriter_1_0<'value, 'top> { impl<'value, 'top> Sealed for BinaryValueWriter_1_0<'value, 'top> {} impl<'value, 'top> ValueWriter for BinaryValueWriter_1_0<'value, 'top> { + type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a> = BinaryAnnotatedValueWriter_1_0<'a, 'top, SymbolType> where Self: 'a; type ListWriter = BinaryListWriter_1_0<'value, 'top>; type SExpWriter = BinarySExpWriter_1_0<'value, 'top>; type StructWriter = BinaryStructWriter_1_0<'value, 'top>; - type MacroArgsWriter = Never; + type EExpWriter = Never; - delegate_value_writer_to!(closure |_self: Self| BinaryValueWriterRef_1_0(_self)); -} - -// TODO: Doc comment -pub(crate) struct BinaryValueWriterRef_1_0<'value, 'top>( - pub(crate) BinaryValueWriter_1_0<'value, 'top>, -); - -impl<'value, 'top> ValueWriter for &mut BinaryValueWriterRef_1_0<'value, 'top> { - type ListWriter = BinaryListWriter_1_0<'value, 'top>; - type SExpWriter = BinarySExpWriter_1_0<'value, 'top>; - type StructWriter = BinaryStructWriter_1_0<'value, 'top>; - type MacroArgsWriter = Never; + delegate_value_writer_to_self!(); - delegate_value_writer_to!(closure |self_: Self| &mut self_.0); -} - -pub struct BinaryAnnotatableValueWriter_1_0<'value, 'top> { - allocator: &'top BumpAllocator, - encoding_buffer: &'value mut BumpVec<'top, u8>, -} - -impl<'value, 'top> BinaryAnnotatableValueWriter_1_0<'value, 'top> { - pub fn new( - allocator: &'top BumpAllocator, - encoding_buffer: &'value mut BumpVec<'top, u8>, - ) -> BinaryAnnotatableValueWriter_1_0<'value, 'top> { - BinaryAnnotatableValueWriter_1_0 { - allocator, - encoding_buffer, - } - } -} - -impl<'value, 'top: 'value> AnnotatableValueWriter - for BinaryAnnotatableValueWriter_1_0<'value, 'top> -{ - type ValueWriter = BinaryValueWriter_1_0<'value, 'top>; - type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a> = - BinaryAnnotationsWrapperWriter<'a, 'top, SymbolType> where Self: 'a; - fn with_annotations<'a, SymbolType: AsRawSymbolTokenRef>( + fn with_annotations<'a, SymbolType: 'a + AsRawSymbolTokenRef>( self, annotations: &'a [SymbolType], ) -> Self::AnnotatedValueWriter<'a, SymbolType> where Self: 'a, { - BinaryAnnotationsWrapperWriter::new(self.allocator, annotations, self.encoding_buffer) - } - - #[inline(always)] - fn without_annotations(self) -> BinaryValueWriter_1_0<'value, 'top> { - BinaryValueWriter_1_0::new(self.allocator, self.encoding_buffer) + BinaryAnnotatedValueWriter_1_0::new(self.allocator, annotations, self.encoding_buffer) } } -pub struct BinaryAnnotationsWrapperWriter<'value, 'top, SymbolType: AsRawSymbolTokenRef> { +pub struct BinaryAnnotatedValueWriter_1_0<'value, 'top, SymbolType: AsRawSymbolTokenRef> { annotations: &'value [SymbolType], allocator: &'top BumpAllocator, output_buffer: &'value mut BumpVec<'top, u8>, } impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> - BinaryAnnotationsWrapperWriter<'value, 'top, SymbolType> + BinaryAnnotatedValueWriter_1_0<'value, 'top, SymbolType> { pub fn new( allocator: &'top BumpAllocator, annotations: &'value [SymbolType], encoding_buffer: &'value mut BumpVec<'top, u8>, - ) -> BinaryAnnotationsWrapperWriter<'value, 'top, SymbolType> { - BinaryAnnotationsWrapperWriter { + ) -> BinaryAnnotatedValueWriter_1_0<'value, 'top, SymbolType> { + BinaryAnnotatedValueWriter_1_0 { annotations, allocator, output_buffer: encoding_buffer, @@ -465,54 +325,26 @@ macro_rules! annotate_and_delegate_1_0 { () => {}; // Recurses one argument pair at a time ($value_type:ty => $method:ident, $($rest:tt)*) => { - fn $method(self, value: $value_type) -> IonResult<()> { + fn $method(mut self, value: $value_type) -> IonResult<()> { let allocator = self.allocator; - let buffer = allocator.alloc_with(|| BumpVec::new_in(allocator)); + let mut buffer = BumpVec::new_in(allocator); let value_writer = $crate::lazy::encoder::binary::v1_0::value_writer::BinaryValueWriter_1_0::new( self.allocator, - buffer, + &mut buffer, ); value_writer.$method(value)?; + println!("{}, {buffer:0x?}", stringify!($value_type)); self.annotate_encoded_value(buffer.as_slice()) } annotate_and_delegate_1_0!($($rest)*); }; } -trait EncodeValueFn: FnOnce(&mut V) -> IonResult<()> -where - for<'a> &'a mut V: ValueWriter, -{ - fn encode_value(self, value_writer: &mut V) -> IonResult<()>; -} - -impl EncodeValueFn for F -where - F: FnOnce(&mut V) -> IonResult<()>, - for<'a> &'a mut V: ValueWriter, -{ - fn encode_value(self, value_writer: &mut V) -> IonResult<()> { - self(value_writer) - } -} - impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> - BinaryAnnotationsWrapperWriter<'value, 'top, SymbolType> + BinaryAnnotatedValueWriter_1_0<'value, 'top, SymbolType> { - fn encode_annotated( - self, - encode_value_fn: impl EncodeValueFn>, - ) -> IonResult<()> { - let allocator = self.allocator; - let buffer = allocator.alloc_with(|| BumpVec::new_in(allocator)); - let value_writer = BinaryValueWriter_1_0::new(self.allocator, buffer); - let vw_ref = &mut BinaryValueWriterRef_1_0(value_writer); - encode_value_fn.encode_value(vw_ref)?; - self.annotate_encoded_value(vw_ref.0.buffer()) - } - - pub(crate) fn annotate_encoded_value(self, encoded_value: &[u8]) -> IonResult<()> { + pub(crate) fn annotate_encoded_value(&mut self, encoded_value: &[u8]) -> IonResult<()> { let mut encoded_annotations_sequence = BumpVec::new_in(self.allocator); self.encode_annotations_sequence(&mut encoded_annotations_sequence)?; @@ -556,21 +388,21 @@ impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> } impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> Sealed - for BinaryAnnotationsWrapperWriter<'value, 'top, SymbolType> + for BinaryAnnotatedValueWriter_1_0<'value, 'top, SymbolType> { // No methods, precludes implementations outside the crate. } impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> ValueWriter - for BinaryAnnotationsWrapperWriter<'value, 'top, SymbolType> + for BinaryAnnotatedValueWriter_1_0<'value, 'top, SymbolType> { + type AnnotatedValueWriter<'a, S: AsRawSymbolTokenRef + 'a> = BinaryAnnotatedValueWriter_1_0<'a, 'top, S> where Self: 'a; type ListWriter = BinaryListWriter_1_0<'value, 'top>; type SExpWriter = BinarySExpWriter_1_0<'value, 'top>; - type StructWriter = BinaryStructWriter_1_0<'value, 'top>; // Ion 1.0 - type MacroArgsWriter = Never; + type EExpWriter = Never; annotate_and_delegate_1_0!( IonType => write_null, @@ -586,25 +418,34 @@ impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> ValueWriter impl AsRef<[u8]> => write_clob, impl AsRef<[u8]> => write_blob, ); - fn write_list(self, list_fn: impl ListFn) -> IonResult<()> { - self.encode_annotated(|value_writer| value_writer.write_list(list_fn)) + fn list_writer(self) -> IonResult { + BinaryListWriter_1_0::new(self.allocator, self.output_buffer) + .with_annotations(self.annotations) + } + fn sexp_writer(self) -> IonResult { + BinarySExpWriter_1_0::new(self.allocator, self.output_buffer) + .with_annotations(self.annotations) } - fn write_sexp(self, sexp_fn: impl SExpFn) -> IonResult<()> { - self.encode_annotated(|value_writer| value_writer.write_sexp(sexp_fn)) + fn struct_writer(self) -> IonResult { + BinaryStructWriter_1_0::new(self.allocator, self.output_buffer) + .with_annotations(self.annotations) } - fn write_struct(self, struct_fn: impl StructFn) -> IonResult<()> { - self.encode_annotated(|value_writer| value_writer.write_struct(struct_fn)) + fn eexp_writer<'a>(self, _macro_id: impl Into>) -> IonResult { + IonResult::encoding_error("binary Ion 1.0 does not support macros") } - fn write_eexp<'macro_id>( + fn with_annotations<'a, S: 'a + AsRawSymbolTokenRef>( self, - macro_id: impl Into>, - _macro_fn: impl MacroArgsFn, - ) -> IonResult<()> { - let id = macro_id.into(); - IonResult::encoding_error(format!( - "attempted to call macro {id:?}; macros are not supported in Ion 1.0" - )) + annotations: &'a [S], + ) -> Self::AnnotatedValueWriter<'a, S> + where + Self: 'a, + { + BinaryAnnotatedValueWriter_1_0 { + annotations, + allocator: self.allocator, + output_buffer: self.output_buffer, + } } } @@ -612,7 +453,6 @@ impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> ValueWriter mod tests { use crate::lazy::encoder::annotate::Annotate; use crate::lazy::encoder::binary::v1_0::writer::LazyRawBinaryWriter_1_0; - use crate::lazy::encoder::value_writer::AnnotatableValueWriter; use crate::lazy::encoder::value_writer::SequenceWriter; use crate::lazy::encoder::value_writer::StructWriter; use crate::lazy::encoder::write_as_ion::WriteAsSExp; @@ -621,13 +461,12 @@ mod tests { fn writer_test( expected: &str, - mut test: impl FnMut(&mut LazyRawBinaryWriter_1_0<&mut Vec>) -> IonResult<()>, + test: impl FnOnce(&mut LazyRawBinaryWriter_1_0>) -> IonResult<()>, ) -> IonResult<()> { let expected = Element::read_all(expected)?; - let mut buffer = Vec::new(); - let mut writer = LazyRawBinaryWriter_1_0::new(&mut buffer)?; + let mut writer = LazyRawBinaryWriter_1_0::new(Vec::new())?; test(&mut writer)?; - writer.flush()?; + let buffer = writer.finish()?; let actual = Element::read_all(buffer)?; assert!( IonData::eq(&expected, &actual), @@ -647,7 +486,8 @@ mod tests { 2023-11-09T {{4AEA6g==}} "#; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { + + writer_test(expected, |writer| { writer .write(1)? .write(false)? @@ -657,20 +497,13 @@ mod tests { .write(Timestamp::with_ymd(2023, 11, 9).build()?)? .write([0xE0u8, 0x01, 0x00, 0xEA])?; Ok(()) - }; - writer_test(expected, test) + }) } #[test] fn write_empty_list() -> IonResult<()> { let expected = "[]"; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { - let value_writer = writer.value_writer(); - value_writer - .without_annotations() - .write_list(|_list| Ok(())) - }; - writer_test(expected, test) + writer_test(expected, |writer| writer.list_writer()?.end()) } #[test] @@ -688,35 +521,24 @@ mod tests { [1, 2, 3], ] "#; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { - writer - .value_writer() - .without_annotations() - .write_list(|list| { - list.write(1)? - .write(false)? - .write(3f32)? - .write("foo")? - .write(RawSymbolTokenRef::SymbolId(4))? - .write(Timestamp::with_ymd(2023, 11, 9).build()?)? - .write([0xE0u8, 0x01, 0x00, 0xEA])? - .write([1, 2, 3])?; - Ok(()) - }) - }; - writer_test(expected, test) + writer_test(expected, |writer| { + let mut list = writer.list_writer()?; + list.write(1)? + .write(false)? + .write(3f32)? + .write("foo")? + .write(RawSymbolTokenRef::SymbolId(4))? + .write(Timestamp::with_ymd(2023, 11, 9).build()?)? + .write([0xE0u8, 0x01, 0x00, 0xEA])? + .write([1, 2, 3])?; + list.end() + }) } #[test] fn write_empty_sexp() -> IonResult<()> { let expected = "()"; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { - writer - .value_writer() - .without_annotations() - .write_sexp(|_sexp| Ok(())) - }; - writer_test(expected, test) + writer_test(expected, |writer| writer.sexp_writer()?.end()) } #[test] @@ -734,35 +556,24 @@ mod tests { [1, 2, 3] ) "#; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { - writer - .value_writer() - .without_annotations() - .write_sexp(|sexp| { - sexp.write(1)? - .write(false)? - .write(3f32)? - .write("foo")? - .write(RawSymbolTokenRef::SymbolId(4))? - .write(Timestamp::with_ymd(2023, 11, 9).build()?)? - .write([0xE0u8, 0x01, 0x00, 0xEA])? - .write([1, 2, 3])?; - Ok(()) - }) - }; - writer_test(expected, test) + writer_test(expected, |writer| { + let mut sexp = writer.sexp_writer()?; + sexp.write(1)? + .write(false)? + .write(3f32)? + .write("foo")? + .write(RawSymbolTokenRef::SymbolId(4))? + .write(Timestamp::with_ymd(2023, 11, 9).build()?)? + .write([0xE0u8, 0x01, 0x00, 0xEA])? + .write([1, 2, 3])?; + sexp.end() + }) } #[test] fn write_empty_struct() -> IonResult<()> { let expected = "{}"; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { - writer - .value_writer() - .without_annotations() - .write_struct(|_struct| Ok(())) - }; - writer_test(expected, test) + writer_test(expected, |writer| writer.struct_writer()?.end()) } #[test] @@ -781,24 +592,19 @@ mod tests { $7: [1, 2, 3], } "#; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { - writer - .value_writer() - .without_annotations() - .write_struct(|struct_| { - struct_ - .write(0, 1)? - .write(1, false)? - .write(2, 3f32)? - .write(3, "foo")? - .write(4, RawSymbolTokenRef::SymbolId(4))? - .write(5, Timestamp::with_ymd(2023, 11, 9).build()?)? - .write(6, [0xE0u8, 0x01, 0x00, 0xEA])? - .write(7, [1, 2, 3])?; - Ok(()) - }) - }; - writer_test(expected, test) + writer_test(expected, |writer| { + let mut struct_ = writer.struct_writer()?; + struct_ + .write(0, 1)? + .write(1, false)? + .write(2, 3f32)? + .write(3, "foo")? + .write(4, RawSymbolTokenRef::SymbolId(4))? + .write(5, Timestamp::with_ymd(2023, 11, 9).build()?)? + .write(6, [0xE0u8, 0x01, 0x00, 0xEA])? + .write(7, [1, 2, 3])?; + struct_.end() + }) } #[test] @@ -814,7 +620,7 @@ mod tests { $ion_symbol_table::2023-11-09T $ion_1_0::{{4AEA6g==}} "#; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { + writer_test(expected, |writer| { writer .write(1.annotated_with(&[4]))? .write(false.annotated_with(&[5]))? @@ -828,8 +634,7 @@ mod tests { )? .write((&[0xE0u8, 0x01, 0x00, 0xEA][..]).annotated_with(&[2]))?; Ok(()) - }; - writer_test(expected, test) + }) } #[test] @@ -850,48 +655,38 @@ mod tests { $4::$7::(1 2 3) ) "#; - let test = |writer: &mut LazyRawBinaryWriter_1_0<&mut Vec>| { + writer_test(expected, |writer| { let empty_sequence: &[i32] = &[]; // [] - writer.write(empty_sequence)?; - - // $4::[] - writer.write(empty_sequence.annotated_with(&[4]))?; - - // $4::[1, 2, 3] - writer.write([1, 2, 3].annotated_with(&[4]))?; - - // $4::$7::[1, 2, 3] - writer.write([1, 2, 3].annotated_with(&[4, 7]))?; - - // $4::$7::[ - // $4::$7::[1, 2, 3] - // ] - writer.write([[1, 2, 3].annotated_with(&[4, 7])].annotated_with(&[4, 7]))?; - - // () - writer.write(empty_sequence.as_sexp())?; - - // $4::() - writer.write(empty_sequence.as_sexp().annotated_with(&[4]))?; - - // $4::(1 2 3) - writer.write([1, 2, 3].as_sexp().annotated_with(&[4]))?; - - // $4::$7::() - writer.write(empty_sequence.as_sexp().annotated_with(&[4, 7]))?; - - // $4::$7::( - // $4::$7::(1 2 3) - // ) - writer.write( - [[1, 2, 3].as_sexp().annotated_with(&[4, 7])] - .as_sexp() - .annotated_with(&[4, 7]), - )?; - + writer + .write(empty_sequence)? + // $4::[] + .write(empty_sequence.annotated_with(&[4]))? + // $4::[1, 2, 3] + .write([1, 2, 3].annotated_with(&[4]))? + // $4::$7::[1, 2, 3] + .write([1, 2, 3].annotated_with(&[4, 7]))? + // $4::$7::[ + // $4::$7::[1, 2, 3] + // ] + .write([[1usize, 2, 3].annotated_with(&[4, 7])].annotated_with(&[4, 7]))? + // () + .write(empty_sequence.as_sexp())? + // $4::() + .write(empty_sequence.as_sexp().annotated_with(&[4]))? + // $4::(1 2 3) + .write([1, 2, 3].as_sexp().annotated_with(&[4]))? + // $4::$7::() + .write(empty_sequence.as_sexp().annotated_with(&[4, 7]))? + // $4::$7::( + // $4::$7::(1 2 3) + // ) + .write( + [[1, 2, 3].as_sexp().annotated_with(&[4, 7])] + .as_sexp() + .annotated_with(&[4, 7]), + )?; Ok(()) - }; - writer_test(expected, test) + }) } } diff --git a/src/lazy/encoder/binary/v1_0/writer.rs b/src/lazy/encoder/binary/v1_0/writer.rs index 9aaae3e8..64e7a768 100644 --- a/src/lazy/encoder/binary/v1_0/writer.rs +++ b/src/lazy/encoder/binary/v1_0/writer.rs @@ -1,5 +1,5 @@ use crate::element::writer::WriteConfigKind; -use crate::lazy::encoder::binary::v1_0::value_writer::BinaryAnnotatableValueWriter_1_0; +use crate::lazy::encoder::binary::v1_0::value_writer::BinaryValueWriter_1_0; use crate::lazy::encoder::private::Sealed; use crate::lazy::encoder::value_writer::internal::MakeValueWriter; use crate::lazy::encoder::value_writer::SequenceWriter; @@ -84,7 +84,12 @@ impl LazyRawBinaryWriter_1_0 { Ok(()) } - pub(crate) fn value_writer(&mut self) -> BinaryAnnotatableValueWriter_1_0<'_, '_> { + pub fn finish(mut self) -> IonResult { + self.flush()?; + Ok(self.output) + } + + pub(crate) fn value_writer(&mut self) -> BinaryValueWriter_1_0<'_, '_> { let top_level = match self.encoding_buffer_ptr { // If the `encoding_buffer_ptr` is set, we already allocated an encoding buffer on // a previous call to `value_writer()`. Dereference the pointer and continue encoding @@ -99,8 +104,7 @@ impl LazyRawBinaryWriter_1_0 { buffer } }; - let annotated_value_writer = - BinaryAnnotatableValueWriter_1_0::new(&self.allocator, top_level); + let annotated_value_writer = BinaryValueWriter_1_0::new(&self.allocator, top_level); annotated_value_writer } } @@ -125,12 +129,13 @@ impl LazyRawWriter for LazyRawBinaryWriter_1_0 { delegate! { to self { fn flush(&mut self) -> IonResult<()>; + fn finish(self) -> IonResult; } } } impl MakeValueWriter for LazyRawBinaryWriter_1_0 { - type ValueWriter<'a> = BinaryAnnotatableValueWriter_1_0<'a, 'a> where Self: 'a; + type ValueWriter<'a> = BinaryValueWriter_1_0<'a, 'a> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { self.value_writer() @@ -138,5 +143,10 @@ impl MakeValueWriter for LazyRawBinaryWriter_1_0 { } impl SequenceWriter for LazyRawBinaryWriter_1_0 { + type End = W; + + fn end(self) -> IonResult { + self.finish() + } // Uses the default method implementations from SequenceWriter } diff --git a/src/lazy/encoder/binary/v1_1/container_writers.rs b/src/lazy/encoder/binary/v1_1/container_writers.rs index 29596ef6..f06dce2e 100644 --- a/src/lazy/encoder/binary/v1_1/container_writers.rs +++ b/src/lazy/encoder/binary/v1_1/container_writers.rs @@ -1,14 +1,13 @@ use bumpalo::collections::Vec as BumpVec; use bumpalo::Bump as BumpAllocator; -use delegate::delegate; use crate::lazy::encoder::binary::v1_1::flex_sym::FlexSym; -use crate::lazy::encoder::binary::v1_1::value_writer::BinaryAnnotatableValueWriter_1_1; -use crate::lazy::encoder::value_writer::internal::MakeValueWriter; -use crate::lazy::encoder::value_writer::{MacroArgsWriter, SequenceWriter, StructWriter}; +use crate::lazy::encoder::binary::v1_1::value_writer::BinaryValueWriter_1_1; +use crate::lazy::encoder::value_writer::internal::{FieldEncoder, MakeValueWriter}; +use crate::lazy::encoder::value_writer::{EExpWriter, SequenceWriter, StructWriter}; use crate::lazy::encoder::write_as_ion::WriteAsIon; use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; -use crate::{FlexUInt, IonResult, RawSymbolTokenRef, SymbolId}; +use crate::{FlexUInt, IonResult}; /// A helper type that holds fields and logic that is common to [`BinaryListWriter_1_1`], /// [`BinarySExpWriter_1_1`], and [`BinaryStructWriter_1_1`]. @@ -22,23 +21,90 @@ use crate::{FlexUInt, IonResult, RawSymbolTokenRef, SymbolId}; pub(crate) struct BinaryContainerWriter_1_1<'value, 'top> { // An allocator reference that can be shared with nested container writers allocator: &'top BumpAllocator, - // The buffer to which child values will be encoded. In the case of: - // 1. a length-prefixed container, this will be a new buffer bump-allocated specifically for this - // container. - // 2. a delimited container, this will be the parent's own encoding buffer, to which the delimited - // container start opcode has already been written. + encoder: ContainerEncodingKind<'value, 'top>, +} + +enum ContainerEncodingKind<'value, 'top> { + Delimited(DelimitedEncoder<'value, 'top>), + LengthPrefixed(LengthPrefixedEncoder<'value, 'top>), +} + +impl<'value, 'top> ContainerEncodingKind<'value, 'top> { + fn target_buffer(&mut self) -> &mut BumpVec<'top, u8> { + match self { + ContainerEncodingKind::Delimited(encoder) => encoder.buffer, + ContainerEncodingKind::LengthPrefixed(encoder) => &mut encoder.child_values_buffer, + } + } +} + +struct DelimitedEncoder<'value, 'top> { + start_opcode: u8, buffer: &'value mut BumpVec<'top, u8>, } +struct LengthPrefixedEncoder<'value, 'top> { + type_code: u8, + flex_len_type_code: u8, + parent_buffer: &'value mut BumpVec<'top, u8>, + child_values_buffer: BumpVec<'top, u8>, +} + impl<'value, 'top> BinaryContainerWriter_1_1<'value, 'top> { - pub fn new(allocator: &'top BumpAllocator, buffer: &'value mut BumpVec<'top, u8>) -> Self { - Self { allocator, buffer } + const DELIMITED_END_OPCODE: u8 = 0xF0; + + pub fn new_delimited( + start_opcode: u8, + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + buffer.push(start_opcode); + let encoder = ContainerEncodingKind::Delimited(DelimitedEncoder { + start_opcode, + buffer, + }); + Self { allocator, encoder } + } + + pub fn new_length_prefixed( + type_code: u8, + flex_len_type_code: u8, + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const DEFAULT_CAPACITY: usize = 512; + let encoder = ContainerEncodingKind::LengthPrefixed(LengthPrefixedEncoder { + type_code, + flex_len_type_code, + parent_buffer: buffer, + child_values_buffer: BumpVec::with_capacity_in(DEFAULT_CAPACITY, allocator), + }); + Self { allocator, encoder } } - /// Constructs a new [`BinaryAnnotatableValueWriter_1_1`] using this [`BinaryContainerWriter_1_1`]'s - /// allocator and targeting its buffer. - fn value_writer<'a>(&'a mut self) -> BinaryAnnotatableValueWriter_1_1<'a, 'top> { - BinaryAnnotatableValueWriter_1_1::new(self.allocator, self.buffer()) + pub fn allocator(&self) -> &'top BumpAllocator { + self.allocator + } + + /// The buffer to which this ContainerWriter encodes child values. + pub fn child_values_buffer(&mut self) -> &'_ mut BumpVec<'top, u8> { + self.encoder.target_buffer() + } + + pub fn has_delimited_containers(&self) -> bool { + matches!(self.encoder, ContainerEncodingKind::Delimited(_)) + } + + /// Constructs a new [`BinaryValueWriter_1_1`] using this [`BinaryContainerWriter_1_1`]'s + /// allocator and targeting its child values buffer. + fn value_writer<'a>(&'a mut self) -> BinaryValueWriter_1_1<'a, 'top> { + // Create a value writer that will use the same container encodings it does by default + let delimited_containers = self.has_delimited_containers(); + BinaryValueWriter_1_1::new( + self.allocator, + self.child_values_buffer(), + delimited_containers, + ) } /// Encodes the provided `value` to the [`BinaryContainerWriter_1_1`]'s buffer. @@ -48,11 +114,31 @@ impl<'value, 'top> BinaryContainerWriter_1_1<'value, 'top> { value.write_as_ion(annotated_value_writer)?; Ok(self) } - pub fn allocator(&self) -> &'top BumpAllocator { - self.allocator - } - pub fn buffer(&mut self) -> &'_ mut BumpVec<'top, u8> { - self.buffer + + pub fn end(self) -> IonResult<()> { + match self.encoder { + ContainerEncodingKind::Delimited(encoder) => { + encoder.buffer.push(Self::DELIMITED_END_OPCODE) + } + ContainerEncodingKind::LengthPrefixed(encoder) => { + let encoded_length = encoder.child_values_buffer.len(); + match encoded_length { + 0..=15 => { + let opcode = encoder.type_code | encoded_length as u8; + encoder.parent_buffer.push(opcode); + } + _ => { + let opcode = encoder.flex_len_type_code; + encoder.parent_buffer.push(opcode); + FlexUInt::write_u64(encoder.parent_buffer, encoded_length as u64)?; + } + } + encoder + .parent_buffer + .extend_from_slice_copy(encoder.child_values_buffer.as_slice()); + } + } + Ok(()) } } @@ -61,13 +147,40 @@ pub struct BinaryListWriter_1_1<'value, 'top> { } impl<'value, 'top> BinaryListWriter_1_1<'value, 'top> { - pub(crate) fn new(container_writer: BinaryContainerWriter_1_1<'value, 'top>) -> Self { + pub(crate) fn with_container_writer( + container_writer: BinaryContainerWriter_1_1<'value, 'top>, + ) -> Self { Self { container_writer } } + + pub(crate) fn new_delimited( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const DELIMITED_LIST_OPCODE: u8 = 0xF1; + let container_writer = + BinaryContainerWriter_1_1::new_delimited(DELIMITED_LIST_OPCODE, allocator, buffer); + Self::with_container_writer(container_writer) + } + + pub(crate) fn new_length_prefixed( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const LENGTH_PREFIXED_LIST_TYPE_CODE: u8 = 0xA0; + const LENGTH_PREFIXED_FLEX_LEN_LIST_TYPE_CODE: u8 = 0xFA; + let container_writer = BinaryContainerWriter_1_1::new_length_prefixed( + LENGTH_PREFIXED_LIST_TYPE_CODE, + LENGTH_PREFIXED_FLEX_LEN_LIST_TYPE_CODE, + allocator, + buffer, + ); + Self::with_container_writer(container_writer) + } } impl<'value, 'top> MakeValueWriter for BinaryListWriter_1_1<'value, 'top> { - type ValueWriter<'a> = BinaryAnnotatableValueWriter_1_1<'a, 'top> where Self: 'a; + type ValueWriter<'a> = BinaryValueWriter_1_1<'a, 'top> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { self.container_writer.value_writer() @@ -75,10 +188,16 @@ impl<'value, 'top> MakeValueWriter for BinaryListWriter_1_1<'value, 'top> { } impl<'value, 'top> SequenceWriter for BinaryListWriter_1_1<'value, 'top> { + type End = (); + fn write(&mut self, value: V) -> IonResult<&mut Self> { self.container_writer.write(value)?; Ok(self) } + + fn end(self) -> IonResult { + self.container_writer.end() + } } pub struct BinarySExpWriter_1_1<'value, 'top> { @@ -86,29 +205,62 @@ pub struct BinarySExpWriter_1_1<'value, 'top> { } impl<'value, 'top> BinarySExpWriter_1_1<'value, 'top> { - pub(crate) fn new(sequence_writer: BinaryContainerWriter_1_1<'value, 'top>) -> Self { - Self { - container_writer: sequence_writer, - } + pub(crate) fn with_container_writer( + container_writer: BinaryContainerWriter_1_1<'value, 'top>, + ) -> Self { + Self { container_writer } + } + + pub(crate) fn new_delimited( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const DELIMITED_SEXP_OPCODE: u8 = 0xF2; + let container_writer = + BinaryContainerWriter_1_1::new_delimited(DELIMITED_SEXP_OPCODE, allocator, buffer); + Self::with_container_writer(container_writer) + } + + pub(crate) fn new_length_prefixed( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const LENGTH_PREFIXED_SEXP_TYPE_CODE: u8 = 0xB0; + const LENGTH_PREFIXED_FLEX_LEN_SEXP_TYPE_CODE: u8 = 0xFB; + let container_writer = BinaryContainerWriter_1_1::new_length_prefixed( + LENGTH_PREFIXED_SEXP_TYPE_CODE, + LENGTH_PREFIXED_FLEX_LEN_SEXP_TYPE_CODE, + allocator, + buffer, + ); + Self::with_container_writer(container_writer) } } impl<'value, 'top> MakeValueWriter for BinarySExpWriter_1_1<'value, 'top> { - type ValueWriter<'a> = BinaryAnnotatableValueWriter_1_1<'a, 'top> where Self: 'a; + type ValueWriter<'a> = BinaryValueWriter_1_1<'a, 'top> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { - BinaryAnnotatableValueWriter_1_1::new( + let delimited_containers = self.container_writer.has_delimited_containers(); + BinaryValueWriter_1_1::new( self.container_writer.allocator(), - self.container_writer.buffer(), + self.container_writer.child_values_buffer(), + delimited_containers, ) } } impl<'value, 'top> SequenceWriter for BinarySExpWriter_1_1<'value, 'top> { + type End = (); + fn write(&mut self, value: V) -> IonResult<&mut Self> { self.container_writer.write(value)?; Ok(self) } + + fn end(self) -> IonResult { + self.container_writer.end() + } } pub struct BinaryStructWriter_1_1<'value, 'top> { @@ -123,15 +275,30 @@ pub struct BinaryStructWriter_1_1<'value, 'top> { impl<'value, 'top> BinaryStructWriter_1_1<'value, 'top> { pub(crate) fn new_length_prefixed( - container_writer: BinaryContainerWriter_1_1<'value, 'top>, + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, ) -> Self { + const LENGTH_PREFIXED_STRUCT_TYPE_CODE: u8 = 0xC0; + const LENGTH_PREFIXED_FLEX_LEN_STRUCT_TYPE_CODE: u8 = 0xFC; + let container_writer = BinaryContainerWriter_1_1::new_length_prefixed( + LENGTH_PREFIXED_STRUCT_TYPE_CODE, + LENGTH_PREFIXED_FLEX_LEN_STRUCT_TYPE_CODE, + allocator, + buffer, + ); Self { flex_uint_encoding: true, container_writer, } } - pub(crate) fn new_delimited(container_writer: BinaryContainerWriter_1_1<'value, 'top>) -> Self { + pub(crate) fn new_delimited( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + ) -> Self { + const DELIMITED_STRUCT_OPCODE: u8 = 0xF3; + let container_writer = + BinaryContainerWriter_1_1::new_delimited(DELIMITED_STRUCT_OPCODE, allocator, buffer); Self { // Delimited structs always use FlexSym encoding. flex_uint_encoding: false, @@ -139,94 +306,90 @@ impl<'value, 'top> BinaryStructWriter_1_1<'value, 'top> { } } - pub(crate) fn buffer(&mut self) -> &'_ mut BumpVec<'top, u8> { - self.container_writer.buffer + pub(crate) fn fields_buffer(&mut self) -> &'_ mut BumpVec<'top, u8> { + self.container_writer.child_values_buffer() } +} - #[inline] - pub fn write( - &mut self, - name: A, - value: V, - ) -> IonResult<&mut Self> { - use RawSymbolTokenRef::*; +impl<'value, 'top> FieldEncoder for BinaryStructWriter_1_1<'value, 'top> { + fn encode_field_name(&mut self, name: impl AsRawSymbolTokenRef) -> IonResult<()> { + use crate::RawSymbolTokenRef::*; match (self.flex_uint_encoding, name.as_raw_symbol_token_ref()) { // We're already in FlexSym encoding mode - (false, _) => self.write_flex_sym_field(name, value), + (false, _) => FlexSym::encode_symbol(self.fields_buffer(), name), // We're still in FlexUInt encoding mode, but this value requires FlexSym encoding - (_, Text(_)) | (_, SymbolId(0)) => - // This can only happen up to once inside a length-prefixed struct - { - self.enable_flex_sym_and_write_field(name, value) + (_, Text(_)) | (_, SymbolId(0)) => { + self.fields_buffer().push(0x01); + self.flex_uint_encoding = false; + FlexSym::encode_symbol(self.fields_buffer(), name) } // We're in FlexUInt encoding mode and can write this field without switching modes - (_, SymbolId(sid)) => self.write_flex_uint_field(sid, value), - } + (_, SymbolId(sid)) => { + FlexUInt::encode_u64(self.fields_buffer(), sid as u64); + } + }; + Ok(()) } +} - #[inline] - fn write_flex_uint_field( - &mut self, - name: SymbolId, - value: V, - ) -> IonResult<&mut Self> { - FlexUInt::encode_u64(self.buffer(), name as u64); - self.container_writer.write(value)?; - Ok(self) - } +impl<'value, 'top> MakeValueWriter for BinaryStructWriter_1_1<'value, 'top> { + type ValueWriter<'a> = BinaryValueWriter_1_1<'a, 'top> + where + Self: 'a,; - #[inline(never)] - fn enable_flex_sym_and_write_field( - &mut self, - name: A, - value: V, - ) -> IonResult<&mut Self> { - // This is the first time we're writing a FlexSym field. Emit a FlexUInt 0 to tell - // readers that we're switching from FlexUInt to FlexSym. - self.buffer().push(0x01); - self.flex_uint_encoding = false; - self.write_flex_sym_field(name, value) - } - - pub fn write_flex_sym_field( - &mut self, - name: A, - value: V, - ) -> IonResult<&mut Self> { - // Write the field name - FlexSym::encode_symbol(self.buffer(), name); - - // Write the value - self.container_writer.write(value)?; - Ok(self) + fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { + self.container_writer.value_writer() } } impl<'value, 'top> StructWriter for BinaryStructWriter_1_1<'value, 'top> { - delegate! { - to self { - fn write( - &mut self, - name: A, - value: V, - ) -> IonResult<&mut Self>; + fn end(mut self) -> IonResult<()> { + if let ContainerEncodingKind::Delimited(_) = &mut self.container_writer.encoder { + // Write the FlexSym escape (FlexUInt 0). The container writer can emit the closing + // delimited END opcode. + self.fields_buffer().push(0x01); } + self.container_writer.end() } } -pub struct BinaryMacroArgsWriter_1_1<'value, 'top> { - pub(crate) container_writer: BinaryContainerWriter_1_1<'value, 'top>, +pub struct BinaryEExpWriter_1_1<'value, 'top> { + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + delimited_containers: bool, } -impl<'value, 'top> MakeValueWriter for BinaryMacroArgsWriter_1_1<'value, 'top> { - type ValueWriter<'a> = BinaryAnnotatableValueWriter_1_1<'a, 'top> where Self: 'a; +impl<'value, 'top> BinaryEExpWriter_1_1<'value, 'top> { + pub fn new( + allocator: &'top BumpAllocator, + buffer: &'value mut BumpVec<'top, u8>, + delimited_containers: bool, + ) -> Self { + Self { + allocator, + buffer, + delimited_containers, + } + } +} + +impl<'value, 'top> MakeValueWriter for BinaryEExpWriter_1_1<'value, 'top> { + type ValueWriter<'a> = BinaryValueWriter_1_1<'a, 'top> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { - self.container_writer.value_writer() + BinaryValueWriter_1_1::new(self.allocator, self.buffer, self.delimited_containers) } } -impl<'value, 'top> SequenceWriter for BinaryMacroArgsWriter_1_1<'value, 'top> {} +impl<'value, 'top> SequenceWriter for BinaryEExpWriter_1_1<'value, 'top> { + type End = (); + + fn end(self) -> IonResult { + // Nothing to do + // TODO: When we have length-prefixed macro invocations, this will require a step to flush the buffered encoding. + Ok(()) + } +} -impl<'value, 'top> MacroArgsWriter for BinaryMacroArgsWriter_1_1<'value, 'top> {} +impl<'value, 'top> EExpWriter for BinaryEExpWriter_1_1<'value, 'top> {} diff --git a/src/lazy/encoder/binary/v1_1/value_writer.rs b/src/lazy/encoder/binary/v1_1/value_writer.rs index 89774f72..8d48f49c 100644 --- a/src/lazy/encoder/binary/v1_1/value_writer.rs +++ b/src/lazy/encoder/binary/v1_1/value_writer.rs @@ -1,23 +1,19 @@ use arrayvec::ArrayVec; use bumpalo::collections::Vec as BumpVec; use bumpalo::Bump as BumpAllocator; -use delegate::delegate; use ice_code::ice as cold_path; use num_bigint::BigInt; use num_traits::ToPrimitive; use crate::lazy::encoder::binary::v1_1::container_writers::{ - BinaryContainerWriter_1_1, BinaryListWriter_1_1, BinaryMacroArgsWriter_1_1, - BinarySExpWriter_1_1, BinaryStructWriter_1_1, + BinaryEExpWriter_1_1, BinaryListWriter_1_1, BinarySExpWriter_1_1, BinaryStructWriter_1_1, }; use crate::lazy::encoder::binary::v1_1::fixed_int::FixedInt; use crate::lazy::encoder::binary::v1_1::fixed_uint::FixedUInt; use crate::lazy::encoder::binary::v1_1::flex_sym::FlexSym; -use crate::lazy::encoder::container_fn::{ListFn, MacroArgsFn, SExpFn, StructFn}; use crate::lazy::encoder::private::Sealed; -use crate::lazy::encoder::value_writer::{ - delegate_value_writer_to_self, AnnotatableValueWriter, ValueWriter, -}; +use crate::lazy::encoder::value_writer::delegate_value_writer_to_self; +use crate::lazy::encoder::value_writer::ValueWriter; use crate::lazy::text::raw::v1_1::reader::MacroIdRef; use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; use crate::result::IonFailure; @@ -39,14 +35,15 @@ pub struct BinaryValueWriter_1_1<'value, 'top> { } impl<'value, 'top> BinaryValueWriter_1_1<'value, 'top> { - pub fn new( - allocator: &'top BumpAllocator, - encoding_buffer: &'value mut BumpVec<'top, u8>, - ) -> BinaryValueWriter_1_1<'value, 'top> { + pub fn new<'a, 'b: 'a>( + allocator: &'b BumpAllocator, + encoding_buffer: &'a mut BumpVec<'b, u8>, + delimited_containers: bool, + ) -> BinaryValueWriter_1_1<'a, 'b> { BinaryValueWriter_1_1 { allocator, encoding_buffer, - delimited_containers: false, + delimited_containers, } } @@ -55,6 +52,11 @@ impl<'value, 'top> BinaryValueWriter_1_1<'value, 'top> { self } + pub fn with_length_prefixed_containers(mut self) -> Self { + self.delimited_containers = false; + self + } + #[inline] fn push_byte(&mut self, byte: u8) { self.encoding_buffer.push(byte); @@ -578,150 +580,37 @@ impl<'value, 'top> BinaryValueWriter_1_1<'value, 'top> { Ok(()) } - fn write_list(self, list_fn: impl ListFn) -> IonResult<()> { - if self.delimited_containers { - return self.write_delimited_list(list_fn); - } - self.write_length_prefixed_list(list_fn) - } - - fn write_length_prefixed_list(mut self, list_fn: impl ListFn) -> IonResult<()> { - // We're writing a length-prefixed list, so we need to set up a space to encode the list's children. - let child_encoding_buffer = self.allocator.alloc_with(|| { - BumpVec::with_capacity_in(DEFAULT_CONTAINER_BUFFER_SIZE, self.allocator) - }); - // Create a BinaryListWriter_1_1 to pass to the user's closure. - let container_writer = - BinaryContainerWriter_1_1::new(self.allocator, child_encoding_buffer); - let mut list_writer = BinaryListWriter_1_1::new(container_writer); - // Pass it to the closure, allowing the user to encode child values. - list_fn(&mut list_writer)?; - // Write the appropriate opcode for a list of this length - let encoded_length = list_writer.container_writer.buffer().len(); - match encoded_length { - 0..=15 => { - let opcode = 0xA0 | encoded_length as u8; - self.push_byte(opcode); - } - _ => { - let opcode = 0xFA; // List w/FlexUInt length - self.push_byte(opcode); - FlexUInt::write_u64(self.encoding_buffer, encoded_length as u64)?; - } - } - self.push_bytes(list_writer.container_writer.buffer()); - Ok(()) - } - - fn write_delimited_list(self, list_fn: impl ListFn) -> IonResult<()> { - let child_encoding_buffer = self.encoding_buffer; - let container_writer = - BinaryContainerWriter_1_1::new(self.allocator, child_encoding_buffer); - let list_writer = &mut BinaryListWriter_1_1::new(container_writer); - list_writer.container_writer.buffer().push(0xF1); // Start delimited list - list_fn(list_writer)?; - list_writer.container_writer.buffer().push(0xF0); // End delimited container - Ok(()) - } - - fn write_sexp(self, sexp_fn: impl SExpFn) -> IonResult<()> { - if self.delimited_containers { - return self.write_delimited_sexp(sexp_fn); - } - self.write_length_prefixed_sexp(sexp_fn) - } - - fn write_length_prefixed_sexp(mut self, sexp_fn: impl SExpFn) -> IonResult<()> { - // We're writing a length-prefixed sexp, so we need to set up a space to encode the sexp's children. - let child_encoding_buffer = self.allocator.alloc_with(|| { - BumpVec::with_capacity_in(DEFAULT_CONTAINER_BUFFER_SIZE, self.allocator) - }); - // Create a BinarySExpWriter_1_1 to pass to the user's closure. - let container_writer = - BinaryContainerWriter_1_1::new(self.allocator, child_encoding_buffer); - let mut sexp_writer = BinarySExpWriter_1_1::new(container_writer); - // Pass it to the closure, allowing the user to encode child values. - sexp_fn(&mut sexp_writer)?; - // Write the appropriate opcode for a sexp of this length - let encoded_length = sexp_writer.container_writer.buffer().len(); - match encoded_length { - 0..=15 => { - let opcode = 0xB0 | encoded_length as u8; - self.push_byte(opcode); - } - _ => { - let opcode = 0xFB; // SExp w/FlexUInt length - self.push_byte(opcode); - FlexUInt::write_u64(self.encoding_buffer, encoded_length as u64)?; - } - } - self.push_bytes(sexp_writer.container_writer.buffer()); - Ok(()) - } - - fn write_delimited_sexp(self, sexp_fn: impl SExpFn) -> IonResult<()> { - let child_encoding_buffer = self.encoding_buffer; - let container_writer = - BinaryContainerWriter_1_1::new(self.allocator, child_encoding_buffer); - let sexp_writer = &mut BinarySExpWriter_1_1::new(container_writer); - sexp_writer.container_writer.buffer().push(0xF2); // Start delimited sexp - sexp_fn(sexp_writer)?; - sexp_writer.container_writer.buffer().push(0xF0); // End delimited container - Ok(()) - } - - fn write_struct(self, struct_fn: impl StructFn) -> IonResult<()> { - if self.delimited_containers { - self.write_delimited_struct(struct_fn) + fn list_writer(self) -> IonResult<::ListWriter> { + let writer = if self.delimited_containers { + BinaryListWriter_1_1::new_delimited(self.allocator, self.encoding_buffer) } else { - self.write_length_prefixed_struct(struct_fn) - } + BinaryListWriter_1_1::new_length_prefixed(self.allocator, self.encoding_buffer) + }; + Ok(writer) } - fn write_delimited_struct(self, struct_fn: impl StructFn) -> IonResult<()> { - let fields_encoding_buffer = self.encoding_buffer; - let container_writer = - BinaryContainerWriter_1_1::new(self.allocator, fields_encoding_buffer); - let struct_writer = &mut BinaryStructWriter_1_1::new_delimited(container_writer); - struct_writer.buffer().push(0xF3); // Start delimited Struct - struct_fn(struct_writer)?; - struct_writer.buffer().push(0xF0); // End delimited container - Ok(()) + fn sexp_writer(self) -> IonResult<::SExpWriter> { + let writer = if self.delimited_containers { + BinarySExpWriter_1_1::new_delimited(self.allocator, self.encoding_buffer) + } else { + BinarySExpWriter_1_1::new_length_prefixed(self.allocator, self.encoding_buffer) + }; + Ok(writer) } - fn write_length_prefixed_struct(mut self, struct_fn: impl StructFn) -> IonResult<()> { - // We're writing a length-prefixed struct, so we need to set up a space to encode the struct's fields. - let field_encoding_buffer = self.allocator.alloc_with(|| { - BumpVec::with_capacity_in(DEFAULT_CONTAINER_BUFFER_SIZE, self.allocator) - }); - // Create a BinaryStructWriter_1_1 to pass to the user's closure. - let container_writer = - BinaryContainerWriter_1_1::new(self.allocator, field_encoding_buffer); - let mut struct_writer = BinaryStructWriter_1_1::new_length_prefixed(container_writer); - // Pass it to the closure, allowing the user to encode field names/values. - struct_fn(&mut struct_writer)?; - // Write the appropriate opcode for a struct of this length - let encoded_length = struct_writer.buffer().len(); - match encoded_length { - 0..=15 => { - let opcode = 0xC0 | encoded_length as u8; - self.push_byte(opcode); - } - _ => { - let opcode = 0xFC; // Struct w/FlexUInt length - self.push_byte(opcode); - FlexUInt::write_u64(self.encoding_buffer, encoded_length as u64)?; - } - } - self.push_bytes(struct_writer.buffer()); - Ok(()) + fn struct_writer(self) -> IonResult<::StructWriter> { + let writer = if self.delimited_containers { + BinaryStructWriter_1_1::new_delimited(self.allocator, self.encoding_buffer) + } else { + BinaryStructWriter_1_1::new_length_prefixed(self.allocator, self.encoding_buffer) + }; + Ok(writer) } - fn write_eexp<'macro_id>( + fn eexp_writer<'a>( self, - macro_id: impl Into>, - macro_fn: impl MacroArgsFn, - ) -> IonResult<()> { + macro_id: impl Into>, + ) -> IonResult<::EExpWriter> { match macro_id.into() { MacroIdRef::LocalName(_name) => { // This would be handled by the system writer @@ -735,52 +624,26 @@ impl<'value, 'top> BinaryValueWriter_1_1<'value, 'top> { todo!("macros with addresses higher than 64"); } } - let container_writer = BinaryContainerWriter_1_1::new(self.allocator, self.encoding_buffer); - let mut args_writer = BinaryMacroArgsWriter_1_1 { container_writer }; - macro_fn(&mut args_writer) + Ok(BinaryEExpWriter_1_1::new( + self.allocator, + self.encoding_buffer, + self.delimited_containers, + )) } } impl<'value, 'top> Sealed for BinaryValueWriter_1_1<'value, 'top> {} impl<'value, 'top> ValueWriter for BinaryValueWriter_1_1<'value, 'top> { + type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a> = BinaryAnnotatedValueWriter_1_1<'a, 'top, SymbolType> where Self: 'a; type ListWriter = BinaryListWriter_1_1<'value, 'top>; type SExpWriter = BinarySExpWriter_1_1<'value, 'top>; type StructWriter = BinaryStructWriter_1_1<'value, 'top>; - type MacroArgsWriter = BinaryMacroArgsWriter_1_1<'value, 'top>; + type EExpWriter = BinaryEExpWriter_1_1<'value, 'top>; delegate_value_writer_to_self!(); -} - -pub struct BinaryAnnotatableValueWriter_1_1<'value, 'top> { - delimited_containers: bool, - allocator: &'top BumpAllocator, - encoding_buffer: &'value mut BumpVec<'top, u8>, -} - -impl<'value, 'top> BinaryAnnotatableValueWriter_1_1<'value, 'top> { - pub fn new( - allocator: &'top BumpAllocator, - encoding_buffer: &'value mut BumpVec<'top, u8>, - ) -> BinaryAnnotatableValueWriter_1_1<'value, 'top> { - BinaryAnnotatableValueWriter_1_1 { - delimited_containers: false, - allocator, - encoding_buffer, - } - } - pub fn with_delimited_containers(mut self) -> Self { - self.delimited_containers = true; - self - } -} - -impl<'value, 'top> AnnotatableValueWriter for BinaryAnnotatableValueWriter_1_1<'value, 'top> { - type ValueWriter = BinaryValueWriter_1_1<'value, 'top>; - type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a> = - BinaryAnnotatedValueWriter_1_1<'a, 'top, SymbolType> where Self: 'a; fn with_annotations<'a, SymbolType: 'a + AsRawSymbolTokenRef>( self, annotations: &'a [SymbolType], @@ -788,17 +651,7 @@ impl<'value, 'top> AnnotatableValueWriter for BinaryAnnotatableValueWriter_1_1<' where Self: 'a, { - let mut writer = - BinaryAnnotatedValueWriter_1_1::new(self.allocator, self.encoding_buffer, annotations); - writer.delimited_containers = self.delimited_containers; - writer - } - - #[inline(always)] - fn without_annotations(self) -> BinaryValueWriter_1_1<'value, 'top> { - let mut writer = BinaryValueWriter_1_1::new(self.allocator, self.encoding_buffer); - writer.delimited_containers = self.delimited_containers; - writer + BinaryAnnotatedValueWriter_1_1::new(self.allocator, self.encoding_buffer, annotations) } } @@ -811,28 +664,11 @@ macro_rules! annotate_and_delegate_1_1 { // Recurses one argument pair at a time ($value_type:ty => $method:ident, $($rest:tt)*) => { fn $method(mut self, value: $value_type) -> IonResult<()> { - match self.annotations { - [] => { - // There are no annotations; nothing to do. - } - [a] => { - // Opcode 0xE7: A single FlexSym annotation follows - self.buffer.push(0xE7); - FlexSym::encode_symbol(self.buffer, a); - } - [a1, a2] => { - // Opcode 0xE8: Two FlexSym annotations follow - self.buffer.push(0xE8); - FlexSym::encode_symbol(self.buffer, a1); - FlexSym::encode_symbol(self.buffer, a2); - } - _ => { - self.write_length_prefixed_flex_sym_annotation_sequence(); - } - } + self.encode_annotations(); // We've encoded the annotations, now create a no-annotations ValueWriter to encode the value itself. - let value_writer = $crate::lazy::encoder::binary::v1_1::value_writer::BinaryValueWriter_1_1::new(self.allocator, self.buffer); - value_writer.$method(value) + let value_writer = $crate::lazy::encoder::binary::v1_1::value_writer::BinaryValueWriter_1_1::new(self.allocator, self.buffer, self.delimited_containers); + value_writer.$method(value)?; + Ok(()) } annotate_and_delegate_1_1!($($rest)*); }; @@ -848,13 +684,7 @@ pub struct BinaryAnnotatedValueWriter_1_1<'value, 'top, SymbolType: AsRawSymbolT impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> BinaryAnnotatedValueWriter_1_1<'value, 'top, SymbolType> { - fn encode_annotated(mut self, encode_value_fn: F) -> IonResult<()> - where - F: for<'a> FnOnce(BinaryValueWriter_1_1<'value, 'top>) -> IonResult<()>, - { - // TODO: With some extra analysis, we could determine whether FlexUInt annotation encodings - // were sufficient. These are potentially slightly more compact, but cannot encode - // inline text or `$0`. For now, we simply use FlexSym encoding. + fn encode_annotations(&mut self) { match self.annotations { [] => { // There are no annotations; nothing to do. @@ -874,9 +704,6 @@ impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> self.write_length_prefixed_flex_sym_annotation_sequence(); } } - // We've encoded the annotations, now create a no-annotations ValueWriter to encode the value itself. - let value_writer = BinaryValueWriter_1_1::new(self.allocator, self.buffer); - encode_value_fn(value_writer) } fn write_flex_sym_annotation( @@ -899,6 +726,10 @@ impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> self.buffer .extend_from_slice_copy(annotations_buffer.as_slice()); } + + fn local_buffer(&mut self) -> &mut BumpVec<'top, u8> { + &mut *self.buffer + } } impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> Sealed @@ -910,10 +741,11 @@ impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> Sealed impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> ValueWriter for BinaryAnnotatedValueWriter_1_1<'value, 'top, SymbolType> { + type AnnotatedValueWriter<'a, S: AsRawSymbolTokenRef + 'a> = BinaryAnnotatedValueWriter_1_1<'a, 'top, S> where Self: 'a; type ListWriter = BinaryListWriter_1_1<'value, 'top>; type SExpWriter = BinarySExpWriter_1_1<'value, 'top>; type StructWriter = BinaryStructWriter_1_1<'value, 'top>; - type MacroArgsWriter = BinaryMacroArgsWriter_1_1<'value, 'top>; + type EExpWriter = BinaryEExpWriter_1_1<'value, 'top>; annotate_and_delegate_1_1!( IonType => write_null, @@ -930,23 +762,41 @@ impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> ValueWriter impl AsRef<[u8]> => write_blob, ); - fn write_list(self, list_fn: impl ListFn) -> IonResult<()> { - self.encode_annotated(|value_writer| value_writer.write_list(list_fn)) + fn list_writer(mut self) -> IonResult { + self.encode_annotations(); + self.value_writer().list_writer() } - fn write_sexp(self, sexp_fn: impl SExpFn) -> IonResult<()> { - self.encode_annotated(|value_writer| value_writer.write_sexp(sexp_fn)) + fn sexp_writer(mut self) -> IonResult { + self.encode_annotations(); + self.value_writer().sexp_writer() } - fn write_struct(self, struct_fn: impl StructFn) -> IonResult<()> { - self.encode_annotated(|value_writer| value_writer.write_struct(struct_fn)) + + fn struct_writer(mut self) -> IonResult { + self.encode_annotations(); + self.value_writer().struct_writer() } - fn write_eexp<'macro_id>( + + fn eexp_writer<'a>(self, macro_id: impl Into>) -> IonResult { + if !self.annotations.is_empty() { + return IonResult::encoding_error("e-expressions cannot have annotations"); + } + self.value_writer().eexp_writer(macro_id) + } + + fn with_annotations<'a, S: 'a + AsRawSymbolTokenRef>( self, - macro_id: impl Into>, - macro_fn: impl MacroArgsFn, - ) -> IonResult<()> { - // todo!() - self.encode_annotated(|value_writer| value_writer.write_eexp(macro_id, macro_fn)) + annotations: &'a [S], + ) -> Self::AnnotatedValueWriter<'a, S> + where + Self: 'a, + { + BinaryAnnotatedValueWriter_1_1 { + annotations, + allocator: self.allocator, + buffer: self.buffer, + delimited_containers: self.delimited_containers, + } } } @@ -966,7 +816,8 @@ impl<'value, 'top, SymbolType: AsRawSymbolTokenRef> } } pub(crate) fn value_writer(self) -> BinaryValueWriter_1_1<'value, 'top> { - let mut writer = BinaryValueWriter_1_1::new(self.allocator, self.buffer); + let mut writer = + BinaryValueWriter_1_1::new(self.allocator, self.buffer, self.delimited_containers); writer.delimited_containers = self.delimited_containers; writer } @@ -984,10 +835,9 @@ mod tests { use crate::lazy::encoder::annotate::{Annotate, Annotated}; use crate::lazy::encoder::binary::v1_1::writer::LazyRawBinaryWriter_1_1; - use crate::lazy::encoder::value_writer::{ - AnnotatableValueWriter, SequenceWriter, StructWriter, ValueWriter, - }; - use crate::lazy::encoder::write_as_ion::{WriteAsIonValue, WriteAsSExp}; + use crate::lazy::encoder::value_writer::ValueWriter; + use crate::lazy::encoder::value_writer::{SequenceWriter, StructWriter}; + use crate::lazy::encoder::write_as_ion::{WriteAsIon, WriteAsSExp}; use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; use crate::{ Decimal, Element, Int, IonResult, IonType, Null, RawSymbolToken, SymbolId, Timestamp, @@ -2369,17 +2219,12 @@ mod tests { for (value, expected_encoding) in test_cases { encoding_test( |writer: &mut LazyRawBinaryWriter_1_1<&mut Vec>| { - writer + let mut list = writer .value_writer() - .without_annotations() .with_delimited_containers() - .write_list(|list| { - for text in *value { - list.write_string(text)?; - } - Ok(()) - })?; - Ok(()) + .list_writer()?; + list.write_all(*value)?; + list.end() }, expected_encoding, )?; @@ -2473,17 +2318,12 @@ mod tests { for (value, expected_encoding) in test_cases { encoding_test( |writer: &mut LazyRawBinaryWriter_1_1<&mut Vec>| { - writer + let mut sexp = writer .value_writer() - .without_annotations() .with_delimited_containers() - .write_sexp(|sexp| { - for text in *value { - sexp.write_string(text)?; - } - Ok(()) - })?; - Ok(()) + .sexp_writer()?; + sexp.write_all(*value)?; + sexp.end() }, expected_encoding, )?; @@ -2493,14 +2333,13 @@ mod tests { /// A list of field name/value pairs that will be serialized as a struct in each test. type TestStruct<'a> = &'a [(RawSymbolToken, Element)]; - impl<'a> WriteAsIonValue for TestStruct<'a> { - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { - writer.write_struct(|s| { - for field in self.iter() { - s.write(&field.0, &field.1)?; - } - Ok(()) - }) + impl<'a> WriteAsIon for TestStruct<'a> { + fn write_as_ion(&self, writer: V) -> IonResult<()> { + let mut struct_writer = writer.struct_writer()?; + for (name, value) in self.iter() { + struct_writer.write(name, value)?; + } + struct_writer.end() } } @@ -2641,7 +2480,7 @@ mod tests { #[rustfmt::skip] let test_cases: &[(TestStruct, &[u8])] = &[ // Empty struct - (&[], &[0xF3, 0xF0]), + (&[], &[0xF3, 0x01, 0xF0]), // Struct with a single symbol ID field name ( &[field(4, "foo")], @@ -2653,8 +2492,8 @@ mod tests { // 3-byte symbol // ↓ f o o 0x93, 0x66, 0x6F, 0x6F, - // End delimited container - 0xF0, + // End delimited struct + 0x01, 0xF0, ], ), // Struct with multiple symbol ID field names @@ -2679,8 +2518,8 @@ mod tests { // 3-byte symbol // ↓ b a z 0x93, 0x62, 0x61, 0x7A, - // End delimited container - 0xF0, + // End delimited struct + 0x01, 0xF0, ], ), // Struct with single inline-text field name @@ -2695,8 +2534,8 @@ mod tests { // 3-byte symbol // ↓ b a r 0x93, 0x62, 0x61, 0x72, - // End delimited container - 0xF0, + // End delimited struct + 0x01, 0xF0, ], ), // Struct with multiple inline-text field names @@ -2717,8 +2556,8 @@ mod tests { // 4-byte symbol // ↓ q u u x 0x94, 0x71, 0x75, 0x75, 0x78, - // End delimited container - 0xF0, + // End delimited struct + 0x01, 0xF0, ], ), // Struct with multiple symbol ID field names followed by an inline text field name @@ -2743,8 +2582,8 @@ mod tests { // 4-byte symbol // ↓ q u u z 0x94, 0x71, 0x75, 0x75, 0x7A, - // End delimited container - 0xF0, + // End delimited struct + 0x01, 0xF0, ], ), ]; @@ -2765,7 +2604,7 @@ mod tests { #[test] fn write_annotated() -> IonResult<()> { - fn case( + fn case( value: Annotated<'_, ValueType, SymbolType>, expected_encoding: &[u8], ) -> IonResult<()> { @@ -2938,16 +2777,11 @@ mod tests { fn write_macro_invocations() -> IonResult<()> { encoding_test( |writer: &mut LazyRawBinaryWriter_1_1<&mut Vec>| { - writer - .value_writer() - .without_annotations() - .write_eexp(0, |args| { - args.write_symbol("foo")? - .write_symbol("bar")? - .write_symbol("baz")?; - Ok(()) - })?; - Ok(()) + let mut args = writer.eexp_writer(0)?; + args.write_symbol("foo")? + .write_symbol("bar")? + .write_symbol("baz")?; + args.end() }, &[ 0x00, // Invoke macro address 0 diff --git a/src/lazy/encoder/binary/v1_1/writer.rs b/src/lazy/encoder/binary/v1_1/writer.rs index 5df16a64..e2f3dd91 100644 --- a/src/lazy/encoder/binary/v1_1/writer.rs +++ b/src/lazy/encoder/binary/v1_1/writer.rs @@ -1,5 +1,5 @@ use crate::element::writer::WriteConfigKind; -use crate::lazy::encoder::binary::v1_1::value_writer::BinaryAnnotatableValueWriter_1_1; +use crate::lazy::encoder::binary::v1_1::value_writer::BinaryValueWriter_1_1; use crate::lazy::encoder::private::Sealed; use crate::lazy::encoder::value_writer::internal::MakeValueWriter; use crate::lazy::encoder::value_writer::SequenceWriter; @@ -84,9 +84,14 @@ impl LazyRawBinaryWriter_1_1 { Ok(()) } + pub fn finish(mut self) -> IonResult { + self.flush()?; + Ok(self.output) + } + // All methods called on the writer are inherently happening at the top level. At the top level, // the lifetimes `'value` and `'top` are identical. In this method signature, '_ is used for both. - pub(crate) fn value_writer(&mut self) -> BinaryAnnotatableValueWriter_1_1<'_, '_> { + pub(crate) fn value_writer(&mut self) -> BinaryValueWriter_1_1<'_, '_> { let top_level = match self.encoding_buffer_ptr { // If the `encoding_buffer_ptr` is set, we already allocated an encoding buffer on // a previous call to `value_writer()`. Dereference the pointer and continue encoding @@ -104,9 +109,12 @@ impl LazyRawBinaryWriter_1_1 { buffer } }; - let annotated_value_writer = - BinaryAnnotatableValueWriter_1_1::new(&self.allocator, top_level); - annotated_value_writer + BinaryValueWriter_1_1::new( + &self.allocator, + top_level, + // By default, writers use length-prefixed encodings. + false, + ) } } @@ -132,12 +140,13 @@ impl LazyRawWriter for LazyRawBinaryWriter_1_1 { delegate! { to self { fn flush(&mut self) -> IonResult<()>; + fn finish(self) -> IonResult; } } } impl MakeValueWriter for LazyRawBinaryWriter_1_1 { - type ValueWriter<'a> = BinaryAnnotatableValueWriter_1_1<'a, 'a> where Self: 'a; + type ValueWriter<'a> = BinaryValueWriter_1_1<'a, 'a> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { self.value_writer() @@ -145,5 +154,10 @@ impl MakeValueWriter for LazyRawBinaryWriter_1_1 { } impl SequenceWriter for LazyRawBinaryWriter_1_1 { - // Uses the default method implementations from SequenceWriter + type End = W; + + fn end(self) -> IonResult { + // Ending the top-level sequence is the same as calling `finish` on the writer itself. + self.finish() + } } diff --git a/src/lazy/encoder/container_fn.rs b/src/lazy/encoder/container_fn.rs deleted file mode 100644 index 0aa58dc0..00000000 --- a/src/lazy/encoder/container_fn.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Container population traits that allow closures to be used in places where the borrow checker -//! would normally balk due to point-in-time limitations. -//! See: - -use crate::lazy::encoder::value_writer::ValueWriter; -use crate::IonResult; - -/// Generates a trait with a single method that takes one of [`ValueWriter`]'s associated types as -/// an argument. The trait extends `FnOnce(TheAssociatedType) -> IonResult<()>`, allowing it to be -/// invoked directly as a closure. The macro also generates a blanket implementation of the trait -/// for any closures that also implement `FnOnce(TheAssociatedType) -> IonResult<()>`. -/// -/// For example: -///```text -/// container_fn_trait!(ListFn => ListWriter); -///``` -/// -/// generates a trait `ListFn` with a single method: -///```text -/// fn populate(self, writer: &mut V::ListWriter) -> IonResult<()>; -///``` -/// -/// Types that implement this trait can be invoked as though they were closures. -/// Closures with the signature `(&mut V::ListWriter) -> IonResult<()>` automatically implement -/// the trait. -/// -/// This circular arrangement allows the compiler to accept closures that would otherwise -/// require explicit Higher-Rank Trait Bounds, a feature which currently has limitations. -macro_rules! container_fn_trait { - // End of iteration - () => {}; - // Recurses one argument pair at a time - ($trait_name:ident => $assoc_type_name:ident, $($rest:tt)*) => { - pub trait $trait_name: FnOnce(&mut V::$assoc_type_name) -> IonResult<()> { - fn populate(self, writer: &mut V::$assoc_type_name) -> IonResult<()>; - } - - impl $trait_name for F - where - F: FnOnce(&mut V::$assoc_type_name) -> IonResult<()>, - { - fn populate(self, writer: &mut V::$assoc_type_name) -> IonResult<()> { - self(writer) - } - } - - container_fn_trait!($($rest)*); - }; -} - -container_fn_trait!( - ListFn => ListWriter, - SExpFn => SExpWriter, - StructFn => StructWriter, - MacroArgsFn => MacroArgsWriter, -); diff --git a/src/lazy/encoder/mod.rs b/src/lazy/encoder/mod.rs index bd541a05..30746ea4 100644 --- a/src/lazy/encoder/mod.rs +++ b/src/lazy/encoder/mod.rs @@ -10,7 +10,6 @@ use value_writer::SequenceWriter; pub mod annotate; pub mod binary; -pub(crate) mod container_fn; pub mod text; pub mod value_writer; pub mod write_as_ion; @@ -38,7 +37,7 @@ pub(crate) mod private { } /// An Ion writer without an encoding context (that is: symbol/macro tables). -pub trait LazyRawWriter: SequenceWriter { +pub trait LazyRawWriter: SequenceWriter { fn new(output: W) -> IonResult where Self: Sized; @@ -46,14 +45,15 @@ pub trait LazyRawWriter: SequenceWriter { where Self: Sized; fn flush(&mut self) -> IonResult<()>; + + fn finish(self) -> IonResult; } #[cfg(test)] mod tests { use crate::lazy::encoder::annotate::Annotate; use crate::lazy::encoder::text::LazyRawTextWriter_1_0; - use crate::lazy::encoder::value_writer::internal::MakeValueWriter; - use crate::lazy::encoder::value_writer::{StructWriter, ValueWriter}; + use crate::lazy::encoder::value_writer::{SequenceWriter, StructWriter}; use crate::symbol_ref::AsSymbolRef; use crate::{Element, IonData, IonResult, Timestamp}; @@ -66,6 +66,7 @@ mod tests { let mut writer = LazyRawTextWriter_1_0::new(&mut buffer); test(&mut writer)?; writer.flush()?; + println!("{}", String::from_utf8_lossy(buffer.as_slice())); let actual = Element::read_all(buffer)?; assert!( IonData::eq(&expected, &actual), @@ -144,16 +145,15 @@ mod tests { ] "#; let test = |writer: &mut LazyRawTextWriter_1_0<&mut Vec>| { - writer.make_value_writer().write_list(|list| { - list.write(1)? - .write(false)? - .write(3f32)? - .write("foo")? - .write("bar".as_symbol_ref())? - .write(Timestamp::with_ymd(2023, 11, 9).build()?)? - .write([0xE0u8, 0x01, 0x00, 0xEA])?; - Ok(()) - }) + let mut list = writer.list_writer()?; + list.write(1)? + .write(false)? + .write(3f32)? + .write("foo")? + .write("bar".as_symbol_ref())? + .write(Timestamp::with_ymd(2023, 11, 9).build()?)? + .write([0xE0u8, 0x01, 0x00, 0xEA])?; + list.end() }; writer_test(expected, test) } @@ -174,17 +174,16 @@ mod tests { ) "#; let test = |writer: &mut LazyRawTextWriter_1_0<&mut Vec>| { - writer.make_value_writer().write_sexp(|sexp| { - sexp.write(1)? - .write(false)? - .write(3f32)? - .write("foo")? - .write("bar".as_symbol_ref())? - .write(Timestamp::with_ymd(2023, 11, 9).build()?)? - .write([0xE0u8, 0x01, 0x00, 0xEA])? - .write([1, 2, 3])?; - Ok(()) - }) + let mut sexp = writer.sexp_writer()?; + sexp.write(1)? + .write(false)? + .write(3f32)? + .write("foo")? + .write("bar".as_symbol_ref())? + .write(Timestamp::with_ymd(2023, 11, 9).build()?)? + .write([0xE0u8, 0x01, 0x00, 0xEA])? + .write([1, 2, 3])?; + sexp.end() }; writer_test(expected, test) } @@ -203,17 +202,16 @@ mod tests { } "#; let test = |writer: &mut LazyRawTextWriter_1_0<&mut Vec>| { - writer.make_value_writer().write_struct(|struct_| { - struct_ - .write("a", 1)? - .write("b", false)? - .write("c", 3f32)? - .write("d", "foo")? - .write("e", "bar".as_symbol_ref())? - .write("f", Timestamp::with_ymd(2023, 11, 9).build()?)? - .write("g", [0xE0u8, 0x01, 0x00, 0xEA])?; - Ok(()) - }) + let mut struct_ = writer.struct_writer()?; + struct_ + .write("a", 1)? + .write("b", false)? + .write("c", 3f32)? + .write("d", "foo")? + .write("e", "bar".as_symbol_ref())? + .write("f", Timestamp::with_ymd(2023, 11, 9).build()?)? + .write("g", [0xE0u8, 0x01, 0x00, 0xEA])?; + struct_.end() }; writer_test(expected, test) } diff --git a/src/lazy/encoder/text/mod.rs b/src/lazy/encoder/text/mod.rs index 19473332..dd0889e4 100644 --- a/src/lazy/encoder/text/mod.rs +++ b/src/lazy/encoder/text/mod.rs @@ -1,7 +1,5 @@ use crate::element::writer::{WriteConfig, WriteConfigKind}; -use crate::lazy::encoder::text::value_writer::{ - TextAnnotatableValueWriter_1_0, TextValueWriter_1_0, -}; +use crate::lazy::encoder::text::value_writer::TextValueWriter_1_0; use crate::lazy::encoder::value_writer::internal::MakeValueWriter; use crate::lazy::encoder::value_writer::SequenceWriter; use crate::lazy::encoder::write_as_ion::WriteAsIon; @@ -31,7 +29,7 @@ impl LazyRawTextWriter_1_0 { /// Writes the provided data as a top-level value. pub fn write(&mut self, value: V) -> IonResult<&mut Self> { - value.write_as_ion(self.annotatable_value_writer())?; + value.write_as_ion(self.value_writer())?; write!( self.output, "{}", @@ -46,31 +44,38 @@ impl LazyRawTextWriter_1_0 { Ok(()) } - /// Helper method to construct this format's `ValueWriter` implementation. - #[inline] - fn value_writer(&mut self) -> TextValueWriter_1_0<'_, W> { - TextValueWriter_1_0::new(self, 0) + pub fn finish(mut self) -> IonResult { + self.flush()?; + Ok(self.output) } - /// Helper method to construct this format's `AnnotatedValueWriter` implementation. + /// Helper method to construct this format's `ValueWriter` implementation. #[inline] - fn annotatable_value_writer(&mut self) -> TextAnnotatableValueWriter_1_0<'_, W> { - TextAnnotatableValueWriter_1_0::new(self.value_writer()) + fn value_writer(&mut self) -> TextValueWriter_1_0<'_, W> { + TextValueWriter_1_0::new( + self, + 0, + self.whitespace_config.space_between_top_level_values, + ) } } impl SequenceWriter for LazyRawTextWriter_1_0 { - // All default method impls + type End = W; + + fn end(self) -> IonResult { + // Calling `end()` on the top level sequence is the same as calling `finish()` on the writer. + self.finish() + } } impl MakeValueWriter for LazyRawTextWriter_1_0 { - type ValueWriter<'a> = TextAnnotatableValueWriter_1_0<'a, W> + type ValueWriter<'a> = TextValueWriter_1_0<'a, W> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { - let value_writer = TextValueWriter_1_0::new(self, 0); - TextAnnotatableValueWriter_1_0::new(value_writer) + self.value_writer() } } @@ -94,6 +99,7 @@ impl LazyRawWriter for LazyRawTextWriter_1_0 { delegate! { to self { fn flush(&mut self) -> IonResult<()>; + fn finish(self) -> IonResult; } } } diff --git a/src/lazy/encoder/text/value_writer.rs b/src/lazy/encoder/text/value_writer.rs index 3bce679f..b7148100 100644 --- a/src/lazy/encoder/text/value_writer.rs +++ b/src/lazy/encoder/text/value_writer.rs @@ -1,10 +1,8 @@ -use crate::lazy::encoder::container_fn::{ListFn, MacroArgsFn, SExpFn, StructFn}; use crate::lazy::encoder::private::Sealed; use crate::lazy::encoder::text::LazyRawTextWriter_1_0; -use crate::lazy::encoder::value_writer::internal::MakeValueWriter; -use crate::lazy::encoder::value_writer::{ - delegate_value_writer_to, AnnotatableValueWriter, SequenceWriter, StructWriter, ValueWriter, -}; +use crate::lazy::encoder::value_writer::internal::{FieldEncoder, MakeValueWriter}; +use crate::lazy::encoder::value_writer::{delegate_value_writer_to, ValueWriter}; +use crate::lazy::encoder::value_writer::{SequenceWriter, StructWriter}; use crate::lazy::encoder::write_as_ion::WriteAsIon; use crate::lazy::never::Never; use crate::lazy::text::raw::v1_1::reader::MacroIdRef; @@ -21,11 +19,20 @@ use std::io::Write; pub struct TextValueWriter_1_0<'value, W: Write + 'value> { writer: &'value mut LazyRawTextWriter_1_0, depth: usize, + value_delimiter: &'static str, } impl<'value, W: Write + 'value> TextValueWriter_1_0<'value, W> { - pub fn new(writer: &'value mut LazyRawTextWriter_1_0, depth: usize) -> Self { - Self { writer, depth } + pub fn new( + writer: &'value mut LazyRawTextWriter_1_0, + depth: usize, + delimiter: &'static str, + ) -> Self { + Self { + writer, + depth, + value_delimiter: delimiter, + } } } @@ -37,37 +44,20 @@ impl<'value, W: Write> TextValueWriter_1_0<'value, W> { fn whitespace_config(&self) -> &WhitespaceConfig { self.writer.whitespace_config } -} - -pub struct TextAnnotatableValueWriter_1_0<'value, W: Write> { - value_writer: TextValueWriter_1_0<'value, W>, -} - -impl<'value, W: Write> TextAnnotatableValueWriter_1_0<'value, W> { - pub fn new(value_writer: TextValueWriter_1_0<'value, W>) -> Self { - Self { value_writer } - } -} -impl<'value, W: Write> AnnotatableValueWriter for TextAnnotatableValueWriter_1_0<'value, W> { - type ValueWriter = TextValueWriter_1_0<'value, W>; - type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a> = - TextAnnotatedValueWriter_1_0<'a, W, SymbolType> where Self: 'a; - fn with_annotations<'a, SymbolType: AsRawSymbolTokenRef>( - self, - annotations: &'a [SymbolType], - ) -> Self::AnnotatedValueWriter<'a, SymbolType> - where - Self: 'a, - { - TextAnnotatedValueWriter_1_0 { - annotations, - value_writer: self.value_writer, - } + pub fn delimiter(&self) -> &'static str { + self.value_delimiter } - fn without_annotations(self) -> TextValueWriter_1_0<'value, W> { - self.value_writer + #[inline] + fn write_delimiter_text(&mut self) -> IonResult<()> { + let space_between_nested_values = self.whitespace_config().space_between_nested_values; + let value_delimiter = self.value_delimiter; + write!( + self.output(), + "{value_delimiter}{space_between_nested_values}" + )?; + Ok(()) } } @@ -114,6 +104,7 @@ struct TextContainerWriter_1_0<'a, W: Write> { // The Ion type of the container using this TextContainerWriter_1_0. This value is only // used for more informative error messages. ion_type: IonType, + value_delimiter: &'static str, } impl<'a, W: Write> Drop for TextContainerWriter_1_0<'a, W> { @@ -135,6 +126,7 @@ impl<'a, W: Write> TextContainerWriter_1_0<'a, W> { depth: usize, ion_type: IonType, opening_delimiter: &str, + value_delimiter: &'static str, ) -> IonResult { let space_after_container_start = writer.whitespace_config.space_after_container_start; write!( @@ -146,6 +138,7 @@ impl<'a, W: Write> TextContainerWriter_1_0<'a, W> { depth, ion_type, has_been_closed: false, + value_delimiter, }) } @@ -162,18 +155,9 @@ impl<'a, W: Write> TextContainerWriter_1_0<'a, W> { /// Writes the provided value to output using its implementation of `WriteAsIon`, then writes /// the whitespace config's `space_between_nested_values`. - fn write_value( - &mut self, - value: V, - delimiter_between_values: &str, - ) -> IonResult<&mut Self> { + fn write_value(&mut self, value: V) -> IonResult<&mut Self> { self.write_indentation()?; - value.write_as_ion(self.annotatable_value_writer())?; - let space_between_nested_values = self.whitespace_config().space_between_nested_values; - write!( - self.output(), - "{delimiter_between_values}{space_between_nested_values}" - )?; + value.write_as_ion(self.value_writer())?; Ok(self) } @@ -202,13 +186,7 @@ impl<'a, W: Write> TextContainerWriter_1_0<'a, W> { TextValueWriter_1_0 { writer: self.writer, depth: self.depth, - } - } - - #[inline] - fn annotatable_value_writer(&mut self) -> TextAnnotatableValueWriter_1_0<'_, W> { - TextAnnotatableValueWriter_1_0 { - value_writer: self.value_writer(), + value_delimiter: self.value_delimiter, } } } @@ -220,13 +198,14 @@ pub struct TextListWriter_1_0<'top, W: Write> { impl<'top, W: Write> TextListWriter_1_0<'top, W> { pub fn new(writer: &'top mut LazyRawTextWriter_1_0, depth: usize) -> IonResult { - let container_writer = TextContainerWriter_1_0::new(writer, depth, IonType::List, "[")?; + let container_writer = + TextContainerWriter_1_0::new(writer, depth, IonType::List, "[", ",")?; Ok(Self { container_writer }) } /// Writes the provided data as a nested value. pub fn write(&mut self, value: V) -> IonResult<&mut Self> { - self.container_writer.write_value(value, ",")?; + self.container_writer.write_value(value)?; Ok(self) } @@ -238,17 +217,23 @@ impl<'top, W: Write> TextListWriter_1_0<'top, W> { } impl<'top, W: Write> MakeValueWriter for TextListWriter_1_0<'top, W> { - type ValueWriter<'a> = TextAnnotatableValueWriter_1_0<'a, W> where Self: 'a; + type ValueWriter<'a> = TextValueWriter_1_0<'a, W> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { - self.container_writer.annotatable_value_writer() + self.container_writer.value_writer() } } impl<'top, W: Write> SequenceWriter for TextListWriter_1_0<'top, W> { + type End = (); + fn write(&mut self, value: V) -> IonResult<&mut Self> { self.write(value) } + + fn end(self) -> IonResult { + self.end() + } } /// Incrementally encodes a potentially heterogeneously typed Ion s-expression. @@ -258,13 +243,14 @@ pub struct TextSExpWriter_1_0<'a, W: Write> { impl<'a, W: Write> TextSExpWriter_1_0<'a, W> { pub fn new(writer: &'a mut LazyRawTextWriter_1_0, depth: usize) -> IonResult { - let container_writer = TextContainerWriter_1_0::new(writer, depth, IonType::SExp, "(")?; + let container_writer = + TextContainerWriter_1_0::new(writer, depth, IonType::SExp, "(", " ")?; Ok(Self { container_writer }) } /// Writes the provided data as a nested value. pub fn write(&mut self, value: V) -> IonResult<&mut Self> { - self.container_writer.write_value(value, " ")?; + self.container_writer.write_value(value)?; Ok(self) } @@ -276,19 +262,25 @@ impl<'a, W: Write> TextSExpWriter_1_0<'a, W> { } impl<'value, W: Write> MakeValueWriter for TextSExpWriter_1_0<'value, W> { - type ValueWriter<'a> = TextAnnotatableValueWriter_1_0<'a, W> where Self: 'a; + type ValueWriter<'a> = TextValueWriter_1_0<'a, W> where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { - self.container_writer.annotatable_value_writer() + self.container_writer.value_writer() } } impl<'a, W: Write> SequenceWriter for TextSExpWriter_1_0<'a, W> { + type End = (); + delegate! { to self { fn write(&mut self, value: V) -> IonResult<&mut Self>; } } + + fn end(self) -> IonResult { + self.end() + } } /// Incrementally encodes an Ion struct. @@ -298,7 +290,8 @@ pub struct TextStructWriter_1_0<'a, W: Write> { impl<'a, W: Write> TextStructWriter_1_0<'a, W> { pub fn new(writer: &'a mut LazyRawTextWriter_1_0, depth: usize) -> IonResult { - let container_writer = TextContainerWriter_1_0::new(writer, depth, IonType::Struct, "{")?; + let container_writer = + TextContainerWriter_1_0::new(writer, depth, IonType::Struct, "{", ",")?; Ok(Self { container_writer }) } @@ -308,47 +301,77 @@ impl<'a, W: Write> TextStructWriter_1_0<'a, W> { } } -impl<'a, W: Write> StructWriter for TextStructWriter_1_0<'a, W> { - fn write( - &mut self, - name: A, - value: V, - ) -> IonResult<&mut Self> { +impl<'a, W: Write> FieldEncoder for TextStructWriter_1_0<'a, W> { + fn encode_field_name(&mut self, name: impl AsRawSymbolTokenRef) -> IonResult<()> { + // Leading indentation for the current depth + self.container_writer.write_indentation()?; // Write the field name RawTextWriter::::write_symbol_token(self.container_writer.output(), name)?; - let space_after_field_name = self .container_writer .whitespace_config() .space_after_field_name; // Write a `:` and configured trailing whitespace write!(self.container_writer.output(), ":{space_after_field_name}",)?; - // Write the field value - self.container_writer.write_value(value, ",")?; - Ok(self) + Ok(()) + } +} + +impl<'value, W: Write> MakeValueWriter for TextStructWriter_1_0<'value, W> { + type ValueWriter<'a> = TextValueWriter_1_0<'a, W> + where + Self: 'a; + + fn make_value_writer(&mut self) -> Self::ValueWriter<'_> { + TextValueWriter_1_0 { + writer: self.container_writer.writer, + depth: self.container_writer.depth, + value_delimiter: ",", + } + } +} + +impl<'value, W: Write> StructWriter for TextStructWriter_1_0<'value, W> { + fn end(self) -> IonResult<()> { + self.end() } } impl<'value, W: Write + 'value, SymbolType: AsRawSymbolTokenRef> ValueWriter for TextAnnotatedValueWriter_1_0<'value, W, SymbolType> { + type AnnotatedValueWriter<'a, S: AsRawSymbolTokenRef + 'a> = TextAnnotatedValueWriter_1_0<'a, W, S> where Self: 'a; type ListWriter = TextListWriter_1_0<'value, W>; type SExpWriter = TextSExpWriter_1_0<'value, W>; type StructWriter = TextStructWriter_1_0<'value, W>; // Ion 1.0 does not support macros - type MacroArgsWriter = Never; + type EExpWriter = Never; delegate_value_writer_to!(fallible closure |self_: Self| self_.encode_annotations()); + + fn with_annotations<'a, S: 'a + AsRawSymbolTokenRef>( + self, + annotations: &'a [S], + ) -> Self::AnnotatedValueWriter<'a, S> + where + Self: 'a, + { + TextAnnotatedValueWriter_1_0 { + annotations, + value_writer: self.value_writer, + } + } } impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> { + type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a> = TextAnnotatedValueWriter_1_0<'a, W, SymbolType> where Self: 'a; type ListWriter = TextListWriter_1_0<'value, W>; type SExpWriter = TextSExpWriter_1_0<'value, W>; type StructWriter = TextStructWriter_1_0<'value, W>; // Ion 1.0 does not support macros - type MacroArgsWriter = Never; + type EExpWriter = Never; fn write_null(mut self, ion_type: IonType) -> IonResult<()> { use crate::IonType::*; let null_text = match ion_type { @@ -367,7 +390,7 @@ impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> { Struct => "null.struct", }; write!(self.output(), "{null_text}")?; - Ok(()) + self.write_delimiter_text() } fn write_bool(mut self, value: bool) -> IonResult<()> { @@ -376,17 +399,17 @@ impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> { false => "false", }; write!(self.output(), "{bool_text}")?; - Ok(()) + self.write_delimiter_text() } fn write_i64(mut self, value: i64) -> IonResult<()> { write!(self.output(), "{value}")?; - Ok(()) + self.write_delimiter_text() } fn write_int(mut self, value: &Int) -> IonResult<()> { write!(self.output(), "{value}")?; - Ok(()) + self.write_delimiter_text() } fn write_f32(self, value: f32) -> IonResult<()> { @@ -396,7 +419,7 @@ impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> { fn write_f64(mut self, value: f64) -> IonResult<()> { if value.is_nan() { write!(self.output(), "nan")?; - return Ok(()); + return self.write_delimiter_text(); } if value.is_infinite() { @@ -405,7 +428,7 @@ impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> { } else { write!(self.output(), "-inf")?; } - return Ok(()); + return self.write_delimiter_text(); } // The {:e} formatter provided by the Display trait writes floats using scientific @@ -413,33 +436,33 @@ impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> { // See: https://github.com/rust-lang/rust/issues/20596 if value == 0.0f64 && value.is_sign_negative() { write!(self.output(), "-0e0")?; - return Ok(()); + return self.write_delimiter_text(); } write!(self.output(), "{value:e}")?; - Ok(()) + self.write_delimiter_text() } fn write_decimal(mut self, value: &Decimal) -> IonResult<()> { write!(self.output(), "{value}")?; - Ok(()) + self.write_delimiter_text() } fn write_timestamp(mut self, value: &Timestamp) -> IonResult<()> { write!(self.output(), "{value}")?; - Ok(()) + self.write_delimiter_text() } fn write_string(mut self, value: impl AsRef) -> IonResult<()> { - write!(self.output(), "\"")?; + write!(self.output(), "\"",)?; RawTextWriter::::write_escaped_text_body(self.output(), value)?; write!(self.output(), "\"")?; - Ok(()) + self.write_delimiter_text() } fn write_symbol(mut self, value: impl AsRawSymbolTokenRef) -> IonResult<()> { RawTextWriter::::write_symbol_token(self.output(), value)?; - Ok(()) + self.write_delimiter_text() } fn write_clob(mut self, value: impl AsRef<[u8]>) -> IonResult<()> { @@ -455,7 +478,7 @@ impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> { } write!(self.output(), "{}", ClobShim(value.as_ref()))?; - Ok(()) + self.write_delimiter_text() } fn write_blob(mut self, value: impl AsRef<[u8]>) -> IonResult<()> { @@ -465,33 +488,32 @@ impl<'value, W: Write> ValueWriter for TextValueWriter_1_0<'value, W> { // should be inserted. // * The closing }} from a text Ion blob, with each brace doubled to escape it. write!(self.output(), "{{{{{}}}}}", base64::encode(value))?; - Ok(()) + self.write_delimiter_text() } - fn write_list(self, list_fn: impl ListFn) -> IonResult<()> { - let mut list_writer = TextListWriter_1_0::new(self.writer, self.depth + 1)?; - list_fn(&mut list_writer)?; - list_writer.end() + fn list_writer(self) -> IonResult { + TextListWriter_1_0::new(self.writer, self.depth + 1) + } + fn sexp_writer(self) -> IonResult { + TextSExpWriter_1_0::new(self.writer, self.depth + 1) } - fn write_sexp(self, sexp_fn: impl SExpFn) -> IonResult<()> { - let mut sexp_writer = TextSExpWriter_1_0::new(self.writer, self.depth + 1)?; - sexp_fn(&mut sexp_writer)?; - sexp_writer.end() + fn struct_writer(self) -> IonResult { + TextStructWriter_1_0::new(self.writer, self.depth + 1) } - fn write_struct(self, struct_fn: impl StructFn) -> IonResult<()> { - let mut struct_writer = TextStructWriter_1_0::new(self.writer, self.depth + 1)?; - struct_fn(&mut struct_writer)?; - struct_writer.end() + fn eexp_writer<'a>(self, _macro_id: impl Into>) -> IonResult { + IonResult::encoding_error("macros are not supported in Ion 1.0") } - fn write_eexp<'macro_id>( + fn with_annotations<'a, SymbolType: 'a + AsRawSymbolTokenRef>( self, - macro_id: impl Into>, - _macro_fn: impl MacroArgsFn, - ) -> IonResult<()> { - let id = macro_id.into(); - IonResult::encoding_error(format!( - "attempted to call macro {id:?}; macros are not supported in Ion 1.0" - )) + annotations: &'a [SymbolType], + ) -> Self::AnnotatedValueWriter<'a, SymbolType> + where + Self: 'a, + { + TextAnnotatedValueWriter_1_0 { + annotations, + value_writer: self, + } } } diff --git a/src/lazy/encoder/value_writer.rs b/src/lazy/encoder/value_writer.rs index 7e5e399c..07858b39 100644 --- a/src/lazy/encoder/value_writer.rs +++ b/src/lazy/encoder/value_writer.rs @@ -1,54 +1,48 @@ -use crate::lazy::encoder::value_writer::internal::MakeValueWriter; -use crate::lazy::encoder::write_as_ion::{WriteAsIon, WriteAsIonValue}; +use crate::lazy::encoder::value_writer::internal::{FieldEncoder, MakeValueWriter}; +use crate::lazy::encoder::write_as_ion::WriteAsIon; use crate::lazy::text::raw::v1_1::reader::MacroIdRef; use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; -use crate::{Decimal, Int, IonResult, IonType, Timestamp}; -use delegate::delegate; +use crate::{Decimal, Int, IonResult, IonType, RawSymbolTokenRef, SymbolId, Timestamp}; pub mod internal { - use crate::lazy::encoder::value_writer::AnnotatableValueWriter; + use crate::lazy::encoder::value_writer::ValueWriter; + use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; + use crate::IonResult; pub trait MakeValueWriter { - type ValueWriter<'a>: AnnotatableValueWriter + type ValueWriter<'a>: ValueWriter where Self: 'a; fn make_value_writer(&mut self) -> Self::ValueWriter<'_>; } -} - -/// One-shot methods that take a (possibly empty) sequence of annotations to encode and return a ValueWriter. -pub trait AnnotatableValueWriter: Sized { - type ValueWriter: ValueWriter; - type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a>: ValueWriter - where - Self: 'a; - /// Writes the provided annotations to the output stream and returns a [`ValueWriter`] that can - /// be used to serialize the value itself. - /// - /// If there are no annotations, use [`Self::without_annotations`] instead. This method will loop - /// over the empty sequence, incurring minor performance overhead while `without_annotations` - /// is a true no-op. - fn with_annotations<'a, SymbolType: AsRawSymbolTokenRef>( - self, - annotations: &'a [SymbolType], - ) -> Self::AnnotatedValueWriter<'a, SymbolType> - where - Self: 'a; - /// Performs no operations and returns a [`ValueWriter`]. - fn without_annotations(self) -> Self::ValueWriter; + /// A (private) prerequisite for [`StructWriter`] implementations. + pub trait FieldEncoder { + /// Encodes the field name portion of a field. + /// + /// For binary implementations, this is typically an encoding primitive (`VarUInt`, + /// `FlexUInt`, or `FlexSym`). + /// + /// For text implementations, this typically includes indentation, a symbol token representing + /// the field name itself, and the delimiting `:`. + fn encode_field_name(&mut self, name: impl AsRawSymbolTokenRef) -> IonResult<()>; + } } -pub trait MacroArgsWriter: SequenceWriter { +pub trait EExpWriter: SequenceWriter { // TODO: methods for writing tagless encodings } pub trait ValueWriter: Sized { - type ListWriter: SequenceWriter; - type SExpWriter: SequenceWriter; + type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a>: ValueWriter + where + Self: 'a; + type ListWriter: SequenceWriter; + type SExpWriter: SequenceWriter; type StructWriter: StructWriter; - type MacroArgsWriter: MacroArgsWriter; + type EExpWriter: EExpWriter; + fn write_null(self, ion_type: IonType) -> IonResult<()>; fn write_bool(self, value: bool) -> IonResult<()>; fn write_i64(self, value: i64) -> IonResult<()>; @@ -62,20 +56,41 @@ pub trait ValueWriter: Sized { fn write_clob(self, value: impl AsRef<[u8]>) -> IonResult<()>; fn write_blob(self, value: impl AsRef<[u8]>) -> IonResult<()>; - fn write_list(self, list_fn: impl ListFn) -> IonResult<()>; + fn list_writer(self) -> IonResult; + fn sexp_writer(self) -> IonResult; + fn struct_writer(self) -> IonResult; + fn eexp_writer<'a>(self, macro_id: impl Into>) -> IonResult; - fn write_sexp(self, sexp_fn: impl SExpFn) -> IonResult<()>; + fn with_annotations<'a, SymbolType: 'a + AsRawSymbolTokenRef>( + self, + annotations: &'a [SymbolType], + ) -> Self::AnnotatedValueWriter<'a, SymbolType> + where + Self: 'a; - fn write_struct(self, struct_fn: impl StructFn) -> IonResult<()>; + fn write(self, value: impl WriteAsIon) -> IonResult<()> { + value.write_as_ion(self) + } - fn write_eexp<'macro_id>( - self, - _macro_id: impl Into>, - _macro_fn: impl MacroArgsFn, - ) -> IonResult<()>; + fn write_list>(self, values: I) -> IonResult<()> { + let mut list = self.list_writer()?; + list.write_all(values)?; + list.end() + } + + fn write_sexp>(self, values: I) -> IonResult<()> { + let mut sexp = self.sexp_writer()?; + sexp.write_all(values)?; + sexp.end() + } - fn write(self, value: impl WriteAsIonValue) -> IonResult<()> { - value.write_as_ion_value(self) + fn write_struct>( + self, + values: I, + ) -> IonResult<()> { + let mut strukt = self.struct_writer()?; + strukt.write_all(values)?; + strukt.end() } } @@ -116,7 +131,7 @@ macro_rules! delegate_value_writer_to { // // If no arguments are passed, trait method calls are delegated to inherent impl methods on `self`. () => { - $crate::lazy::encoder::value_writer::delegate_value_writer_to!(closure |self_: Self| self_); + $crate::lazy::encoder::value_writer::delegate_value_writer_to!(closure std::convert::identity); }; // If an identifier is passed, it is treated as the name of a subfield of `self`. ($name:ident) => { @@ -127,7 +142,7 @@ macro_rules! delegate_value_writer_to { // In order to forward this call to the `fallible closure` pattern, the provided closure is // wrapped in another closure that wraps the closure's output in IonResult::Ok(_). The // compiler can eliminate the redundant closure call. - $crate::lazy::encoder::value_writer::delegate_value_writer_to!(fallible closure |self_: Self| { + $crate::lazy::encoder::value_writer::delegate_value_writer_to!(fallible closure |self_| { let infallible_closure = $f; $crate::IonResult::Ok(infallible_closure(self_)) }); @@ -136,7 +151,7 @@ macro_rules! delegate_value_writer_to { // will return. Otherwise, trait method calls are delegated to the `Ok(_)` value. (fallible closure $f:expr) => { // The `self` keyword can only be used within the `delegate!` proc macro. - delegate! { + delegate::delegate! { to {let f = $f; f(self)?} { fn write_null(self, ion_type: IonType) -> IonResult<()>; fn write_bool(self, value: bool) -> IonResult<()>; @@ -150,22 +165,13 @@ macro_rules! delegate_value_writer_to { fn write_symbol(self, value: impl AsRawSymbolTokenRef) -> IonResult<()>; fn write_clob(self, value: impl AsRef<[u8]>) -> IonResult<()>; fn write_blob(self, value: impl AsRef<[u8]>) -> IonResult<()>; - fn write_list(self, list_fn: impl $crate::lazy::encoder::container_fn::ListFn) -> IonResult<()>; - fn write_sexp( - self, - sexp_fn: impl $crate::lazy::encoder::container_fn::SExpFn, - ) -> IonResult<()>; - fn write_struct( + fn list_writer(self) -> IonResult; + fn sexp_writer(self) -> IonResult; + fn struct_writer(self) -> IonResult; + fn eexp_writer<'a>( self, - struct_fn: impl $crate::lazy::encoder::container_fn::StructFn, - ) -> IonResult<()>; - fn write_eexp< - 'macro_id, - >( - self, - macro_id: impl Into>, - macro_fn: impl $crate::lazy::encoder::container_fn::MacroArgsFn, - ) -> IonResult<()>; + macro_id: impl Into>, + ) -> IonResult; } } }; @@ -180,31 +186,102 @@ macro_rules! delegate_value_writer_to_self { }; } -use crate::lazy::encoder::container_fn::{ListFn, MacroArgsFn, SExpFn, StructFn}; pub(crate) use delegate_value_writer_to; pub(crate) use delegate_value_writer_to_self; -impl ValueWriter for V -where - V: AnnotatableValueWriter, +pub struct FieldWriter<'annotations, 'field, StructWriterType, FieldNameType, AnnotationsType> { + name: FieldNameType, + annotations: &'annotations [AnnotationsType], + struct_writer: &'field mut StructWriterType, +} + +impl<'annotations, 'field, StructWriterType: StructWriter> + FieldWriter<'annotations, 'field, StructWriterType, RawSymbolTokenRef<'field>, SymbolId> { - type ListWriter = ::ListWriter; - type SExpWriter = ::SExpWriter; - type StructWriter = ::StructWriter; - type MacroArgsWriter = ::MacroArgsWriter; + pub fn new( + name: RawSymbolTokenRef<'field>, + struct_writer: &'field mut StructWriterType, + ) -> Self { + Self { + name, + annotations: &[], + struct_writer, + } + } +} - delegate_value_writer_to!(closure |self_: Self| { - self_.without_annotations() +impl< + 'annotations, + 'field, + StructWriterType: StructWriter, + FieldNameType: AsRawSymbolTokenRef, + AnnotationsType: AsRawSymbolTokenRef, + > ValueWriter + for FieldWriter<'annotations, 'field, StructWriterType, FieldNameType, AnnotationsType> +{ + type AnnotatedValueWriter<'a, NewAnnotationsType: AsRawSymbolTokenRef + 'a> = FieldWriter<'a, 'field, StructWriterType, FieldNameType, NewAnnotationsType> + where + Self: 'a; + type ListWriter = + <::ValueWriter<'field> as ValueWriter>::ListWriter; + type SExpWriter = + <::ValueWriter<'field> as ValueWriter>::SExpWriter; + type StructWriter = + <::ValueWriter<'field> as ValueWriter>::StructWriter; + type EExpWriter = + <::ValueWriter<'field> as ValueWriter>::EExpWriter; + + delegate_value_writer_to!(fallible closure |self_: Self| { + self_.struct_writer.encode_field_name(self_.name)?; + let value_writer = self_.struct_writer.make_value_writer(); + IonResult::Ok(value_writer) }); + + fn with_annotations<'a, S: 'a + AsRawSymbolTokenRef>( + self, + annotations: &'a [S], + ) -> Self::AnnotatedValueWriter<'a, S> + where + Self: 'a, + { + FieldWriter { + name: self.name, + annotations, + struct_writer: self.struct_writer, + } + } } -pub trait StructWriter { +pub trait StructWriter: FieldEncoder + MakeValueWriter + Sized { /// Writes a struct field using the provided name/value pair. fn write( &mut self, name: A, value: V, - ) -> IonResult<&mut Self>; + ) -> IonResult<&mut Self> { + self.encode_field_name(name)?; + value.write_as_ion(self.make_value_writer())?; + Ok(self) + } + + fn write_all<'a, A: AsRawSymbolTokenRef, V: WriteAsIon, I: IntoIterator>( + &mut self, + fields: I, + ) -> IonResult<&mut Self> { + for field in fields { + self.write(field.0, field.1)?; + } + Ok(self) + } + + fn field<'a>( + &'a mut self, + name: impl Into>, + ) -> FieldWriter<'_, 'a, Self, RawSymbolTokenRef<'a>, SymbolId> { + FieldWriter::new(name.into(), self) + } + + fn end(self) -> IonResult<()>; } /// Takes a series of `TYPE => METHOD` pairs, generating a function for each that calls the @@ -215,7 +292,7 @@ macro_rules! delegate_and_return_self { // Recurses one argument pair at a time ($value_type:ty => $method:ident, $($rest:tt)*) => { fn $method(&mut self, value: $value_type) -> IonResult<&mut Self> { - self.value_writer().without_annotations().$method(value)?; + self.value_writer().$method(value)?; Ok(self) } delegate_and_return_self!($($rest)*); @@ -223,18 +300,20 @@ macro_rules! delegate_and_return_self { } pub trait SequenceWriter: MakeValueWriter { + /// The type returned by the [`end`](Self::end) method. + /// + /// For top-level writers, this can be any resource(s) owned by the writer that need to survive + /// after the writer is dropped. (For example, a `BufWriter` or `Vec` serving as the output.) + /// + /// Containers and E-expressions must use `()`. + // ^^^ This constraint could be loosened if needed, but it requires using verbose references + // to `::End` in a variety of APIs. + type End; + fn value_writer(&mut self) -> Self::ValueWriter<'_> { ::make_value_writer(self) } - fn annotate<'a, A: AsRawSymbolTokenRef>( - &'a mut self, - annotations: &'a [A], - ) -> <::ValueWriter<'_> as AnnotatableValueWriter>::AnnotatedValueWriter<'a, A> - { - self.value_writer().with_annotations(annotations) - } - /// Writes a value in the current context (list, s-expression, or stream) and upon success /// returns another reference to `self` to enable method chaining. fn write(&mut self, value: V) -> IonResult<&mut Self> { @@ -242,6 +321,21 @@ pub trait SequenceWriter: MakeValueWriter { Ok(self) } + fn write_all>( + &mut self, + values: I, + ) -> IonResult<&mut Self> { + for value in values { + self.write(value)?; + } + Ok(self) + } + + /// Closes out the sequence being written. Delimited writers can use this opportunity to emit + /// a sentinel value, and length-prefixed writers can flush any buffered data to the output + /// buffer. + fn end(self) -> IonResult; + // Creates functions that delegate to the ValueWriter method of the same name but which then // return `self` so it can be re-used/chained. delegate_and_return_self!( @@ -259,15 +353,22 @@ pub trait SequenceWriter: MakeValueWriter { impl AsRef<[u8]> => write_blob, ); - // XXX: For now, it's not possible to offer versions of `write_list`, `write_sexp`, - // `write_struct` or `write_eexp`. This is due to a point-in-time limitation in the borrow checker[1]. - // It is still possible to call (e.g.) - // self.value_writer().list_writer(...) - // as a workaround. - // [1]: https://blog.rust-lang.org/2022/10/28/gats-stabilization.html#implied-static-requirement-from-higher-ranked-trait-bounds - // - // The ValueWriter implementation of these methods moves `self`. In contrast, all of the methods - // in the SequenceWriter interface take `&mut self`, which adds another lifetime to the mix. The - // borrow checker is not currently able to tell that `&mut self`'s lifetime will outlive the - // closure argument's. + fn list_writer(&mut self) -> IonResult< as ValueWriter>::ListWriter> { + self.value_writer().list_writer() + } + + fn sexp_writer(&mut self) -> IonResult< as ValueWriter>::SExpWriter> { + self.value_writer().sexp_writer() + } + + fn struct_writer(&mut self) -> IonResult< as ValueWriter>::StructWriter> { + self.value_writer().struct_writer() + } + + fn eexp_writer<'a>( + &'a mut self, + macro_id: impl Into>, + ) -> IonResult< as ValueWriter>::EExpWriter> { + self.value_writer().eexp_writer(macro_id) + } } diff --git a/src/lazy/encoder/write_as_ion.rs b/src/lazy/encoder/write_as_ion.rs index 457092ec..59573fc6 100644 --- a/src/lazy/encoder/write_as_ion.rs +++ b/src/lazy/encoder/write_as_ion.rs @@ -1,13 +1,13 @@ //! Defines traits that allow Rust values to be serialized as Ion. //! //! In order for a [`LazyRawWriter`](crate::lazy::encoder::LazyRawWriter) to serialize a Rust value -//! as Ion, that Rust type must implement [`WriteAsIonValue`] and may optionally also +//! as Ion, that Rust type must implement [`WriteAsIon`] and may optionally also //! implement [`WriteAsIon`]. //! -//! [`WriteAsIonValue`] allows the implementor to map the Rust value to an unannotated Ion value. +//! [`WriteAsIon`] allows the implementor to map the Rust value to an unannotated Ion value. //! -//! [`WriteAsIon`] builds on [`WriteAsIonValue`], offering a staged writer that requires the -//! implementor to specify what annotations to write before delegating to [`WriteAsIonValue`] +//! [`WriteAsIon`] builds on [`WriteAsIon`], offering a staged writer that requires the +//! implementor to specify what annotations to write before delegating to [`WriteAsIon`] //! to serialize the value itself. //! //! Types that do not explicitly implement [`WriteAsIon`] will fall back to a blanket implementation @@ -16,51 +16,29 @@ //! provided by the [`Annotate`](crate::lazy::encoder::annotate::Annotate) trait. use std::marker::PhantomData; -use crate::lazy::encoder::value_writer::{ - AnnotatableValueWriter, SequenceWriter, StructWriter, ValueWriter, -}; +use crate::lazy::encoder::value_writer::ValueWriter; use crate::{ Blob, Clob, Decimal, Element, Int, IonResult, Null, RawSymbolToken, RawSymbolTokenRef, Symbol, SymbolRef, Timestamp, Value, }; /// Defines how a Rust type should be serialized as Ion in terms of the methods available -/// on [`ValueWriter`]. To annotate instances of your type with a sequence of text values, -/// implement the [`WriteAsIon`] trait instaed. -pub trait WriteAsIonValue { - fn write_as_ion_value(&self, writer: V) -> IonResult<()>; -} - -/// Defines how a Rust type should be serialized as Ion in terms of the methods available -/// on [`AnnotatableValueWriter`] and [`ValueWriter`]. +/// on [`ValueWriter`]. pub trait WriteAsIon { - fn write_as_ion(&self, writer: V) -> IonResult<()>; + fn write_as_ion(&self, writer: V) -> IonResult<()>; } impl WriteAsIon for &Element { - fn write_as_ion(&self, writer: V) -> IonResult<()> { + fn write_as_ion(&self, writer: V) -> IonResult<()> { if self.annotations().is_empty() { - self.value() - .write_as_ion_value(writer.without_annotations()) + self.value().write_as_ion(writer) } else { - self.value().write_as_ion_value( - writer.with_annotations(&Vec::from_iter(self.annotations().iter())), - ) + self.value() + .write_as_ion(writer.with_annotations(self.annotations().as_ref())) } } } -// Any type that does not define `WriteAsIon` itself will use this blanket implementation that does -// not write any annotations. -impl WriteAsIon for T -where - T: WriteAsIonValue, -{ - fn write_as_ion(&self, writer: V) -> IonResult<()> { - self.write_as_ion_value(writer.without_annotations()) - } -} - // ===== WriteAsIonValue implementations for common types ===== macro_rules! impl_write_as_ion_value { @@ -68,9 +46,9 @@ macro_rules! impl_write_as_ion_value { () => {}; // The caller defined an expression to write other than `self` (e.g. `*self`, `*self.0`, etc) ($target_type:ty => $method:ident with $self:ident as $value:expr, $($rest:tt)*) => { - impl WriteAsIonValue for $target_type { + impl WriteAsIon for $target_type { #[inline] - fn write_as_ion_value(&$self, writer: V) -> IonResult<()> { + fn write_as_ion(&$self, writer: V) -> IonResult<()> { writer.$method($value) } } @@ -78,9 +56,9 @@ macro_rules! impl_write_as_ion_value { }; // We're writing the expression `self` ($target_type:ty => $method:ident, $($rest:tt)*) => { - impl WriteAsIonValue for $target_type { + impl WriteAsIon for $target_type { #[inline] - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { + fn write_as_ion(&self, writer: V) -> IonResult<()> { writer.$method(self) } } @@ -117,46 +95,41 @@ impl_write_as_ion_value!( Clob => write_clob, ); -impl<'b> WriteAsIonValue for RawSymbolTokenRef<'b> { +impl<'b> WriteAsIon for RawSymbolTokenRef<'b> { #[inline] - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { + fn write_as_ion(&self, writer: V) -> IonResult<()> { writer.write_symbol(self) } } -impl<'b> WriteAsIonValue for SymbolRef<'b> { +impl<'b> WriteAsIon for SymbolRef<'b> { #[inline] - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { + fn write_as_ion(&self, writer: V) -> IonResult<()> { writer.write_symbol(self) } } -impl WriteAsIonValue for [u8; N] { - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { +impl WriteAsIon for [u8; N] { + fn write_as_ion(&self, writer: V) -> IonResult<()> { writer.write_blob(self) } } -impl WriteAsIonValue for &T { +impl WriteAsIon for &T { #[inline] - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { - (*self).write_as_ion_value(writer) + fn write_as_ion(&self, writer: V) -> IonResult<()> { + (*self).write_as_ion(writer) } } macro_rules! impl_write_as_ion_value_for_iterable { ($iterable:ty, $item:ident $(, const $n:ident: $n_type:ty)?) => { - impl<$item $(, const $n: $n_type)?> WriteAsIonValue for $iterable + impl<'a, $item $(, const $n: $n_type)?> WriteAsIon for $iterable where - for<'a> &'a $item: WriteAsIon, + $item: WriteAsIon + 'a, { - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { - writer.write_list(|list: &mut V::ListWriter| { - for value in self.iter() { - list.write(value)?; - } - Ok(()) - }) + fn write_as_ion(&self, writer: V) -> IonResult<()> { + writer.write_list(self.into_iter()) } } }; @@ -216,17 +189,13 @@ impl SExpTypeHint { macro_rules! impl_write_as_ion_value_for_sexp_type_hint { ($iterable:ty, $item:ident $(, const $n:ident: $n_type:ty)?) => { - impl<$item $(, const $n: $n_type)?> WriteAsIonValue for SExpTypeHint<$iterable, $item> + impl<$item $(, const $n: $n_type)?> WriteAsIon for SExpTypeHint<$iterable, $item> where + $item: WriteAsIon, for<'a> &'a $item: WriteAsIon, { - fn write_as_ion_value(&self, writer: V) -> IonResult<()> { - writer.write_sexp(|sexp| { - for value in self.values.iter() { - sexp.write(value)?; - } - Ok(()) - }) + fn write_as_ion(&self, writer: V) -> IonResult<()> { + writer.write_sexp((&self.values).into_iter()) } } }; @@ -236,8 +205,8 @@ impl_write_as_ion_value_for_sexp_type_hint!(Vec, T); impl_write_as_ion_value_for_sexp_type_hint!(&[T], T); impl_write_as_ion_value_for_sexp_type_hint!([T; N], T, const N: usize); -impl WriteAsIonValue for Value { - fn write_as_ion_value(&self, value_writer: V) -> IonResult<()> { +impl WriteAsIon for Value { + fn write_as_ion(&self, value_writer: V) -> IonResult<()> { match self { Value::Null(i) => value_writer.write_null(*i), Value::Bool(b) => value_writer.write_bool(*b), @@ -249,24 +218,9 @@ impl WriteAsIonValue for Value { Value::String(s) => value_writer.write_string(s), Value::Clob(c) => value_writer.write_clob(c), Value::Blob(b) => value_writer.write_blob(b), - Value::List(l) => value_writer.write_list(|list| { - for value in l { - list.write(value)?; - } - Ok(()) - }), - Value::SExp(s) => value_writer.write_sexp(|sexp| { - for value in s { - sexp.write(value)?; - } - Ok(()) - }), - Value::Struct(s) => value_writer.write_struct(|struct_value| { - for (k, v) in s { - struct_value.write(k, v)?; - } - Ok(()) - }), + Value::List(l) => value_writer.write_list(l), + Value::SExp(s) => value_writer.write_sexp(s), + Value::Struct(s) => value_writer.write_struct(s.iter()), } } } diff --git a/src/lazy/never.rs b/src/lazy/never.rs index bc8c1273..7b6d3aab 100644 --- a/src/lazy/never.rs +++ b/src/lazy/never.rs @@ -1,15 +1,13 @@ use std::fmt::Debug; use crate::lazy::decoder::{LazyDecoder, LazyRawValueExpr}; -use crate::lazy::encoder::value_writer::internal::MakeValueWriter; -use crate::lazy::encoder::value_writer::{ - AnnotatableValueWriter, MacroArgsWriter, SequenceWriter, StructWriter, -}; -use crate::lazy::encoder::write_as_ion::WriteAsIon; +use crate::lazy::encoder::value_writer::internal::{FieldEncoder, MakeValueWriter}; +use crate::lazy::encoder::value_writer::{delegate_value_writer_to_self, ValueWriter}; +use crate::lazy::encoder::value_writer::{EExpWriter, SequenceWriter, StructWriter}; use crate::lazy::expanded::macro_evaluator::{MacroExpr, RawEExpression}; use crate::lazy::text::raw::v1_1::reader::MacroIdRef; use crate::raw_symbol_token_ref::AsRawSymbolTokenRef; -use crate::IonResult; +use crate::{Decimal, Int, IonResult, IonType, Timestamp}; /// An uninhabited type that signals to the compiler that related code paths are not reachable. #[derive(Debug, Copy, Clone)] @@ -38,34 +36,23 @@ impl<'top, D: LazyDecoder> From for MacroExpr<'top, D> { } } -impl AnnotatableValueWriter for Never { - type ValueWriter = Never; - type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a> = Never where Self: 'a; +impl SequenceWriter for Never { + type End = (); - fn with_annotations<'a, SymbolType: AsRawSymbolTokenRef>( - self, - _annotations: &'a [SymbolType], - ) -> Self::AnnotatedValueWriter<'a, SymbolType> - where - Self: 'a, - { - unreachable!("AnnotatableValueWriter::with_annotations in Never") + fn end(self) -> IonResult<()> { + unreachable!("SequenceWriter::end() in Never") } +} - fn without_annotations(self) -> Self::ValueWriter { - unreachable!("AnnotatableValueWriter::without_annotations in Never") +impl FieldEncoder for Never { + fn encode_field_name(&mut self, _name: impl AsRawSymbolTokenRef) -> IonResult<()> { + unreachable!("FieldEncoder::encode_field_name in Never") } } -impl SequenceWriter for Never {} - impl StructWriter for Never { - fn write( - &mut self, - _name: A, - _value: V, - ) -> IonResult<&mut Self> { - unreachable!("StructWriter::write in Never") + fn end(self) -> IonResult<()> { + unreachable!("StructWriter::end in Never") } } @@ -77,4 +64,24 @@ impl MakeValueWriter for Never { } } -impl MacroArgsWriter for Never {} +impl EExpWriter for Never {} + +impl ValueWriter for Never { + type AnnotatedValueWriter<'a, SymbolType: AsRawSymbolTokenRef + 'a> = Never where Self: 'a; + type ListWriter = Never; + type SExpWriter = Never; + type StructWriter = Never; + type EExpWriter = Never; + + delegate_value_writer_to_self!(); + + fn with_annotations<'a, SymbolType: 'a + AsRawSymbolTokenRef>( + self, + _annotations: &'a [SymbolType], + ) -> Self::AnnotatedValueWriter<'a, SymbolType> + where + Self: 'a, + { + unreachable!("Never as MutRefValueWriter") + } +} diff --git a/src/raw_symbol_token_ref.rs b/src/raw_symbol_token_ref.rs index 67b54ffe..14d795b1 100644 --- a/src/raw_symbol_token_ref.rs +++ b/src/raw_symbol_token_ref.rs @@ -77,8 +77,32 @@ impl AsRawSymbolTokenRef for RawSymbolToken { } } +impl<'a> From<&'a RawSymbolToken> for RawSymbolTokenRef<'a> { + fn from(value: &'a RawSymbolToken) -> Self { + value.as_raw_symbol_token_ref() + } +} + +impl<'a, 'b> From<&'a RawSymbolTokenRef<'b>> for RawSymbolTokenRef<'b> { + fn from(value: &'a RawSymbolTokenRef<'b>) -> Self { + value.clone() + } +} + impl<'a> From<&'a str> for RawSymbolTokenRef<'a> { fn from(value: &'a str) -> Self { RawSymbolTokenRef::Text(Cow::Borrowed(value)) } } + +impl<'a> From for RawSymbolTokenRef<'a> { + fn from(value: SymbolId) -> Self { + RawSymbolTokenRef::SymbolId(value) + } +} + +impl<'a> From<&'a Symbol> for RawSymbolTokenRef<'a> { + fn from(value: &'a Symbol) -> Self { + value.as_raw_symbol_token_ref() + } +}