Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add alloc feature #42

Merged
merged 5 commits into from Dec 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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