use super::stream_reader::{CodePairValue, DxfCodePair, DxfStreamReader};
use crate::error::{DxfError, Result};
use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
pub const BINARY_SENTINEL: &[u8] = b"AutoCAD Binary DXF\r\n\x1a\x00";
pub struct DxfBinaryReader<R: Read + Seek> {
reader: BufReader<R>,
peeked_pair: Option<DxfCodePair>,
use_single_byte_codes: bool,
str_buf: Vec<u8>,
}
impl<R: Read + Seek> DxfBinaryReader<R> {
pub fn new(mut reader: BufReader<R>) -> Result<Self> {
let mut sentinel = [0u8; 22]; reader.read_exact(&mut sentinel)?;
if sentinel != BINARY_SENTINEL {
return Err(DxfError::Parse("Invalid binary DXF sentinel".to_string()));
}
let mut probe = [0u8; 2];
reader.read_exact(&mut probe)?;
reader.seek(SeekFrom::Start(BINARY_SENTINEL.len() as u64))?;
let use_single_byte_codes = probe[0] == 0 && probe[1] >= 0x20 && probe[1] < 0x7F;
Ok(Self {
reader,
peeked_pair: None,
use_single_byte_codes,
str_buf: Vec::with_capacity(256),
})
}
fn read_pair_internal(&mut self) -> Result<Option<DxfCodePair>> {
let code = if self.use_single_byte_codes {
let mut code_byte = [0u8; 1];
match self.reader.read_exact(&mut code_byte) {
Ok(_) => {},
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(e.into()),
}
if code_byte[0] == 255 {
let mut ext_code = [0u8; 2];
self.reader.read_exact(&mut ext_code)?;
i16::from_le_bytes(ext_code) as i32
} else {
code_byte[0] as i32
}
} else {
let mut code_bytes = [0u8; 2];
match self.reader.read_exact(&mut code_bytes) {
Ok(_) => {},
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(e.into()),
}
i16::from_le_bytes(code_bytes) as i32
};
self.read_pair_for_code(code)
}
fn read_null_terminated_string(&mut self) -> Result<String> {
self.str_buf.clear();
self.reader.read_until(0, &mut self.str_buf)?;
if self.str_buf.last() == Some(&0) {
self.str_buf.pop();
}
let s = match std::str::from_utf8(&self.str_buf) {
Ok(s) => s.to_owned(),
Err(_) => String::from_utf8_lossy(&self.str_buf).into_owned(),
};
self.str_buf.clear(); Ok(s)
}
fn read_pair_for_code(&mut self, code: i32) -> Result<Option<DxfCodePair>> {
use crate::io::dxf::GroupCodeValueType;
let value_type = GroupCodeValueType::from_raw_code(code);
let pair = match value_type {
GroupCodeValueType::String => {
let s = self.read_null_terminated_string()?;
DxfCodePair::new(code, s)
}
GroupCodeValueType::Double => {
let mut bytes = [0u8; 8];
self.reader.read_exact(&mut bytes)?;
let value = f64::from_le_bytes(bytes);
let mut buf = ryu::Buffer::new();
let value_string = buf.format(value).to_owned();
DxfCodePair::new_typed(code, value_string, CodePairValue::Double(value))
}
GroupCodeValueType::Int16 | GroupCodeValueType::Byte => {
let mut bytes = [0u8; 2];
self.reader.read_exact(&mut bytes)?;
let value = i16::from_le_bytes(bytes);
let mut buf = itoa::Buffer::new();
let value_string = buf.format(value).to_owned();
DxfCodePair::new_typed(code, value_string, CodePairValue::Int(value as i64))
}
GroupCodeValueType::Int32 => {
let mut bytes = [0u8; 4];
self.reader.read_exact(&mut bytes)?;
let value = i32::from_le_bytes(bytes);
let mut buf = itoa::Buffer::new();
let value_string = buf.format(value).to_owned();
DxfCodePair::new_typed(code, value_string, CodePairValue::Int(value as i64))
}
GroupCodeValueType::Int64 => {
let mut bytes = [0u8; 8];
self.reader.read_exact(&mut bytes)?;
let value = i64::from_le_bytes(bytes);
let mut buf = itoa::Buffer::new();
let value_string = buf.format(value).to_owned();
DxfCodePair::new_typed(code, value_string, CodePairValue::Int(value))
}
GroupCodeValueType::Bool => {
let mut byte = [0u8; 1];
self.reader.read_exact(&mut byte)?;
let value = byte[0] != 0;
let value_string = if value { "1" } else { "0" }.to_owned();
DxfCodePair::new_typed(code, value_string, CodePairValue::Bool(value))
}
GroupCodeValueType::BinaryData => {
let mut len_byte = [0u8; 1];
self.reader.read_exact(&mut len_byte)?;
let length = len_byte[0] as usize;
let mut data = vec![0u8; length];
if length > 0 {
self.reader.read_exact(&mut data)?;
}
const HEX_CHARS: &[u8; 16] = b"0123456789ABCDEF";
let mut hex = String::with_capacity(length * 2);
for &b in &data {
hex.push(HEX_CHARS[(b >> 4) as usize] as char);
hex.push(HEX_CHARS[(b & 0x0F) as usize] as char);
}
DxfCodePair::new(code, hex)
}
GroupCodeValueType::Handle | _ => {
let s = self.read_null_terminated_string()?;
DxfCodePair::new(code, s)
}
};
Ok(Some(pair))
}
}
impl<R: Read + Seek> DxfStreamReader for DxfBinaryReader<R> {
fn read_pair(&mut self) -> Result<Option<DxfCodePair>> {
if let Some(pair) = self.peeked_pair.take() {
return Ok(Some(pair));
}
self.read_pair_internal()
}
fn peek_code(&mut self) -> Result<Option<i32>> {
if let Some(ref pair) = self.peeked_pair {
return Ok(Some(pair.code));
}
if let Some(pair) = self.read_pair_internal()? {
let code = pair.code;
self.peeked_pair = Some(pair);
Ok(Some(code))
} else {
Ok(None)
}
}
fn push_back(&mut self, pair: DxfCodePair) {
self.peeked_pair = Some(pair);
}
fn reset(&mut self) -> Result<()> {
self.reader.seek(SeekFrom::Start(0))?;
self.peeked_pair = None;
let mut sentinel = [0u8; 22];
self.reader.read_exact(&mut sentinel)?;
if sentinel != BINARY_SENTINEL {
return Err(DxfError::Parse("Invalid binary DXF sentinel".to_string()));
}
let mut probe = [0u8; 2];
self.reader.read_exact(&mut probe)?;
self.reader.seek(SeekFrom::Start(BINARY_SENTINEL.len() as u64))?;
Ok(())
}
}