Skip to content

Commit

Permalink
Add alloc feature (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
Luro02 committed Dec 23, 2020
1 parent d8fa31f commit a354af2
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 44 deletions.
3 changes: 3 additions & 0 deletions .gitlab-ci-matrix.yml
Expand Up @@ -22,6 +22,9 @@ steps:
script:
- cargo test
- cargo test --no-default-features
- cargo test --no-default-features --features alloc
- cargo test --no-default-features --features std
- cargo test --no-default-features --features serde
dependencies:
- compile

Expand Down
19 changes: 18 additions & 1 deletion .gitlab-ci.yml
Expand Up @@ -115,6 +115,9 @@ test:rust-stable:
- cargo test
- cargo test --features serde
- cargo test --no-default-features
- cargo test --no-default-features --features std
- cargo test --no-default-features --features alloc
- cargo test --no-default-features --features serde
variables:
RUST_KEY: rust-stable

Expand All @@ -127,6 +130,9 @@ test:rust-stable-musl:
- cargo test
- cargo test --features serde
- cargo test --no-default-features
- cargo test --no-default-features --features std
- cargo test --no-default-features --features alloc
- cargo test --no-default-features --features serde
variables:
RUST_KEY: rust-stable-musl

Expand All @@ -139,6 +145,9 @@ test:rust-beta:
- cargo test
- cargo test --features serde
- cargo test --no-default-features
- cargo test --no-default-features --features std
- cargo test --no-default-features --features alloc
- cargo test --no-default-features --features serde
variables:
RUST_KEY: rust-beta

Expand All @@ -151,6 +160,9 @@ test:rust-beta-musl:
- cargo test
- cargo test --features serde
- cargo test --no-default-features
- cargo test --no-default-features --features std
- cargo test --no-default-features --features alloc
- cargo test --no-default-features --features serde
variables:
RUST_KEY: rust-beta-musl

Expand All @@ -163,6 +175,9 @@ test:rust-nightly:
- cargo test
- cargo test --features serde
- cargo test --no-default-features
- cargo test --no-default-features --features std
- cargo test --no-default-features --features alloc
- cargo test --no-default-features --features serde
variables:
RUST_KEY: rust-nightly
allow_failure: true
Expand All @@ -176,7 +191,9 @@ test:rust-nightly-musl:
- cargo test
- cargo test --features serde
- cargo test --no-default-features
- cargo test --no-default-features --features std
- cargo test --no-default-features --features alloc
- cargo test --no-default-features --features serde
variables:
RUST_KEY: rust-nightly-musl
allow_failure: true

5 changes: 4 additions & 1 deletion .travis.yml
Expand Up @@ -21,9 +21,12 @@ install:

script:
- if [[ $TRAVIS_RUST_VERSION == "stable" && $TRAVIS_OS_NAME == "linux" ]]; then cargo fmt -- --check; fi
- if [[ $TRAVIS_RUST_VERSION == "stable" && $TRAVIS_OS_NAME == "linux" ]]; then cargo clippy -- -D clippy::all; fi
- if [[ $TRAVIS_RUST_VERSION == "stable" && $TRAVIS_OS_NAME == "linux" ]]; then cargo clippy -- -W clippy::all; fi
- cargo test
- cargo test --features serde
- cargo test --no-default-features
- cargo test --no-default-features --features std
- cargo test --no-default-features --features alloc
- cargo test --no-default-features --features serde
# Validate benches still work.
- cargo bench --all -- --test
5 changes: 3 additions & 2 deletions Cargo.toml
Expand Up @@ -16,14 +16,15 @@ travis-ci = { repository = "KokaKiwi/rust-hex", branch = "master" }

[features]
default = ["std"]
std = []
alloc = []
std = ["alloc"]

[[bench]]
name = "hex"
harness = false

[dependencies]
serde = { version = "1.0", optional = true }
serde = { version = "1.0", default-features = false, optional = true }

[dev-dependencies]
criterion = "0.3"
Expand Down
6 changes: 5 additions & 1 deletion src/error.rs
Expand Up @@ -33,13 +33,17 @@ impl fmt::Display for FromHexError {
}

#[cfg(test)]
// this feature flag is here to suppress unused
// warnings of `super::*` and `pretty_assertions::assert_eq`
#[cfg(feature = "alloc")]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
#[cfg(feature = "alloc")]
use alloc::string::ToString;
use pretty_assertions::assert_eq;

#[test]
#[cfg(feature = "alloc")]
fn test_display() {
assert_eq!(
FromHexError::InvalidHexCharacter { c: '\n', index: 5 }.to_string(),
Expand Down
82 changes: 62 additions & 20 deletions src/lib.rs
Expand Up @@ -15,6 +15,16 @@
//! # Example
//!
//! ```
//! # #[cfg(not(feature = "alloc"))]
//! # let mut output = [0; 0x18];
//! #
//! # #[cfg(not(feature = "alloc"))]
//! # hex::encode_to_slice(b"Hello world!", &mut output).unwrap();
//! #
//! # #[cfg(not(feature = "alloc"))]
//! # let hex_string = ::core::str::from_utf8(&output).unwrap();
//! #
//! # #[cfg(feature = "alloc")]
//! let hex_string = hex::encode("Hello world!");
//!
//! println!("{}", hex_string); // Prints "48656c6c6f20776f726c6421"
Expand All @@ -27,9 +37,9 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::unreadable_literal)]

#[cfg(not(feature = "std"))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(not(feature = "std"))]
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};

use core::iter;
Expand All @@ -41,7 +51,9 @@ pub use crate::error::FromHexError;
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub mod serde;
#[cfg(feature = "serde")]
pub use crate::serde::{deserialize, serialize, serialize_upper};
pub use crate::serde::deserialize;
#[cfg(all(feature = "alloc", feature = "serde"))]
pub use crate::serde::{serialize, serialize_upper};

/// Encoding values as hex string.
///
Expand Down Expand Up @@ -95,7 +107,7 @@ impl<'a> Iterator for BytesToHexChars<'a> {
Some(current) => Some(current),
None => self.inner.next().map(|byte| {
let current = self.table[(byte >> 4) as usize] as char;
self.next = Some(self.table[(byte & 0xf) as usize] as char);
self.next = Some(self.table[(byte & 0x0F) as usize] as char);
current
}),
}
Expand All @@ -117,6 +129,7 @@ impl<'a> iter::ExactSizeIterator for BytesToHexChars<'a> {
}
}

#[inline]
fn encode_to_iter<T: iter::FromIterator<char>>(table: &'static [u8; 16], source: &[u8]) -> T {
BytesToHexChars::new(source, table).collect()
}
Expand All @@ -139,17 +152,14 @@ impl<T: AsRef<[u8]>> ToHex for T {
///
/// ```
/// use hex::FromHex;
/// use core::str;
///
/// match Vec::from_hex("48656c6c6f20776f726c6421") {
/// Ok(vec) => {
/// for b in vec {
/// println!("{}", b as char);
/// }
/// }
/// Err(e) => {
/// // Deal with the error ...
/// }
/// }
/// let buffer = <[u8; 12]>::from_hex("48656c6c6f20776f726c6421")?;
/// let string = str::from_utf8(&buffer).expect("invalid buffer length");
///
/// println!("{}", string); // prints "Hello world!"
/// # assert_eq!("Hello world!", string);
/// # Ok::<(), hex::FromHexError>(())
/// ```
pub trait FromHex: Sized {
type Error;
Expand All @@ -174,6 +184,7 @@ fn val(c: u8, idx: usize) -> Result<u8, FromHexError> {
}
}

#[cfg(feature = "alloc")]
impl FromHex for Vec<u8> {
type Error = FromHexError;

Expand All @@ -198,7 +209,7 @@ macro_rules! from_hex_array_impl {
type Error = FromHexError;

fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
let mut out = [0u8; $len];
let mut out = [0_u8; $len];
decode_to_slice(hex, &mut out as &mut [u8])?;
Ok(out)
}
Expand Down Expand Up @@ -243,6 +254,8 @@ from_hex_array_impl! {
/// assert_eq!(hex::encode("Hello world!"), "48656c6c6f20776f726c6421");
/// assert_eq!(hex::encode(vec![1, 2, 3, 15, 16]), "0102030f10");
/// ```
#[must_use]
#[cfg(feature = "alloc")]
pub fn encode<T: AsRef<[u8]>>(data: T) -> String {
data.encode_hex()
}
Expand All @@ -257,6 +270,8 @@ pub fn encode<T: AsRef<[u8]>>(data: T) -> String {
/// assert_eq!(hex::encode_upper("Hello world!"), "48656C6C6F20776F726C6421");
/// assert_eq!(hex::encode_upper(vec![1, 2, 3, 15, 16]), "0102030F10");
/// ```
#[must_use]
#[cfg(feature = "alloc")]
pub fn encode_upper<T: AsRef<[u8]>>(data: T) -> String {
data.encode_hex_upper()
}
Expand All @@ -277,6 +292,7 @@ pub fn encode_upper<T: AsRef<[u8]>>(data: T) -> String {
/// assert_eq!(hex::decode("123"), Err(hex::FromHexError::OddLength));
/// assert!(hex::decode("foo").is_err());
/// ```
#[cfg(feature = "alloc")]
pub fn decode<T: AsRef<[u8]>>(data: T) -> Result<Vec<u8>, FromHexError> {
FromHex::from_hex(data)
}
Expand Down Expand Up @@ -316,11 +332,14 @@ pub fn decode_to_slice<T: AsRef<[u8]>>(data: T, out: &mut [u8]) -> Result<(), Fr
// (4, 5)
// (6, 7)
// ...
#[inline]
fn generate_iter(len: usize) -> impl Iterator<Item = (usize, usize)> {
(0..len).step_by(2).zip((0..len).skip(1).step_by(2))
}

// the inverse of `val`.
#[inline]
#[must_use]
fn byte2hex(byte: u8, table: &[u8; 16]) -> (u8, u8) {
let high = table[((byte & 0xf0) >> 4) as usize];
let low = table[(byte & 0x0f) as usize];
Expand Down Expand Up @@ -350,7 +369,11 @@ pub fn encode_to_slice<T: AsRef<[u8]>>(input: T, output: &mut [u8]) -> Result<()
return Err(FromHexError::InvalidStringLength);
}

for (byte, (i, j)) in input.as_ref().iter().zip(generate_iter(input.as_ref().len() * 2)) {
for (byte, (i, j)) in input
.as_ref()
.iter()
.zip(generate_iter(input.as_ref().len() * 2))
{
let (high, low) = byte2hex(*byte, HEX_CHARS_LOWER);
output[i] = high;
output[j] = low;
Expand All @@ -362,11 +385,12 @@ pub fn encode_to_slice<T: AsRef<[u8]>>(input: T, output: &mut [u8]) -> Result<()
#[cfg(test)]
mod test {
use super::*;
#[cfg(not(feature = "std"))]
#[cfg(feature = "alloc")]
use alloc::string::ToString;
use pretty_assertions::assert_eq;

#[test]
#[cfg(feature = "alloc")]
fn test_gen_iter() {
let mut result = Vec::new();
result.push((0, 1));
Expand Down Expand Up @@ -405,38 +429,53 @@ mod test {

let mut output_3 = [0; 4];

assert_eq!(decode_to_slice(b"6", &mut output_3), Err(FromHexError::OddLength));
assert_eq!(
decode_to_slice(b"6", &mut output_3),
Err(FromHexError::OddLength)
);
}

#[test]
#[cfg(feature = "alloc")]
fn test_encode() {
assert_eq!(encode("foobar"), "666f6f626172");
}

#[test]
#[cfg(feature = "alloc")]
fn test_decode() {
assert_eq!(decode("666f6f626172"), Ok(String::from("foobar").into_bytes()));
assert_eq!(
decode("666f6f626172"),
Ok(String::from("foobar").into_bytes())
);
}

#[test]
#[cfg(feature = "alloc")]
pub fn test_from_hex_okay_str() {
assert_eq!(Vec::from_hex("666f6f626172").unwrap(), b"foobar");
assert_eq!(Vec::from_hex("666F6F626172").unwrap(), b"foobar");
}

#[test]
#[cfg(feature = "alloc")]
pub fn test_from_hex_okay_bytes() {
assert_eq!(Vec::from_hex(b"666f6f626172").unwrap(), b"foobar");
assert_eq!(Vec::from_hex(b"666F6F626172").unwrap(), b"foobar");
}

#[test]
#[cfg(feature = "alloc")]
pub fn test_invalid_length() {
assert_eq!(Vec::from_hex("1").unwrap_err(), FromHexError::OddLength);
assert_eq!(Vec::from_hex("666f6f6261721").unwrap_err(), FromHexError::OddLength);
assert_eq!(
Vec::from_hex("666f6f6261721").unwrap_err(),
FromHexError::OddLength
);
}

#[test]
#[cfg(feature = "alloc")]
pub fn test_invalid_char() {
assert_eq!(
Vec::from_hex("66ag").unwrap_err(),
Expand All @@ -445,11 +484,13 @@ mod test {
}

#[test]
#[cfg(feature = "alloc")]
pub fn test_empty() {
assert_eq!(Vec::from_hex("").unwrap(), b"");
}

#[test]
#[cfg(feature = "alloc")]
pub fn test_from_hex_whitespace() {
assert_eq!(
Vec::from_hex("666f 6f62617").unwrap_err(),
Expand All @@ -471,6 +512,7 @@ mod test {
}

#[test]
#[cfg(feature = "alloc")]
fn test_to_hex() {
assert_eq!(
[0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72].encode_hex::<String>(),
Expand Down

0 comments on commit a354af2

Please sign in to comment.