use std::io::Write;
use std::fs::File;
use std::mem;
use byteorder::{ByteOrder, BigEndian, LittleEndian, WriteBytesExt};
use anyhow::bail;
use log::debug;
use crate::{DMImage, Result};
use crate::dmtypes::*;
use crate::taggroup_entries::{Key, Tag, TagGroup};
pub trait DMImageWriteByVersion {
fn write_by_version(dmwriter: &mut DMImageWriter, val: usize) -> Result<usize>;
fn write_by_version_to_buffer(buffer: &mut Vec<u8>, val: usize) -> Result<usize>;
fn write_header(dmwriter: &mut DMImageWriter) -> Result<usize>;
fn write_taggroup_size(dmwriter: &mut DMImageWriter, val: usize) -> Result<usize>;
}
pub struct DMImageWriter<'a> {
buffer: Vec<u8>,
image: &'a DMImage,
}
impl DMImageWriteByVersion for DM3Writer {
#[inline]
fn write_by_version(dmwriter: &mut DMImageWriter, val: usize) -> Result<usize> {
debug!("DM3::write_by_version()");
dmwriter.buffer.write_u32::<BigEndian>(val as u32)?;
let bytes = 4;
debug!("DM3::write_by_version() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_header(dmwriter: &mut DMImageWriter) -> Result<usize> {
debug!("DM3::write_header()");
dmwriter.buffer.write_u32::<BigEndian>(3)?;
let tg_size = dmwriter.image.tg_size;
dmwriter.buffer.write_u32::<BigEndian>(tg_size as u32)?;
let bigendian = if dmwriter.image.bigendian {
BIG_ENDIAN
} else {
LITTLE_ENDIAN
};
dmwriter.buffer.write_u32::<BigEndian>(bigendian as u32)?;
let bytes = 12;
debug!("DM3::write_header() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_by_version_to_buffer(buffer: &mut Vec<u8>, t: usize) -> Result<usize> {
debug!("DM3:write_by_version_to_buffer()");
buffer.write_u32::<BigEndian>(D_ZERO as u32)?;
buffer.write_u32::<BigEndian>(t as u32)?;
let bytes = 8;
debug!("DM3:write_by_version_to_buffer() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_taggroup_size(_dmwriter: &mut DMImageWriter, _size: usize) -> Result<usize> {
Ok(0)
}
}
impl DMImageWriteByVersion for DM4Writer {
fn write_by_version(dmwriter: &mut DMImageWriter, val: usize) -> Result<usize> {
debug!("DM4::write_by_version()");
dmwriter.buffer.write_u32::<BigEndian>(val as u32)?;
let bytes = 8;
debug!("DM4::write_by_version() -> {} bytes written", bytes);
Ok(bytes)
}
fn write_header(dmwriter: &mut DMImageWriter) -> Result<usize> {
debug!("DM4::write_header()");
dmwriter.buffer.write_u32::<BigEndian>(4)?;
let tg_size = dmwriter.image.tg_size;
dmwriter.buffer.write_u64::<BigEndian>(tg_size as u64)?;
let bigendian = if dmwriter.image.bigendian {
BIG_ENDIAN
} else {
LITTLE_ENDIAN
};
dmwriter.buffer.write_u32::<BigEndian>(bigendian as u32)?;
let bytes =16;
debug!("DM4::write_header() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_by_version_to_buffer(buffer: &mut Vec<u8>, t: usize) -> Result<usize> {
debug!("DM4:write_by_version_to_buffer()");
buffer.write_u64::<BigEndian>(D_ZERO as u64)?;
buffer.write_u64::<BigEndian>(t as u64)?;
let bytes = 16;
debug!("DM4:write_by_version_to_buffer() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_taggroup_size(dmwriter: &mut DMImageWriter, size: usize) -> Result<usize> {
dmwriter.buffer.write_u64::<BigEndian>(size as u64)?;
Ok(8)
}
}
impl<'a> DMImageWriter<'a> {
pub fn new(image: &'a DMImage) -> DMImageWriter<'a> {
DMImageWriter {
buffer: Vec::new(),
image,
}
}
pub fn from(image: &'a DMImage) -> DMImageWriter<'a> {
DMImageWriter {
buffer: Vec::with_capacity(image.tg_size + 16),
image,
}
}
pub fn write_to_file<R: DMImageWriteByVersion>(&mut self, mut file: File) -> Result<()> {
debug!("DMImageWriter::write_to_file()");
let buffer = self.serialize::<R>()?;
debug!("DMImageWriter::write_to_file() -> serialization completed");
file.write_all(&buffer[..])?;
debug!("DMImageWriter::write_to_file() -> file written, {} bytes total", buffer.len());
Ok(())
}
pub fn serialize<R: DMImageWriteByVersion>(&mut self) -> Result<Vec<u8>> {
debug!("DMImageWriter::serialize()");
let mut bytes = 0;
bytes += R::write_header(self)?;
if self.image.bigendian {
bytes += self.write_taggroup::<R, BigEndian>(&self.image.root)?;
} else {
bytes += self.write_taggroup::<R, LittleEndian>(&self.image.root)?;
}
self.buffer.append(&mut vec![0u8; 8]);
bytes += 8;
let final_file_size = self.buffer.len() - 16;
BigEndian::write_u32(&mut self.buffer[4..8], final_file_size as u32);
debug!("DMImageWriter::serialize() -> file size {}, total bytes {}", final_file_size, bytes);
let new_buffer= Vec::new();
let buffer = mem::replace(&mut self.buffer, new_buffer);
Ok(buffer)
}
#[inline]
fn write_taggroup<R: DMImageWriteByVersion, T: ByteOrder>(&mut self, tg: &TagGroup) -> Result<usize> {
debug!("DMImageWriter::write_taggroup()");
self.buffer.write_u8(
if tg.sorted() { B_TRUE } else { B_FALSE },
)?;
self.buffer.write_u8(
if tg.opened() { B_TRUE } else { B_FALSE },
)?;
let mut bytes = 2;
bytes += R::write_by_version(self, tg.len())?;
for (key, tag) in tg.iter() {
bytes += self.write_tag_entry::<R, T>(key, tag)?;
}
debug!("DMImageWriter::write_taggroup() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_tag_entry<R: DMImageWriteByVersion, T: ByteOrder>(&mut self, key: &Key, tag: &Tag) -> Result<usize> {
debug!("DMImageWriter::write_tag_entry()");
let mut bytes = 0;
let label = match *key {
Key::Label(ref string) => string.clone(),
Key::Index(_) => String::from(""),
};
match *tag {
Tag::TagGroupEntry(ref tg) => {
self.buffer.write_u8(T_GROUP)?;
self.buffer.write_u16::<BigEndian>(label.len() as u16)?;
bytes += 3 + label.len();
self.buffer.append(&mut label.into_bytes());
bytes += R::write_taggroup_size(self, 0)?; bytes += self.write_taggroup::<R, T>(tg)?;
}
_ => {
self.buffer.write_u8(T_TAG)?;
self.buffer.write_u16::<BigEndian>(label.len() as u16)?;
bytes += 3 + label.len();
self.buffer.append(&mut label.into_bytes());
bytes += R::write_taggroup_size(self, 0)?; bytes += self.write_tag::<R, T>(tag)?;
}
}
debug!("DMImageWriter::write_tag_entry() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_tag<R: DMImageWriteByVersion, T: ByteOrder>(&mut self, val: &Tag) -> Result<usize> {
debug!("DMImageWriter::write_tag()");
self.buffer.append(&mut String::from("%%%%").into_bytes());
let mut bytes = 4;
match *val {
Tag::Empty => bail!("TagEntry has type Empty! Cannot write"),
Tag::Short(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_SHORT)?;
self.buffer.write_i16::<T>(val)?;
bytes += 2;
}
Tag::Long(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_LONG)?;
self.buffer.write_i32::<T>(val)?;
bytes += 4;
}
Tag::UShort(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_USHORT)?;
self.buffer.write_u16::<T>(val)?;
bytes += 2;
}
Tag::ULong(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_ULONG)?;
self.buffer.write_u32::<T>(val)?;
bytes += 4;
}
Tag::Float(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_FLOAT)?;
self.buffer.write_f32::<T>(val)?;
bytes += 4;
}
Tag::Double(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_DOUBLE)?;
self.buffer.write_f64::<T>(val)?;
bytes += 8;
}
Tag::Boolean(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_BOOLEAN)?;
self.buffer.push(if val { B_TRUE } else { B_FALSE });
bytes += 1;
}
Tag::Char(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_CHAR)?;
self.buffer.push(val as u8);
bytes += 1;
}
Tag::Octet(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_OCTET)?;
self.buffer.push(val);
bytes += 1;
}
Tag::ULongLong(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_ULONGLONG)?;
self.buffer.write_u64::<T>(val)?;
bytes += 8;
}
Tag::Unknown64(val) => {
bytes += R::write_by_version(self, T_SIMPLE)?;
bytes += R::write_by_version(self, D_UNKNOWN64)?;
self.buffer.write_u64::<T>(val)?;
bytes += 8;
}
Tag::CharSeq(ref val) => {
bytes += R::write_by_version(self, T_STRING)?;
bytes += R::write_by_version(self, D_STRING)?;
let mut string = val.clone().into_bytes();
bytes += R::write_by_version(self, string.len())?;
bytes += string.len();
self.buffer.append(&mut string);
}
Tag::ArrayShort(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_SHORT)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.write_i16::<T>(*elem)?;
bytes += 2;
}
}
Tag::ArrayLong(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_LONG)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.write_i32::<T>(*elem)?;
bytes += 4;
}
}
Tag::ArrayUShort(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_USHORT)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.write_u16::<T>(*elem)?;
bytes += 2;
}
}
Tag::ArrayULong(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_ULONG)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.write_u32::<T>(*elem)?;
bytes += 4;
}
}
Tag::ArrayFloat(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_FLOAT)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.write_f32::<T>(*elem)?;
bytes += 4;
}
}
Tag::ArrayDouble(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_DOUBLE)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.write_f64::<T>(*elem)?;
bytes += 8;
}
}
Tag::ArrayBoolean(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_BOOLEAN)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.push(if *elem { B_TRUE } else { B_FALSE });
bytes += 1;
}
}
Tag::ArrayChar(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_CHAR)?;
bytes += R::write_by_version(self, arr.len())?;
self.buffer.append(&mut arr.clone().into_bytes());
bytes += arr.len();
}
Tag::ArrayOctet(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_OCTET)?;
bytes += R::write_by_version(self, arr.len())?;
self.buffer.append(&mut arr.clone());
bytes += arr.len();
}
Tag::ArrayULongLong(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_ULONGLONG)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.write_u64::<T>(*elem)?;
bytes += 8;
}
}
Tag::ArrayUnknown64(ref arr) => {
bytes += R::write_by_version(self, T_ARRAY)?;
bytes += R::write_by_version(self, D_ARRAY)?;
bytes += R::write_by_version(self, D_UNKNOWN64)?;
bytes += R::write_by_version(self, arr.len())?;
for elem in arr.iter() {
self.buffer.write_u64::<T>(*elem)?;
bytes += 8;
}
}
Tag::Struct(ref arr) => {
bytes += self.write_struct::<R, T>(arr)?;
}
Tag::ComplexArray(ref arr) => {
bytes += self.write_complex_array::<R, T>(arr)?;
}
_ => bail!("Did not expect this type, corrupt data"),
}
debug!("DMImageWriter::write_tag() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_struct<R: DMImageWriteByVersion, T: ByteOrder>(&mut self, struct_tg: &[Tag]) -> Result<usize> {
debug!("DMImageWriter::write_struct()");
let mut bytes = 0;
let number_fields = struct_tg.len();
let info_size = 2 * number_fields + 4;
bytes += R::write_by_version(self, info_size - 1)?;
bytes += R::write_by_version(self, D_STRUCT)?;
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, number_fields)?;
let mut data_buf = Vec::new();
for val in struct_tg.iter() {
match *val {
Tag::Short(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_SHORT)?;
data_buf.write_i16::<T>(val)?;
}
Tag::Long(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_LONG)?;
data_buf.write_i32::<T>(val)?;
}
Tag::UShort(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_USHORT)?;
data_buf.write_u16::<T>(val)?;
}
Tag::ULong(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_ULONG)?;
data_buf.write_u32::<T>(val)?;
}
Tag::Float(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_FLOAT)?;
data_buf.write_f32::<T>(val)?;
}
Tag::Double(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_DOUBLE)?;
data_buf.write_f64::<T>(val)?;
}
Tag::Boolean(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_BOOLEAN)?;
data_buf.push(if val { B_TRUE } else { B_FALSE });
}
Tag::Char(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_CHAR)?;
data_buf.push(val as u8);
}
Tag::Octet(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_OCTET)?;
data_buf.push(val);
}
Tag::ULongLong(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_ULONGLONG)?;
data_buf.write_u64::<T>(val)?;
}
Tag::Unknown64(val) => {
bytes += R::write_by_version(self, D_ZERO)?;
bytes += R::write_by_version(self, D_UNKNOWN64)?;
data_buf.write_u64::<T>(val)?;
}
_ => bail!("Write struct: Unexpected data type in struct!"),
}
}
self.buffer.append(&mut data_buf);
bytes += data_buf.len();
debug!("DMImageWriter::write_struct() -> {} bytes written", bytes);
Ok(bytes)
}
#[inline]
fn write_complex_array<R: DMImageWriteByVersion, T: ByteOrder>(&mut self, array: &[Tag]) -> Result<usize> {
debug!("DMImageWriter::write_complex_array()");
let info_size = match self.image.version {
DM3 => 4 * 4, DM4 => 4 * 8, _ => bail!("Unknown DMImage version, cannot write..."),
};
let mut info_buf = vec![0u8; info_size];
match self.image.version {
DM3 => {
BigEndian::write_u32(&mut info_buf[4..8], D_ARRAY as u32);
BigEndian::write_u32(&mut info_buf[8..12], D_STRUCT as u32);
BigEndian::write_u32(&mut info_buf[12..16], D_ZERO as u32);
},
DM4 => {
BigEndian::write_u64(&mut info_buf[8..16], D_ARRAY as u64);
BigEndian::write_u64(&mut info_buf[16..24], D_STRUCT as u64);
BigEndian::write_u64(&mut info_buf[24..32], D_ZERO as u64);
},
_ => bail!("Unknown DMImage version, cannot write..."),
}
let mut struct_elements = 0usize;
let mut type_buf = Vec::new();
let mut data_buf = Vec::new();
for arr_elem in array.iter() {
match *arr_elem {
Tag::Struct(ref arr_str) => {
struct_elements = arr_str.len();
type_buf = Vec::new();
for elem in arr_str.iter() {
match *elem {
Tag::Short(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_SHORT)?;
data_buf.write_i16::<T>(val)?;
}
Tag::Long(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_LONG)?;
data_buf.write_i32::<T>(val)?;
}
Tag::UShort(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_USHORT)?;
data_buf.write_u16::<T>(val)?;
}
Tag::ULong(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_ULONG)?;
data_buf.write_u32::<T>(val)?;
}
Tag::Float(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_FLOAT)?;
data_buf.write_f32::<T>(val)?;
}
Tag::Double(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_DOUBLE)?;
data_buf.write_f64::<T>(val)?;
}
Tag::Boolean(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_BOOLEAN)?;
data_buf.push(if val { B_TRUE } else { B_FALSE });
}
Tag::Char(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_CHAR)?;
data_buf.push(val as u8);
}
Tag::Octet(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_OCTET)?;
data_buf.push(val);
}
Tag::ULongLong(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_ULONGLONG)?;
data_buf.write_u64::<T>(val)?;
}
Tag::Unknown64(val) => {
R::write_by_version_to_buffer(&mut type_buf, D_UNKNOWN64)?;
data_buf.write_u64::<T>(val)?;
}
_ => bail!("Unknown data type for complex array"),
}
}
}
_ => bail!("Expected a Tag::Struct inside the Tag::ComplexArray"),
}
}
match self.image.version {
DM3 => BigEndian::write_u32(&mut info_buf[0..4], 5u32 + struct_elements as u32 * 2u32),
DM4 => BigEndian::write_u64(&mut info_buf[0..8], 5u64 + struct_elements as u64 * 2u64),
_ => bail!("Unknown DMImage version, cannot write..."),
}
let mut bytes = 0;
self.buffer.append(&mut info_buf);
bytes += info_buf.len();
bytes += R::write_by_version(self, struct_elements)?;
self.buffer.append(&mut type_buf);
bytes += type_buf.len();
bytes += R::write_by_version(self, array.len())?;
self.buffer.append(&mut data_buf);
bytes += data_buf.len();
debug!("DMImageWriter::write_complex_array() -> {} bytes written", bytes);
Ok(bytes)
}
}