use crate::wire::{self, Tag};
use thiserror::Error;
#[allow(clippy::missing_errors_doc)]
pub trait Sink {
type Error;
fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Error)]
pub enum WriteError {
#[error("output buffer capacity exceeded")]
BufferFull,
}
pub struct SliceSink<'a> {
buf: &'a mut [u8],
len: usize,
}
impl<'a> SliceSink<'a> {
#[must_use]
pub fn new(buf: &'a mut [u8]) -> Self {
Self { buf, len: 0 }
}
#[must_use]
pub fn written(&self) -> &[u8] {
&self.buf[..self.len]
}
}
impl Sink for SliceSink<'_> {
type Error = WriteError;
#[inline]
fn write(&mut self, bytes: &[u8]) -> Result<(), WriteError> {
let end = self.len.checked_add(bytes.len()).ok_or(WriteError::BufferFull)?;
let dst = self.buf.get_mut(self.len..end).ok_or(WriteError::BufferFull)?;
dst.copy_from_slice(bytes);
self.len = end;
Ok(())
}
}
#[cfg(feature = "alloc")]
impl Sink for alloc::vec::Vec<u8> {
type Error = core::convert::Infallible;
#[inline]
fn write(&mut self, bytes: &[u8]) -> Result<(), core::convert::Infallible> {
self.extend_from_slice(bytes);
Ok(())
}
}
#[cfg(feature = "std")]
pub struct IoSink<W>(pub W);
#[cfg(feature = "std")]
impl<W: std::io::Write> Sink for IoSink<W> {
type Error = std::io::Error;
#[inline]
fn write(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> {
self.0.write_all(bytes)
}
}
pub struct Writer<S: Sink> {
sink: S,
}
#[allow(
clippy::missing_errors_doc,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap
)]
impl<S: Sink> Writer<S> {
pub const fn new(sink: S) -> Self {
Self { sink }
}
pub fn into_inner(self) -> S {
self.sink
}
#[inline]
pub fn write_header(&mut self) -> Result<(), S::Error> {
self.sink.write(&[wire::MAJOR_VERSION, wire::MINOR_VERSION])
}
#[inline]
pub fn write_byte(&mut self, byte: u8) -> Result<(), S::Error> {
self.sink.write(core::slice::from_ref(&byte))
}
#[inline]
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), S::Error> {
self.sink.write(bytes)
}
#[inline]
pub fn write_tag(&mut self, tag: Tag) -> Result<(), S::Error> {
self.write_byte(tag.byte())
}
#[inline]
pub fn write_int(&mut self, value: i32) -> Result<(), S::Error> {
let mut buf = [0u8; 5];
let encoded = wire::encode_int(value, &mut buf);
self.write_bytes(encoded)
}
#[inline]
pub fn write_len(&mut self, len: u32) -> Result<(), S::Error> {
self.write_int(len as i32)
}
#[inline]
pub fn write_chunk(&mut self, bytes: &[u8]) -> Result<(), S::Error> {
self.write_len(bytes.len() as u32)?;
self.write_bytes(bytes)
}
#[inline]
pub fn write_nil(&mut self) -> Result<(), S::Error> {
self.write_tag(Tag::Nil)
}
#[inline]
pub fn write_bool(&mut self, value: bool) -> Result<(), S::Error> {
self.write_tag(if value { Tag::True } else { Tag::False })
}
#[inline]
pub fn write_fixnum(&mut self, value: i32) -> Result<(), S::Error> {
self.write_tag(Tag::Fixnum)?;
self.write_int(value)
}
pub fn write_bignum(&mut self, negative: bool, magnitude_le: &[u8]) -> Result<(), S::Error> {
self.write_tag(Tag::Bignum)?;
self.write_byte(if negative {
wire::SIGN_NEGATIVE
} else {
wire::SIGN_POSITIVE
})?;
let word_len = magnitude_le.len().div_ceil(2);
self.write_len(word_len as u32)?;
self.write_bytes(magnitude_le)?;
if magnitude_le.len() % 2 == 1 {
self.write_byte(0)?;
}
Ok(())
}
#[inline]
pub fn write_float(&mut self, ascii: &[u8]) -> Result<(), S::Error> {
self.write_tag(Tag::Float)?;
self.write_chunk(ascii)
}
#[inline]
pub fn write_string_bytes(&mut self, bytes: &[u8]) -> Result<(), S::Error> {
self.write_tag(Tag::String)?;
self.write_chunk(bytes)
}
#[inline]
pub fn write_regexp(&mut self, source: &[u8], options: u8) -> Result<(), S::Error> {
self.write_tag(Tag::Regexp)?;
self.write_chunk(source)?;
self.write_byte(options)
}
#[inline]
pub fn write_class_name(&mut self, bytes: &[u8]) -> Result<(), S::Error> {
self.write_tag(Tag::Class)?;
self.write_chunk(bytes)
}
#[inline]
pub fn write_module_name(&mut self, bytes: &[u8], old: bool) -> Result<(), S::Error> {
self.write_tag(if old { Tag::ModuleOld } else { Tag::Module })?;
self.write_chunk(bytes)
}
#[inline]
pub fn write_symbol_new(&mut self, bytes: &[u8]) -> Result<(), S::Error> {
self.write_tag(Tag::Symbol)?;
self.write_chunk(bytes)
}
#[inline]
pub fn write_symbol_link(&mut self, idx: u32) -> Result<(), S::Error> {
self.write_tag(Tag::SymLink)?;
self.write_len(idx)
}
#[inline]
pub fn write_object_link(&mut self, idx: u32) -> Result<(), S::Error> {
self.write_tag(Tag::Link)?;
self.write_len(idx)
}
#[inline]
pub fn write_array_header(&mut self, len: u32) -> Result<(), S::Error> {
self.write_tag(Tag::Array)?;
self.write_len(len)
}
#[inline]
pub fn write_hash_header(&mut self, len: u32, has_default: bool) -> Result<(), S::Error> {
self.write_tag(if has_default { Tag::HashDefault } else { Tag::Hash })?;
self.write_len(len)
}
#[inline]
pub fn write_ivar_wrap_tag(&mut self) -> Result<(), S::Error> {
self.write_tag(Tag::Ivar)
}
#[inline]
pub fn write_extended_tag(&mut self) -> Result<(), S::Error> {
self.write_tag(Tag::Extended)
}
#[inline]
pub fn write_uclass_tag(&mut self) -> Result<(), S::Error> {
self.write_tag(Tag::UClass)
}
}