reader: create a generic reader to reduce code duplication

This commit is contained in:
Matt Bilker
2018-07-10 17:04:08 -04:00
parent 9908352300
commit 3df2b1eda1
2 changed files with 217 additions and 118 deletions

View File

@@ -29,15 +29,16 @@ mod error;
mod ip4;
mod node_types;
mod options;
mod reader;
mod sixbit;
mod de;
mod ser;
use byte_buffer::{ByteBufferRead, ByteBufferWrite};
use compression::Compression;
use byte_buffer::ByteBufferWrite;
use node_types::StandardType;
use sixbit::{pack_sixbit, unpack_sixbit};
use reader::Reader;
use sixbit::pack_sixbit;
// Public exports
pub use encoding_type::EncodingType;
@@ -75,56 +76,14 @@ impl KbinXml {
}
fn from_binary_internal(&mut self, stack: &mut Vec<Element>, input: &[u8]) -> Result<(Element, EncodingType)> {
// Node buffer starts from the beginning.
// Data buffer starts later after reading `len_data`.
let mut node_buf = ByteBufferRead::new(&input[..]);
let mut reader = Reader::new(input)?;
let signature = node_buf.read_u8().context(KbinErrorKind::HeaderRead("signature"))?;
if signature != SIGNATURE {
return Err(KbinErrorKind::HeaderValue("signature").into());
}
// TODO: support uncompressed
let compress_byte = node_buf.read_u8().context(KbinErrorKind::HeaderRead("compression"))?;
if compress_byte != SIG_COMPRESSED {
return Err(KbinErrorKind::HeaderValue("compression").into());
}
let compressed = Compression::from_byte(compress_byte)?;
let encoding_byte = node_buf.read_u8().context(KbinErrorKind::HeaderRead("encoding"))?;
let encoding_negation = node_buf.read_u8().context(KbinErrorKind::HeaderRead("encoding negation"))?;
let encoding = EncodingType::from_byte(encoding_byte)?;
if encoding_negation != 0xFF ^ encoding_byte {
return Err(KbinErrorKind::HeaderValue("encoding negation").into());
}
info!("signature: 0x{:x}", signature);
info!("compression: 0x{:x} ({:?})", compress_byte, compressed);
info!("encoding: 0x{:x} ({:?})", encoding_byte, encoding);
let len_node = node_buf.read_u32::<BigEndian>().context(KbinErrorKind::LenNodeRead)?;
info!("len_node: {} (0x{:x})", len_node, len_node);
// We have read 8 bytes so far, so offset the start of the data buffer from
// our current position.
let data_buf_start = len_node + 8;
let mut data_buf = ByteBufferRead::new(&input[(data_buf_start as usize)..]);
let len_data = data_buf.read_u32::<BigEndian>().context(KbinErrorKind::LenDataRead)?;
info!("len_data: {} (0x{:x})", len_data, len_data);
let node_buf_end = data_buf_start.into();
while node_buf.position() < node_buf_end {
let raw_node_type = node_buf.read_u8().context(KbinErrorKind::NodeTypeRead)?;
let is_array = raw_node_type & 64 == 64;
let node_type = raw_node_type & !64;
let xml_type = StandardType::from_u8(node_type);
debug!("raw_node_type: {}, node_type: {:?} ({}), is_array: {}", raw_node_type, xml_type, node_type, is_array);
while reader.node_buf.position() < reader.data_buf_start() {
let (xml_type, is_array) = reader.read_node_type()?;
match xml_type {
StandardType::NodeEnd | StandardType::FileEnd => {
StandardType::NodeEnd |
StandardType::FileEnd => {
if stack.len() > 1 {
let node = stack.pop().expect("Stack must have last node");
if let Some(to) = stack.last_mut() {
@@ -141,85 +100,92 @@ impl KbinXml {
_ => {},
};
let name = unpack_sixbit(&mut *node_buf)?;
let name = reader.read_node_identifier()?;
if xml_type == StandardType::NodeStart {
stack.push(Element::bare(name));
} else {
if xml_type != StandardType::Attribute {
stack.push(Element::bare(name.clone()));
}
if let Some(to) = stack.last_mut() {
match xml_type {
StandardType::Attribute => {
let val = data_buf.read_str(encoding)?;
debug!("attr name: {}, val: {}", name, val);
to.set_attr(name, val);
},
// Removing null bytes is *so much* fun.
//
// Handle String nodes separately to use the string reading logic
// which automatically removes trailing null bytes.
StandardType::String => {
to.set_attr("__type", xml_type.name);
continue;
}
let val = data_buf.read_str(encoding)?;
debug!("name: {}, val: {}", name, val);
to.append_text_node(val);
},
_ => {
to.set_attr("__type", xml_type.name);
if xml_type != StandardType::Attribute {
stack.push(Element::bare(name.clone()));
}
if let Some(to) = stack.last_mut() {
match xml_type {
StandardType::Attribute => {
let val = reader.read_string()?;
debug!("attr name: {}, val: {}", name, val);
to.set_attr(name, val);
},
StandardType::Binary => {
to.set_attr("__type", xml_type.name);
let type_size = xml_type.size;
let type_count = xml_type.count;
let (is_array, size) = if type_count == -1 {
(true, data_buf.read_u32::<BigEndian>().context(KbinErrorKind::BinaryLengthRead)?)
} else if is_array {
let node_size = type_size * type_count;
let arr_count = data_buf.read_u32::<BigEndian>().context(KbinErrorKind::ArrayLengthRead)? / node_size as u32;
to.set_attr("__count", arr_count);
/*
let size = reader.data_buf.read_u32::<BigEndian>().context(KbinErrorKind::BinaryLengthRead)?;
let data = reader.data_buf.get(size)?;
reader.data_buf.realign_reads(None)?;
*/
let data = reader.read_bytes().context(KbinErrorKind::BinaryLengthRead)?;
let size = (node_size as u32) * arr_count;
(true, size)
} else {
(false, 1)
};
to.set_attr("__size", data.len());
debug!("type: {:?}, type_size: {}, type_count: {}, is_array: {}, size: {}",
xml_type,
type_size,
type_count,
is_array,
size);
let len = data.len() * 2;
let val = data.into_iter().fold(String::with_capacity(len), |mut val, x| {
write!(val, "{:02x}", x).expect("Failed to append hex char");
val
});
debug!("name: {}, string: {}", name, val);
to.append_text_node(val);
},
// Removing null bytes is *so much* fun.
//
// Handle String nodes separately to use the string reading logic
// which automatically removes trailing null bytes.
StandardType::String => {
to.set_attr("__type", xml_type.name);
let data = if is_array {
let data = data_buf.get(size)?;
data_buf.realign_reads(None)?;
let val = reader.read_string()?;
debug!("type: {:?}, is_array: {}, name: {}, val: {}", xml_type, is_array, name, val);
to.append_text_node(val);
},
_ => {
to.set_attr("__type", xml_type.name);
data
} else {
data_buf.get_aligned(*xml_type)?
};
let type_size = xml_type.size;
let type_count = xml_type.count;
let (is_array, size) = if is_array {
let node_size = type_size * type_count;
let arr_count = reader.read_u32().context(KbinErrorKind::ArrayLengthRead)? / node_size as u32;
to.set_attr("__count", arr_count);
debug!("data: 0x{:02x?}", data);
if xml_type == StandardType::Binary {
to.set_attr("__size", data.len());
let size = (node_size as u32) * arr_count;
(true, size)
} else {
(false, 1)
};
let len = data.len() * 2;
let val = data.into_iter().fold(String::with_capacity(len), |mut val, x| {
write!(val, "{:02x}", x).expect("Failed to append hex char");
val
});
debug!("name: {}, string: {}", name, val);
to.append_text_node(val);
} else {
let inner_value = xml_type.parse_bytes(&data)?;
debug!("name: {}, string: {}", name, inner_value);
to.append_text_node(inner_value);
}
},
};
}
debug!("type: {:?}, type_size: {}, type_count: {}, is_array: {}, size: {}",
xml_type,
type_size,
type_count,
is_array,
size);
let data = if is_array {
let data = reader.data_buf.get(size)?;
reader.data_buf.realign_reads(None)?;
data
} else {
reader.data_buf.get_aligned(*xml_type)?
};
debug!("data: 0x{:02x?}", data);
let inner_value = xml_type.parse_bytes(&data)?;
debug!("name: {}, string: {}", name, inner_value);
to.append_text_node(inner_value);
},
};
}
}
@@ -229,6 +195,7 @@ impl KbinXml {
stack.truncate(1);
let element = stack.pop().expect("Stack must have root node");
let encoding = reader.encoding();
Ok((element, encoding))
}

132
src/reader.rs Normal file
View File

@@ -0,0 +1,132 @@
use byteorder::{BigEndian, ReadBytesExt};
use failure::ResultExt;
use byte_buffer::ByteBufferRead;
use compression::Compression;
use encoding_type::EncodingType;
use error::{KbinErrorKind, Result};
use node_types::StandardType;
use sixbit::unpack_sixbit;
use super::{ARRAY_MASK, SIGNATURE, SIG_COMPRESSED};
pub struct Reader<'buf> {
encoding: EncodingType,
pub(crate) node_buf: ByteBufferRead<&'buf [u8]>,
pub(crate) data_buf: ByteBufferRead<&'buf [u8]>,
data_buf_start: u64,
}
impl<'buf> Reader<'buf> {
pub fn new(input: &'buf [u8]) -> Result<Self> {
// Node buffer starts from the beginning.
// Data buffer starts later after reading `len_data`.
let mut node_buf = ByteBufferRead::new(&input[..]);
let signature = node_buf.read_u8().context(KbinErrorKind::HeaderRead("signature"))?;
if signature != SIGNATURE {
return Err(KbinErrorKind::HeaderValue("signature").into());
}
// TODO: support uncompressed
let compress_byte = node_buf.read_u8().context(KbinErrorKind::HeaderRead("compression"))?;
if compress_byte != SIG_COMPRESSED {
return Err(KbinErrorKind::HeaderValue("compression").into());
}
let compressed = Compression::from_byte(compress_byte)?;
let encoding_byte = node_buf.read_u8().context(KbinErrorKind::HeaderRead("encoding"))?;
let encoding_negation = node_buf.read_u8().context(KbinErrorKind::HeaderRead("encoding negation"))?;
let encoding = EncodingType::from_byte(encoding_byte)?;
if encoding_negation != !encoding_byte {
return Err(KbinErrorKind::HeaderValue("encoding negation").into());
}
info!("signature: 0x{:X}, compression: 0x{:X} ({:?}), encoding: 0x{:X} ({:?})", signature, compress_byte, compressed, encoding_byte, encoding);
let len_node = node_buf.read_u32::<BigEndian>().context(KbinErrorKind::LenNodeRead)?;
info!("len_node: {0} (0x{0:x})", len_node);
// We have read 8 bytes so far, so offset the start of the data buffer from
// the start of the input data.
let data_buf_start = len_node + 8;
let mut data_buf = ByteBufferRead::new(&input[(data_buf_start as usize)..]);
let len_data = data_buf.read_u32::<BigEndian>().context(KbinErrorKind::LenDataRead)?;
info!("len_data: {0} (0x{0:x})", len_data);
//let node_buf_end = data_buf_start.into();
Ok(Self {
encoding,
//read_mode: ReadMode::Single,
//first_struct: true,
//node_buf_end,
node_buf,
data_buf,
data_buf_start: data_buf_start as u64,
})
}
#[inline]
pub fn encoding(&self) -> EncodingType {
self.encoding
}
#[inline]
pub fn data_buf_start(&self) -> u64 {
self.data_buf_start
}
pub fn read_node_type(&mut self) -> Result<(StandardType, bool)> {
let raw_node_type = self.node_buf.read_u8().context(KbinErrorKind::NodeTypeRead)?;
let is_array = raw_node_type & ARRAY_MASK == ARRAY_MASK;
let node_type = raw_node_type & !ARRAY_MASK;
let xml_type = StandardType::from_u8(node_type);
debug!("Reader::read_node_type() => raw_node_type: {}, node_type: {:?} ({}), is_array: {}",
raw_node_type,
xml_type,
node_type,
is_array);
Ok((xml_type, is_array))
}
pub fn read_node_identifier(&mut self) -> Result<String> {
let value = unpack_sixbit(&mut *self.node_buf)?;
debug!("Reader::read_node_identifier() => value: {:?}", value);
Ok(value)
}
pub fn read_string(&mut self) -> Result<String> {
let value = self.data_buf.read_str(self.encoding)?;
debug!("Reader::read_string() => value: {:?}", value);
Ok(value)
}
pub fn read_u8(&mut self) -> Result<u8> {
let value = self.data_buf.read_u8().context(KbinErrorKind::DataReadOneByte)?;
debug!("Reader::read_u8() => value: {}", value);
Ok(value)
}
pub fn read_u32(&mut self) -> Result<u32> {
let value = self.data_buf.read_u32::<BigEndian>().context(KbinErrorKind::DataRead(4))?;
debug!("Reader::read_u32() => result: {}", value);
Ok(value)
}
// TODO: make a more intelligent reader to avoid allocating the Vec
#[inline]
pub fn read_bytes(&mut self) -> Result<Vec<u8>> {
self.data_buf.buf_read()
}
}