extern crate thiserror;
use crate::prelude_std::*;
use super::UnsafeBufWriteGuard;
mod encode;
mod decode;
pub use self::encode::{
TABLE_ENCODER_LEN,
TABLE_ENCODER_LOWER,
TABLE_ENCODER_UPPER
};
#[inline]
pub fn encode_hex(bytes: &[u8]) -> String {
_encode::<false>(bytes)
}
#[inline]
pub fn encode_hex_upper(bytes: &[u8]) -> String {
_encode::<true>(bytes)
}
fn _encode<const UPPER: bool>(bytes: &[u8]) -> String {
debug_assert!(bytes.len() >> (usize::BITS - 1) == 0, "size overflow");
let len = bytes.len();
let capacity = len << 1;
let ptr = bytes.as_ptr();
let mut dest = UnsafeBufWriteGuard::with_capacity(capacity);
unsafe { encode::generic::<UPPER>(ptr, &mut dest, len) };
let vec = unsafe { dest.into_full_vec() };
unsafe {
debug_assert!(str::from_utf8(&vec).is_ok(), "output bytes are valid utf-8");
String::from_utf8_unchecked(vec)
}
}
#[inline]
pub fn decode_hex(bytes: &[u8]) -> Result<Vec<u8>, DecodeError> {
let len = bytes.len();
if len & 0b1 != 0 { return Err(DecodeError::InvalidLength) }
let capacity = len >> 1;
let mut dest = UnsafeBufWriteGuard::with_capacity(capacity);
let ptr = bytes.as_ptr();
unsafe { decode::generic(ptr, &mut dest, capacity)? }
Ok(unsafe { dest.into_full_vec() })
}
#[derive(Debug, thiserror::Error)]
pub enum DecodeError {
#[error("invalid length")]
InvalidLength,
#[error("invalid character")]
InvalidChar
}
#[cfg(test)]
mod tests {
extern crate hex;
extern crate rand;
use crate::prelude_std::*;
use super::*;
use rand::{ Rng, thread_rng };
#[test]
fn rfc_provided_examples() {
let examples = [
("", ""),
("f", "66"),
("fo", "666F"),
("foo", "666F6F"),
("foob", "666F6F62"),
("fooba", "666F6F6261"),
("foobar", "666F6F626172")
];
for (bytes, encoded) in examples {
assert_eq!(encoded, encode_hex_upper(bytes.as_bytes()));
assert_eq!(encoded.to_lowercase(), encode_hex(bytes.as_bytes()));
}
}
#[test]
fn randomised() {
let expected_lengths = [
(0usize, 0usize),
(1, 2),
(2, 4),
(3, 6),
(4, 8),
(5, 10),
(6, 12),
(7, 14),
(8, 16),
(9, 18),
(10, 20),
(11, 22),
(12, 24),
(13, 26),
(14, 28),
(15, 30),
(16, 32),
(17, 34),
(18, 36),
(19, 38),
(20, 40),
(50, 100),
(100, 200),
(500, 1000),
(1000, 2000),
(100_000, 200_000),
(1_000_000, 2_000_000),
];
let mut rng = thread_rng();
for (expected_input_len, expected_output_len) in expected_lengths {
for _ in 0usize..5 {
let mut original_input = vec![0u8; expected_input_len];
rng.fill(&mut *original_input);
assert_eq!(original_input.len(), expected_input_len);
let encoded_lower = encode_hex(&original_input);
assert_eq!(encoded_lower.len(), expected_output_len);
let encoded_upper = encode_hex_upper(&original_input);
assert_eq!(encoded_upper.len(), expected_output_len);
let decoded_lower = decode_hex(encoded_lower.as_bytes())
.expect("can round trip decode just encoded data");
assert_eq!(decoded_lower.len(), expected_input_len);
assert_eq!(original_input, decoded_lower);
let decoded_upper = decode_hex(encoded_upper.as_bytes())
.expect("can round trip decode just encoded data");
assert_eq!(decoded_upper.len(), expected_input_len);
assert_eq!(original_input, decoded_upper);
}
}
}
#[test]
fn hex_crate_compat() {
let mut rng = thread_rng();
let mut bytes = vec![0u8; 1000];
rng.fill(&mut *bytes);
let bytes = &*bytes;
let wiwi_encoded = encode_hex(bytes);
let hex_encoded = hex::encode(bytes);
assert_eq!(wiwi_encoded, hex_encoded);
let wiwi_decoded_hex = decode_hex(hex_encoded.as_bytes())
.expect("wiwi can decode hex");
let hex_decoded_wiwi = hex::decode(wiwi_encoded.as_bytes())
.expect("hex can decode wiwi");
assert_eq!(wiwi_decoded_hex, hex_decoded_wiwi);
}
}