From e3d00de2c19691d2d547f7646826e4f371813bbf Mon Sep 17 00:00:00 2001 From: Matt Bilker Date: Sat, 2 Jun 2018 05:56:40 +0000 Subject: [PATCH] lib: convert remaining functions to use failure --- src/compression.rs | 10 ++++---- src/encoding_type.rs | 32 +++++++++++++++----------- src/error.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 28 +++++++++++----------- src/node_types.rs | 52 ++++++++++++++++++++++++++--------------- src/sixbit.rs | 11 +++++---- 6 files changed, 133 insertions(+), 55 deletions(-) diff --git a/src/compression.rs b/src/compression.rs index 703f73b..b685525 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -1,3 +1,5 @@ +use error::{KbinError, KbinErrorKind}; + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Compression { Compressed, @@ -5,11 +7,11 @@ pub enum Compression { } impl Compression { - pub fn from_byte(byte: u8) -> Option { + pub fn from_byte(byte: u8) -> Result { match byte { - 0x42 => Some(Compression::Compressed), - 0x45 => Some(Compression::Uncompressed), - _ => None, + 0x42 => Ok(Compression::Compressed), + 0x45 => Ok(Compression::Uncompressed), + _ => Err(KbinErrorKind::UnknownCompression.into()), } } diff --git a/src/encoding_type.rs b/src/encoding_type.rs index 94571cd..68f8a5c 100644 --- a/src/encoding_type.rs +++ b/src/encoding_type.rs @@ -1,3 +1,5 @@ +use error::{KbinError, KbinErrorKind}; + use encoding::{DecoderTrap, Encoding}; use encoding::all::{ASCII, EUC_JP, ISO_8859_1, WINDOWS_31J}; @@ -25,7 +27,7 @@ impl EncodingType { } } - pub fn from_byte(byte: u8) -> Option { + pub fn from_byte(byte: u8) -> Result { let val = match byte { 0x00 => EncodingType::None, 0x20 => EncodingType::ASCII, @@ -33,29 +35,31 @@ impl EncodingType { 0x60 => EncodingType::EUC_JP, 0x80 => EncodingType::SHIFT_JIS, 0xA0 => EncodingType::UTF_8, - _ => return None, + _ => return Err(KbinErrorKind::UnknownEncoding.into()), }; - Some(val) + Ok(val) } /// Decode bytes using the encoding definition from the `encoding` crate. /// /// A `Some` value indicates an encoding should be used from the `encoding` /// crate. A `None` value indicates Rust's own UTF-8 handling should be used. - pub fn decode_bytes(&self, input: Vec) -> String { - const DECODER_FAIL: &str = "Unable to interpret string as alternate encoding"; + pub fn decode_bytes(&self, input: Vec) -> Result { + let decoder_fail = |e| { + format_err!("{}", e).context(KbinErrorKind::EncodingDecode) + }; - match *self { + let result = match *self { EncodingType::None | - EncodingType::UTF_8 => { - String::from_utf8(input).expect("Unable to interpret string as UTF-8") - }, + EncodingType::UTF_8 => String::from_utf8(input)?, - EncodingType::ASCII => ASCII.decode(&input, DecoderTrap::Strict).expect(DECODER_FAIL), - EncodingType::ISO_8859_1 => ISO_8859_1.decode(&input, DecoderTrap::Strict).expect(DECODER_FAIL), - EncodingType::EUC_JP => EUC_JP.decode(&input, DecoderTrap::Strict).expect(DECODER_FAIL), - EncodingType::SHIFT_JIS => WINDOWS_31J.decode(&input, DecoderTrap::Strict).expect(DECODER_FAIL), - } + EncodingType::ASCII => ASCII.decode(&input, DecoderTrap::Strict).map_err(decoder_fail)?, + EncodingType::ISO_8859_1 => ISO_8859_1.decode(&input, DecoderTrap::Strict).map_err(decoder_fail)?, + EncodingType::EUC_JP => EUC_JP.decode(&input, DecoderTrap::Strict).map_err(decoder_fail)?, + EncodingType::SHIFT_JIS => WINDOWS_31J.decode(&input, DecoderTrap::Strict).map_err(decoder_fail)?, + }; + + Ok(result) } } diff --git a/src/error.rs b/src/error.rs index de6ca1d..7c1cc91 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::string::FromUtf8Error; use failure::{Backtrace, Context, Fail}; @@ -26,6 +27,54 @@ pub enum KbinErrorKind { #[fail(display = "Unable to seek data buffer")] Seek, + + #[fail(display = "Unable to read signature byte")] + SignatureRead, + + #[fail(display = "Unable to read compression byte")] + CompressionRead, + + #[fail(display = "Unknown compression value")] + UnknownCompression, + + #[fail(display = "Unable to read encoding byte")] + EncodingRead, + + #[fail(display = "Unable to read encoding negation byte")] + EncodingNegationRead, + + #[fail(display = "Unknown encoding")] + UnknownEncoding, + + #[fail(display = "Unable to read len_node")] + LenNodeRead, + + #[fail(display = "Unable to read len_data")] + LenDataRead, + + #[fail(display = "Unable to read node type")] + NodeTypeRead, + + #[fail(display = "Unable to read binary/string byte length")] + BinaryLengthRead, + + #[fail(display = "Unable to read array node length")] + ArrayLengthRead, + + #[fail(display = "Failed to write {} to output string", _0)] + ByteParse(&'static str), + + #[fail(display = "Unable to read sixbit string length")] + SixbitLengthRead, + + #[fail(display = "Unable to read sixbit string content")] + SixbitRead, + + #[fail(display = "Unable to interpret string as UTF-8")] + Utf8, + + #[fail(display = "Unable to interpret string as alternate encoding")] + EncodingDecode, } impl fmt::Display for KbinError { @@ -55,3 +104,9 @@ impl From> for KbinError { KbinError { inner } } } + +impl From for KbinError { + fn from(inner: FromUtf8Error) -> KbinError { + inner.context(KbinErrorKind::Utf8).into() + } +} diff --git a/src/lib.rs b/src/lib.rs index 5505f3c..1f5f522 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ impl KbinXml { data.truncate(index + 1); trace!("data_buf_read_str => size: {}, data: 0x{:02x?}", data.len(), data); - Ok(encoding.decode_bytes(data)) + encoding.decode_bytes(data) } fn data_buf_get(&mut self, data_buf: &mut Cursor<&[u8]>, size: u32) -> Result, KbinError> { @@ -159,25 +159,25 @@ impl KbinXml { // Data buffer starts later after reading `len_data`. let mut node_buf = Cursor::new(&input[..]); - let signature = node_buf.read_u8().expect("Unable to read signature byte"); + let signature = node_buf.read_u8().context(KbinErrorKind::SignatureRead)?; assert_eq!(signature, SIGNATURE); // TODO: support uncompressed - let compress_byte = node_buf.read_u8().expect("Unable to read compression byte"); + let compress_byte = node_buf.read_u8().context(KbinErrorKind::CompressionRead)?; assert_eq!(compress_byte, SIG_COMPRESSED); - let compressed = Compression::from_byte(compress_byte).expect("Unknown compression value"); + let compressed = Compression::from_byte(compress_byte)?; - let encoding_byte = node_buf.read_u8().expect("Unable to read encoding byte"); - let encoding_negation = node_buf.read_u8().expect("Unable to read encoding negation byte"); - let encoding = EncodingType::from_byte(encoding_byte).expect("Unknown encoding"); + let encoding_byte = node_buf.read_u8().context(KbinErrorKind::EncodingRead)?; + let encoding_negation = node_buf.read_u8().context(KbinErrorKind::EncodingNegationRead)?; + let encoding = EncodingType::from_byte(encoding_byte)?; assert_eq!(encoding_negation, 0xFF ^ encoding_byte); 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::().expect("Unable to read len_node"); + let len_node = node_buf.read_u32::().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 @@ -192,14 +192,14 @@ impl KbinXml { trace!("offset_1: {}, offset_2: {}", self.offset_1, self.offset_2); } - let len_data = data_buf.read_u32::().expect("Unable to read len_data"); + let len_data = data_buf.read_u32::().context(KbinErrorKind::LenDataRead)?; info!("len_data: {} (0x{:x})", len_data, len_data); let mut stack: Vec = Vec::new(); { let node_buf_end = data_buf_start.into(); while node_buf.position() < node_buf_end { - let raw_node_type = node_buf.read_u8().expect("Unable to read node type"); + 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; @@ -224,7 +224,7 @@ impl KbinXml { _ => {}, }; - let name = unpack_sixbit(&mut node_buf); + let name = unpack_sixbit(&mut node_buf)?; if xml_type == KbinType::NodeStart { stack.push(Element::bare(name)); @@ -256,10 +256,10 @@ impl KbinXml { 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::().expect("Unable to read binary/string byte length")) + (true, data_buf.read_u32::().context(KbinErrorKind::BinaryLengthRead)?) } else if is_array { let node_size = type_size * type_count; - let arr_count = data_buf.read_u32::().expect("Unable to read array node length") / node_size as u32; + let arr_count = data_buf.read_u32::().context(KbinErrorKind::ArrayLengthRead)? / node_size as u32; to.set_attr("__count", arr_count); let size = (node_size as u32) * arr_count; @@ -296,7 +296,7 @@ impl KbinXml { debug!("name: {}, string: {}", name, val); to.append_text_node(val); } else { - let inner_value = xml_type.parse_bytes(&data); + let inner_value = xml_type.parse_bytes(&data)?; debug!("name: {}, string: {}", name, inner_value); to.append_text_node(inner_value); } diff --git a/src/node_types.rs b/src/node_types.rs index 65b73ae..bfeab3c 100644 --- a/src/node_types.rs +++ b/src/node_types.rs @@ -1,20 +1,26 @@ +use error::{KbinError, KbinErrorKind}; + use std::fmt::Write; +use failure::ResultExt; + trait KbinWrapperType { - fn from_kbin_bytes(output: &mut String, input: &[u8]); + fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError>; } macro_rules! number_impl { (integer; $($inner_type:ident),*) => { $( impl KbinWrapperType<$inner_type> for $inner_type { - fn from_kbin_bytes(output: &mut String, input: &[u8]) { + fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError> { trace!("KbinWrapperType<{}> => input: {:02x?}", stringify!($inner_type), input); let mut data = [0; ::std::mem::size_of::<$inner_type>()]; data.clone_from_slice(input); write!(output, "{}", $inner_type::from_be($inner_type::from_bytes(data))) - .expect(concat!("Failed to write ", stringify!($inner_type), " to output string")); + .context(KbinErrorKind::ByteParse(stringify!($inner_type)))?; + + Ok(()) } } )* @@ -22,7 +28,7 @@ macro_rules! number_impl { (float; $($intermediate:ident => $inner_type:ident),*) => { $( impl KbinWrapperType<$inner_type> for $inner_type { - fn from_kbin_bytes(output: &mut String, input: &[u8]) { + fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError> { trace!("KbinWrapperType<{}> => input: {:02x?}", stringify!($inner_type), input); let mut data = [0; ::std::mem::size_of::<$inner_type>()]; @@ -30,7 +36,9 @@ macro_rules! number_impl { let bits = $intermediate::from_be($intermediate::from_bytes(data)); write!(output, "{:.6}", $inner_type::from_bits(bits)) - .expect(concat!("Failed to write ", stringify!($inner_type), " to output string")); + .context(KbinErrorKind::ByteParse(stringify!($inner_type)))?; + + Ok(()) } } )* @@ -41,7 +49,7 @@ number_impl!(integer; i8, u8, i16, u16, i32, u32, i64, u64); number_impl!(float; u32 => f32, u64 => f64); impl KbinWrapperType for bool { - fn from_kbin_bytes(output: &mut String, input: &[u8]) { + fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError> { trace!("KbinWrapperType => input: {:02x?}", input); let value = match input[0] { @@ -50,12 +58,14 @@ impl KbinWrapperType for bool { v => panic!("Unsupported value for boolean: {}", v), }; output.push_str(value); + + Ok(()) } } struct Ip4; impl KbinWrapperType for Ip4 { - fn from_kbin_bytes(output: &mut String, input: &[u8]) { + fn from_kbin_bytes(output: &mut String, input: &[u8]) -> Result<(), KbinError> { trace!("KbinWrapperType => input: {:02x?}", input); if input.len() < 4 { @@ -63,18 +73,20 @@ impl KbinWrapperType for Ip4 { } write!(output, "{}.{}.{}.{}", input[0], input[1], input[2], input[3]) - .expect("Failed to write IP address to output string"); + .context(KbinErrorKind::ByteParse("Ip4"))?; + + Ok(()) } } struct DummyConverter; impl KbinWrapperType for DummyConverter { - fn from_kbin_bytes(_output: &mut String, _input: &[u8]) {} + fn from_kbin_bytes(_output: &mut String, _input: &[u8]) -> Result<(), KbinError> { Ok(()) } } struct InvalidConverter; impl KbinWrapperType for InvalidConverter { - fn from_kbin_bytes(_output: &mut String, input: &[u8]) { + fn from_kbin_bytes(_output: &mut String, input: &[u8]) -> Result<(), KbinError> { panic!("Invalid kbin type converter called for input: {:02x?}", input); } } @@ -135,12 +147,12 @@ macro_rules! construct_types { } } - fn parse_array(output: &mut String, input: &[u8], size: usize, count: usize, arr_count: usize) + fn parse_array(output: &mut String, input: &[u8], size: usize, count: usize, arr_count: usize) -> Result<(), KbinError> where T: KbinWrapperType { { let first = &input[..size]; - T::from_kbin_bytes(output, first); + T::from_kbin_bytes(output, first)?; } let total_nodes = count * arr_count; @@ -149,11 +161,13 @@ macro_rules! construct_types { let end = (i + 1) * size; let data = &input[offset..end]; output.push(' '); - T::from_kbin_bytes(output, data); + T::from_kbin_bytes(output, data)?; } + + Ok(()) } - fn parse_bytes_inner(&self, input: &[u8], name: &str, size: u8, count: i8) -> String + fn parse_bytes_inner(&self, input: &[u8], name: &str, size: u8, count: i8) -> Result where T: KbinWrapperType { let type_size = (size as usize) * (count as usize); @@ -170,20 +184,20 @@ macro_rules! construct_types { // May have a node (i.e. Ip4) that is only a single count, but it // can be part of an array if arr_count == 1 { - T::from_kbin_bytes(&mut result, input); + T::from_kbin_bytes(&mut result, input)?; } else { - Self::parse_array::(&mut result, input, size as usize, count as usize, arr_count); + Self::parse_array::(&mut result, input, size as usize, count as usize, arr_count)?; } } else if count > 1 { - Self::parse_array::(&mut result, input, size as usize, count as usize, arr_count); + Self::parse_array::(&mut result, input, size as usize, count as usize, arr_count)?; } else { unimplemented!(); } - result + Ok(result) } - pub fn parse_bytes(&self, input: &[u8]) -> String { + pub fn parse_bytes(&self, input: &[u8]) -> Result { match *self { $( KbinType::$konst => self.parse_bytes_inner::<$inner_type>(input, $name, $size, $count), diff --git a/src/sixbit.rs b/src/sixbit.rs index f6bf65b..288182e 100644 --- a/src/sixbit.rs +++ b/src/sixbit.rs @@ -2,8 +2,11 @@ use std::collections::HashMap; use std::io::{Read, Write}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use failure::ResultExt; use num::{BigUint, FromPrimitive, ToPrimitive}; +use error::{KbinError, KbinErrorKind}; + static CHAR_MAP: &'static [u8] = b"0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; lazy_static! { @@ -42,10 +45,10 @@ pub fn pack_sixbit(writer: &mut T, input: &str) writer.write_uint::(bits, (input.len() * 6 + padding) / 8).expect("Unable to write sixbit contents"); } -pub fn unpack_sixbit(reader: &mut T) -> String +pub fn unpack_sixbit(reader: &mut T) -> Result where T: Read { - let len = reader.read_u8().expect("Unable to read sixbit string length"); + let len = reader.read_u8().context(KbinErrorKind::SixbitLengthRead)?; let real_len = (f32::from(len * 6) / 8f32).ceil(); let real_len = (real_len as u32) as usize; let padding = (8 - ((len * 6) % 8)) as usize; @@ -53,7 +56,7 @@ pub fn unpack_sixbit(reader: &mut T) -> String debug!("sixbit_len: {}, real_len: {}, padding: {}", len, real_len, padding); let mut buf = vec![0; real_len]; - reader.read_exact(&mut buf).expect("Unable to read sixbit string content"); + reader.read_exact(&mut buf).context(KbinErrorKind::SixbitRead)?; let bits = BigUint::from_bytes_be(&buf); let bits = bits >> padding; @@ -76,5 +79,5 @@ pub fn unpack_sixbit(reader: &mut T) -> String }).collect(); debug!("result: {}", result); - result + Ok(result) }