use std::io::Write;
use bumpalo::collections::Vec as BumpVec;
use bumpalo::Bump as BumpAllocator;
use delegate::delegate;
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;
use crate::lazy::encoder::value_writer_config::ValueWriterConfig;
use crate::lazy::encoder::write_as_ion::WriteAsIon;
use crate::lazy::encoder::writer::WriterMacroTable;
use crate::lazy::encoder::LazyRawWriter;
use crate::lazy::encoding::Encoding;
use crate::unsafe_helpers::{mut_ref_to_ptr, ptr_to_mut_ref, ptr_to_ref};
use crate::write_config::{WriteConfig, WriteConfigKind};
use crate::{ContextWriter, IonResult, IonVersion, MacroTable};
pub struct LazyRawBinaryWriter_1_1<W: Write> {
output: W,
allocator: BumpAllocator,
macros: WriterMacroTable,
encoding_buffer_ptr: Option<*mut ()>,
}
const DEFAULT_BUMP_SIZE: usize = 16 * 1024;
impl<W: Write> LazyRawBinaryWriter_1_1<W> {
pub fn new(mut output: W) -> IonResult<Self> {
output.write_all(&[0xE0, 0x01, 0x01, 0xEA])?;
Ok(Self {
output,
allocator: BumpAllocator::with_capacity(DEFAULT_BUMP_SIZE),
macros: WriterMacroTable::new(MacroTable::with_system_macros(IonVersion::v1_1)),
encoding_buffer_ptr: None,
})
}
pub fn write<V: WriteAsIon>(&mut self, value: V) -> IonResult<&mut Self> {
value.write_as_ion(self.value_writer())?;
Ok(self)
}
pub fn flush(&mut self) -> IonResult<()> {
let Self {
output,
allocator,
encoding_buffer_ptr,
..
} = self;
if let Some(ptr) = encoding_buffer_ptr {
let encoding_buffer = unsafe { ptr_to_ref::<'_, BumpVec<'_, u8>>(*ptr).as_slice() };
output.write_all(encoding_buffer)?;
output.flush()?;
}
self.encoding_buffer_ptr = None;
allocator.reset();
Ok(())
}
pub(crate) fn value_writer(&mut self) -> BinaryValueWriter_1_1<'_, '_> {
let top_level = match self.encoding_buffer_ptr {
Some(ptr) => unsafe { ptr_to_mut_ref::<'_, BumpVec<'_, u8>>(ptr) },
None => {
let buffer: &mut BumpVec<'_, u8> = self.allocator.alloc_with(|| {
BumpVec::with_capacity_in(DEFAULT_BUMP_SIZE / 2, &self.allocator)
});
self.encoding_buffer_ptr = Some(mut_ref_to_ptr(buffer));
unsafe { ptr_to_mut_ref::<'_, BumpVec<'_, u8>>(self.encoding_buffer_ptr.unwrap()) }
}
};
BinaryValueWriter_1_1::new(
&self.allocator,
top_level,
ValueWriterConfig::default(),
&self.macros,
)
}
}
impl<W: Write> Sealed for LazyRawBinaryWriter_1_1<W> {}
impl<W: Write> LazyRawWriter<W> for LazyRawBinaryWriter_1_1<W> {
fn new(output: W) -> IonResult<Self> {
Self::new(output)
}
fn build<E: Encoding>(config: WriteConfig<E>, output: W) -> IonResult<Self>
where
Self: Sized,
{
match &config.kind {
WriteConfigKind::Text(_) => {
unreachable!("Text writer can not be created from binary encoding")
}
WriteConfigKind::Binary(_) => LazyRawBinaryWriter_1_1::new(output),
}
}
delegate! {
to self {
fn flush(&mut self) -> IonResult<()>;
}
}
fn output(&self) -> &W {
&self.output
}
fn output_mut(&mut self) -> &mut W {
&mut self.output
}
fn macro_table(&self) -> &WriterMacroTable {
&self.macros
}
fn macro_table_mut(&mut self) -> Option<&mut WriterMacroTable> {
Some(&mut self.macros)
}
fn write_version_marker(&mut self) -> IonResult<()> {
self.output.write_all(&[0xE0, 0x01, 0x01, 0xEA])?;
Ok(())
}
}
impl<W: Write> ContextWriter for LazyRawBinaryWriter_1_1<W> {
type NestedValueWriter<'a>
= BinaryValueWriter_1_1<'a, 'a>
where
Self: 'a;
}
impl<W: Write> MakeValueWriter for LazyRawBinaryWriter_1_1<W> {
fn make_value_writer(&mut self) -> Self::NestedValueWriter<'_> {
self.value_writer()
}
}
impl<W: Write> SequenceWriter for LazyRawBinaryWriter_1_1<W> {
type Resources = W;
fn close(mut self) -> IonResult<Self::Resources> {
self.flush()?;
Ok(self.output)
}
}
#[cfg(test)]
mod tests {
use crate::{v1_1, IonResult};
#[test]
fn flush_clears_encoding_buffer() -> IonResult<()> {
use crate::Writer;
let mut writer = Writer::new(v1_1::Binary, Vec::new()).expect("BinaryWriter::new() failed");
writer.write(42)?;
writer.flush()?;
let expected_output = writer.output().clone();
writer.flush()?;
assert_eq!(
*writer.output(),
expected_output,
"actual\n{:x?}\nwas not eq to\n{:x?}",
writer.output(),
expected_output
);
Ok(())
}
}