use crate::sink::Sink;
const SENTINEL: &[u8] = b"AutoCAD Binary DXF\r\n\x1a\0";
pub trait Output {
type Error;
fn write_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error>;
}
impl<T: Output> Output for &mut T {
type Error = T::Error;
fn write_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
T::write_all(self, bytes)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BufferOverflow;
impl Output for &mut [u8] {
type Error = BufferOverflow;
fn write_all(&mut self, bytes: &[u8]) -> Result<(), BufferOverflow> {
if bytes.len() > self.len() {
return Err(BufferOverflow);
}
let (head, tail) = core::mem::take(self).split_at_mut(bytes.len());
head.copy_from_slice(bytes);
*self = tail;
Ok(())
}
}
impl Output for alloc::vec::Vec<u8> {
type Error = core::convert::Infallible;
fn write_all(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
self.extend_from_slice(bytes);
Ok(())
}
}
#[cfg(feature = "std")]
pub struct IoOutput<W>(pub W);
#[cfg(feature = "std")]
impl<W: std::io::Write> Output for IoOutput<W> {
type Error = std::io::Error;
fn write_all(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> {
self.0.write_all(bytes)
}
}
pub struct BinarySink<W> {
output: W,
}
impl<W> BinarySink<W> {
pub fn new(output: W) -> Self {
Self { output }
}
pub fn into_inner(self) -> W {
self.output
}
}
impl<W: Output> Sink for BinarySink<W> {
type Error = W::Error;
fn sentinel(&mut self) -> Result<(), Self::Error> {
self.output.write_all(SENTINEL)
}
fn group_code(&mut self, code: u16) -> Result<(), Self::Error> {
self.output.write_all(&code.to_le_bytes())
}
fn string(&mut self, value: &[u8]) -> Result<(), Self::Error> {
self.output.write_all(value)?;
self.output.write_all(&[0])
}
fn boolean(&mut self, value: bool) -> Result<(), Self::Error> {
self.output.write_all(&[value as u8])
}
fn int16(&mut self, value: i16) -> Result<(), Self::Error> {
self.output.write_all(&value.to_le_bytes())
}
fn int32(&mut self, value: i32) -> Result<(), Self::Error> {
self.output.write_all(&value.to_le_bytes())
}
fn int64(&mut self, value: i64) -> Result<(), Self::Error> {
self.output.write_all(&value.to_le_bytes())
}
fn double(&mut self, value: f64) -> Result<(), Self::Error> {
self.output.write_all(&value.to_le_bytes())
}
fn binary_chunk(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.output.write_all(&[data.len() as u8])?;
self.output.write_all(data)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sink::Sink;
use alloc::vec::Vec;
#[test]
fn sentinel_bytes() {
let mut out = Vec::new();
BinarySink::new(&mut out).sentinel().unwrap();
assert_eq!(out, b"AutoCAD Binary DXF\r\n\x1a\0");
assert_eq!(out.len(), 22);
}
#[test]
fn group_code_encoding() {
let mut out = Vec::new();
BinarySink::new(&mut out).group_code(0).unwrap();
assert_eq!(out, &[0x00, 0x00]);
let mut out = Vec::new();
BinarySink::new(&mut out).group_code(254).unwrap();
assert_eq!(out, &[0xFE, 0x00]);
let mut out = Vec::new();
BinarySink::new(&mut out).group_code(310).unwrap();
assert_eq!(out, &[0x36, 0x01]);
let mut out = Vec::new();
BinarySink::new(&mut out).group_code(1004).unwrap();
assert_eq!(out, &[0xEC, 0x03]);
}
#[test]
fn string_null_terminated() {
let mut out = Vec::new();
BinarySink::new(&mut out).string(b"SECTION").unwrap();
assert_eq!(out, b"SECTION\0");
}
#[test]
fn boolean_values() {
let mut out = Vec::new();
{
let mut sink = BinarySink::new(&mut out);
sink.boolean(false).unwrap();
sink.boolean(true).unwrap();
}
assert_eq!(out, &[0x00, 0x01]);
}
#[test]
fn integer_values() {
let mut out = Vec::new();
BinarySink::new(&mut out).int16(256).unwrap();
assert_eq!(out, &[0x00, 0x01]);
let mut out = Vec::new();
BinarySink::new(&mut out).int32(0x01020304).unwrap();
assert_eq!(out, &[0x04, 0x03, 0x02, 0x01]);
let mut out = Vec::new();
BinarySink::new(&mut out).int64(1).unwrap();
assert_eq!(out, &[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
}
#[test]
fn double_value() {
let mut out = Vec::new();
BinarySink::new(&mut out).double(1.0).unwrap();
assert_eq!(out, &1.0f64.to_le_bytes());
assert_eq!(out.len(), 8);
}
#[test]
fn binary_chunk_value() {
let mut out = Vec::new();
BinarySink::new(&mut out).binary_chunk(b"Hello").unwrap();
assert_eq!(out, &[5, b'H', b'e', b'l', b'l', b'o']);
}
#[test]
fn slice_output() {
let mut buf = [0u8; 32];
let mut slice = &mut buf[..];
{
let mut sink = BinarySink::new(&mut slice);
sink.group_code(0).unwrap();
sink.string(b"EOF").unwrap();
}
assert_eq!(&buf[..6], &[0x00, 0x00, b'E', b'O', b'F', 0x00]);
}
#[test]
fn slice_output_overflow() {
let mut buf = [0u8; 2];
let mut slice = &mut buf[..];
let mut sink = BinarySink::new(&mut slice);
assert_eq!(sink.string(b"too long").unwrap_err(), BufferOverflow);
}
}