Skip to content

Commit

Permalink
Use custom error type for from_hex (#305)
Browse files Browse the repository at this point in the history
* Use custom error type, remove dups.

* cargo fmt

* parity-util-mem: fix authors in Cargo.toml

Co-authored-by: Andronik Ordian <[email protected]>
  • Loading branch information
2 people authored and NikVolf committed Jan 3, 2020
1 parent a7dcd00 commit f02f892
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 42 deletions.
2 changes: 0 additions & 2 deletions kvdb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
use bytes::Bytes;
use smallvec::SmallVec;
use std::io;
use std::path::Path;
use std::sync::Arc;

mod io_stats;

Expand Down
2 changes: 1 addition & 1 deletion parity-util-mem/derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "parity-util-mem-derive"
version = "0.1.0"
authors = ["Parity Technologies"]
authors = ["Parity Technologies <[email protected]>"]
license = "MIT"
description = "Crate for memory reporting"
repository = "https://github.com/paritytech/pariry-common/parity-util-mem/derive"
Expand Down
87 changes: 48 additions & 39 deletions primitive-types/impls/serde/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,57 @@ fn to_hex_raw<'a>(v: &'a mut [u8], bytes: &[u8], skip_leading_zero: bool) -> &'a
unsafe { std::str::from_utf8_unchecked(&v[0..idx]) }
}

/// Decoding bytes from hex string error.
#[derive(Debug, PartialEq, Eq)]
pub enum FromHexError {
/// The `0x` prefix is missing.
MissingPrefix,
/// Invalid (non-hex) character encountered.
InvalidHex {
/// The unexpected character.
character: char,
/// Index of that occurrence.
index: usize,
},
}

impl std::error::Error for FromHexError {}

impl fmt::Display for FromHexError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Self::MissingPrefix => write!(fmt, "0x prefix is missing"),
Self::InvalidHex { character, index } => write!(fmt, "invalid hex character: {}, at {}", character, index),
}
}
}

/// Decode given hex string into a vector of bytes.
///
/// Returns an error if the string is not prefixed with `0x`
/// or non-hex characters are present.
pub fn from_hex(v: &str) -> Result<Vec<u8>, String> {
pub fn from_hex(v: &str) -> Result<Vec<u8>, FromHexError> {
if !v.starts_with("0x") {
return Err("0x prefix is missing".into());
return Err(FromHexError::MissingPrefix);
}

let mut bytes = vec![0u8; (v.len() - 1) / 2];
from_hex_raw(v, &mut bytes)?;
Ok(bytes)
}

/// Decode given 0x-prefixed hex string into provided slice.
/// Used internally by `from_hex` and `deserialize_check_len`.
///
/// The method will panic if:
/// 1. `v` is shorter than 2 characters (you need to check 0x prefix outside).
/// 2. `bytes` have incorrect length (make sure to allocate enough beforehand).
fn from_hex_raw<'a>(v: &str, bytes: &mut [u8]) -> Result<usize, FromHexError> {
let bytes_len = v.len() - 2;
let mut modulus = bytes_len % 2;
let mut bytes = vec![0u8; (bytes_len + 1) / 2];
let mut buf = 0;
let mut pos = 0;
for (idx, byte) in v.bytes().enumerate().skip(2) {
for (index, byte) in v.bytes().enumerate().skip(2) {
buf <<= 4;

match byte {
Expand All @@ -87,8 +123,8 @@ pub fn from_hex(v: &str) -> Result<Vec<u8>, String> {
continue;
}
b => {
let ch = char::from(b);
return Err(format!("invalid hex character: {}, at {}", ch, idx));
let character = char::from(b);
return Err(FromHexError::InvalidHex { character, index });
}
}

Expand All @@ -100,7 +136,7 @@ pub fn from_hex(v: &str) -> Result<Vec<u8>, String> {
}
}

Ok(bytes)
Ok(pos)
}

/// Serializes a slice of bytes.
Expand Down Expand Up @@ -204,12 +240,13 @@ where

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
if !v.starts_with("0x") {
return Err(E::custom("prefix is missing"));
return Err(E::custom(FromHexError::MissingPrefix));
}

let len = v.len();
let is_len_valid = match self.len {
ExpectedLen::Exact(ref slice) => v.len() == 2 * slice.len() + 2,
ExpectedLen::Between(min, ref slice) => v.len() <= 2 * slice.len() + 2 && v.len() > 2 * min + 2,
ExpectedLen::Exact(ref slice) => len == 2 * slice.len() + 2,
ExpectedLen::Between(min, ref slice) => len <= 2 * slice.len() + 2 && len > 2 * min + 2,
};

if !is_len_valid {
Expand All @@ -221,35 +258,7 @@ where
ExpectedLen::Between(_, slice) => slice,
};

let mut modulus = v.len() % 2;
let mut buf = 0;
let mut pos = 0;
for (idx, byte) in v.bytes().enumerate().skip(2) {
buf <<= 4;

match byte {
b'A'..=b'F' => buf |= byte - b'A' + 10,
b'a'..=b'f' => buf |= byte - b'a' + 10,
b'0'..=b'9' => buf |= byte - b'0',
b' ' | b'\r' | b'\n' | b'\t' => {
buf >>= 4;
continue;
}
b => {
let ch = char::from(b);
return Err(E::custom(&format!("invalid hex character: {}, at {}", ch, idx)));
}
}

modulus += 1;
if modulus == 2 {
modulus = 0;
bytes[pos] = buf;
pos += 1;
}
}

Ok(pos)
from_hex_raw(v, bytes).map_err(E::custom)
}

fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
Expand Down

0 comments on commit f02f892

Please sign in to comment.