You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

821 lines
21 KiB

//! Extensions
use std::{
fmt,
error,
pin::Pin,
task::{Poll,Context,},
ops::{
Range,
},
marker::{
PhantomData,
},
};
use tokio::{
io::AsyncRead,
prelude::*,
};
use futures::future::Future;
pub trait FindSliceBounds
{
type SliceType: ?Sized;
fn slice_bounds(&self, from: &Self::SliceType) -> Range<usize>;
}
pub trait SliceInPlace
{
#[deprecated = "Slow. Use `drain_inverse`"]
#[inline]
fn slice_in_place<R: std::ops::RangeBounds<usize>>(&mut self, slice: R)
{
self.drain_inverse(slice);
}
fn drain_inverse<R: std::ops::RangeBounds<usize>>(&mut self, slice: R);
}
impl SliceInPlace for String
{
fn slice_in_place<R: std::ops::RangeBounds<usize>>(&mut self, slice: R) {
let mut i=0;
self.retain(|_| (slice.contains(&i), i+=1).0);
}
fn drain_inverse<R: std::ops::RangeBounds<usize>>(&mut self, slice: R)
{
use std::ops::Bound;
match slice.end_bound() {
Bound::Excluded(&ex) => drop(self.drain(ex..)),
Bound::Included(&inc) => drop(self.drain(inc+1..)),
_ => (),
};
match slice.start_bound() {
Bound::Included(&ex) => drop(self.drain(..ex)),
Bound::Excluded(&ex) => drop(..ex+1),
_ => ()
};
}
}
impl<T> SliceInPlace for Vec<T>
{
fn slice_in_place<R: std::ops::RangeBounds<usize>>(&mut self, slice: R) {
let mut i=0;
self.retain(|_| (slice.contains(&i), i+=1).0);
}
fn drain_inverse<R: std::ops::RangeBounds<usize>>(&mut self, slice: R)
{
use std::ops::Bound;
match slice.end_bound() {
Bound::Excluded(&ex) => drop(self.drain(ex..)),
Bound::Included(&inc) => drop(self.drain(inc+1..)),
_ => (),
};
match slice.start_bound() {
Bound::Included(&ex) => drop(self.drain(..ex)),
Bound::Excluded(&ex) => drop(..ex+1),
_ => ()
};
}
}
impl<T: ?Sized + AsRef<str>> FindSliceBounds for T
{
type SliceType = str;
fn slice_bounds(&self, from: &Self::SliceType) -> Range<usize>{
let this = self.as_ref();
unsafe {
let sptr = from.as_ptr();
let eptr = sptr.add(from.len());
let ssptr = this.as_ptr();
let septr = ssptr.add(this.len());
let sptr = sptr as usize;
let ssptr = ssptr as usize;
let eptr = eptr as usize;
let septr = septr as usize;
assert!(sptr >= ssptr && sptr <= septr, "Start index of slice is outside the bounds of self");
assert!(eptr >= ssptr && eptr <= septr, "End index of slice is outside the bounds of self");
(sptr - ssptr)..(eptr - ssptr)
}
}
}
#[cfg(test)]
mod test_slice_in_place
{
use test::{Bencher, black_box};
use super::*;
#[test]
fn slice_in_place_str()
{
let mut string = String::from(" hello world ");
assert_eq!(&string[string.slice_bounds(string.trim())], string.trim());
assert_eq!(&string[string.slice_bounds(string.trim())], "hello world");
string.slice_in_place(string.slice_bounds(string.trim()));
assert_eq!(&string[..], "hello world");
let string = String::from("hello world");
assert_eq!(&string[string.slice_bounds(string.trim())], string.trim());
assert_eq!(&string[string.slice_bounds(string.trim())], "hello world");
}
#[bench]
fn bench_slice_in_place(b: &mut Bencher)
{
let mut string = String::from("hello ONE TWO world");
b.iter(|| {
black_box(string.slice_in_place(7..16));
string.push_str("ONE TWO");
});
}
#[bench]
fn bench_drain_inverse(b: &mut Bencher)
{
let mut string = String::from("hello ONE TWO world");
b.iter(|| {
black_box(string.drain_inverse(7..16));
string.push_str("ONE TWO");
});
}
#[test]
fn drain_inverse()
{
let mut string = String::from("123hello world5678");
string.drain_inverse(3..=13);
assert_eq!(&string[..], "hello world");
string.drain_inverse(6..);
assert_eq!(&string[..], "world");
}
}
#[derive(Debug)]
pub struct GroupIter<I, T, U=Box<[T]>>(std::iter::Fuse<I>, Vec<T>, usize, PhantomData<U>);
impl<U: From<Vec<T>>, I: Iterator<Item=T>, T> Iterator for GroupIter<I, T, U>
{
type Item = U;
fn next(&mut self) -> Option<Self::Item>
{
if self.1.len() == 0 {
// fill
for (_, item) in (0..self.2).zip(&mut self.0)
{
self.1.push(item);
}
}
if self.1.len() == 0 {
None
} else{
Some(std::mem::replace(&mut self.1, Vec::with_capacity(self.2)).into())
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, high) = self.0.size_hint();
(low / self.2, high.map(|x| (x / self.2) + 1)) //not too sure if this is right...
}
}
impl<I: Iterator<Item=T>, T> std::iter::FusedIterator for GroupIter<I, T>{}
pub trait GroupIterExt<I, T>: Sized
{
/// Group this iterator to return a constructed `U` of every `n` items.
///
/// # Notes
/// If there isn't `n` items left in the iterator, then the rest is returned.
fn group_into<U>(self, n: usize) -> GroupIter<I, T, U>
where U: From<Vec<T>>;
/// Group this iterator to return a `Vec<T>` of every `n` items.
///
/// # Notes
/// If there isn't `n` items left in the iterator, then the rest is returned.
#[inline] fn group(self, n: usize) -> GroupIter<I, T, Vec<T>>
{
self.group_into(n)
}
/// Group this iterator to return a boxed slice of every `n` items.
///
/// # Notes
/// If there isn't `n` items left in the iterator, then the rest is returned.
#[inline] fn group_into_boxed_slice(self, n: usize) -> GroupIter<I, T>
{
self.group_into(n)
}
}
impl<T: IntoIterator> GroupIterExt<<T as IntoIterator>::IntoIter, <T as IntoIterator>::Item> for T
{
fn group_into<U>(self, every: usize) -> GroupIter<<T as IntoIterator>::IntoIter, <T as IntoIterator>::Item, U>
where U: From<Vec<<T as IntoIterator>::Item>>
{
GroupIter(self.into_iter().fuse(), Vec::with_capacity(every), every, PhantomData)
}
}
pub trait JoinStrsExt: Sized
{
/// Join an iterator of `str` with a seperator
fn join(self, with: &str) -> String;
}
impl<T,I> JoinStrsExt for I
where I: IntoIterator<Item=T>,
T: AsRef<str>
{
/// Join an iterator of `str` with a seperator
fn join(self, with: &str) -> String
{
let mut output = String::new();
let mut first=true;
for string in self.into_iter()
{
if !first {
output.push_str(with);
}
let string = string.as_ref();
output.push_str(string);
first=false;
}
output
}
}
#[derive(Debug, Clone)]
pub struct StrChunks<'a, T: ?Sized>(&'a str, usize, PhantomData<&'a T>);
impl<'a, T: ?Sized> StrChunks<'a, T>
{
/// The rest of the string
pub fn as_str(&self) -> &'a str
{
&self.0[..]
}
/// The number of chars to break at
pub fn every(&self) -> usize
{
self.1
}
/// Set the number of chars to break at.
///
/// # Note
/// Probably don't do this as it modifies the iterators internal state weirdly. But it should be fine generally.
pub fn every_mut(&mut self) -> &mut usize
{
&mut self.1
}
}
impl<'a, T: ?Sized> Iterator for StrChunks<'a, T>
{
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item>
{
match self.0.char_indices().nth(self.1).map(|x| x.0) {
None if self.0.len() > 0 => Some(std::mem::replace(&mut self.0, "")),
Some(i) => {
let (left, right) = self.0.split_at(i);
self.0 = right;
Some(left)
},
_ => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (low, high) = self.0.chars().size_hint();
(low / self.1, high.map(|x| (x / self.1) + 1)) //not too sure if this is right...
}
}
impl<'a, T: ?Sized> std::iter::FusedIterator for StrChunks<'a, T>{}
/// Split a `str` into chunks on char boundaries
pub trait ChunkStrsExt
{
/// Split this str into a chunking iterator every specified number of chars.
///
/// If there are not enough chars left in the string, the rest is returned.
/// # Note
/// This operates on codepoints, not bytes.
fn chunk(&self, every: usize) -> StrChunks<'_, Self>;
}
#[cfg(test)]
mod chunk_tests
{
use super::*;
#[test]
fn chunk_test()
{
let string = r"a2eab409c57a829d23139c61ff2d5e479260c96158ebec0ce4d458afb85b76dłこんな僕は生きてるだけ何万人の人が悲しいんで。!?";
assert_eq!(&string.chunk(8).join("")[..], string);
for chunk in string.chunk(8) {
println!("{}", chunk);
}
}
#[test]
fn group_test()
{
let string = r"a2eab409c57a829d23139c61ff2d5e479260c96158ebec0ce4d458afb85b76dłこんな僕は生きてるだけ何万人の人が悲しいんで。!?";
let astr: String = string.chars().group(8).map(|x| -> String {x.iter().collect()}).collect();
assert_eq!(&astr[..], string);
for chunk in string.chars().group(8) {
println!("{:?}", chunk);
}
}
}
impl<T: AsRef<str>+?Sized> ChunkStrsExt for T
{
fn chunk(&self, every: usize) -> StrChunks<'_, Self>
{
StrChunks(self.as_ref(), every, PhantomData)
}
}
pub trait SwapTupleExt<T,U>: Sized
{
fn swap(self) -> (U,T);
}
impl<T,U> SwapTupleExt<T,U> for (T,U)
{
#[inline(always)] fn swap(self) -> (U,T) {
(self.1, self.0)
}
}
const ASCII_MAP: [char; 256] = [
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
];
const fn create_hex_map() -> [(u8, u8); 256]
{
let mut out = [(0, 0); 256];
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut i = 0usize;
while i <= 255
{
out[i] = (
HEX[i >> 4],
HEX[i & 0xf]
);
i+=1;
}
out
}
const HEX_MAP: [(u8, u8); 256] = create_hex_map();
pub struct HexStringView<'a, I:?Sized>(&'a I, bool);
pub struct HexStringIter<'a, I:?Sized>(std::slice::Iter<'a, u8>, (u8, u8), PhantomData<&'a I>);
pub struct HexView<'a, I:?Sized>(&'a I);
pub struct AsciiView<'a, I:?Sized>(&'a I);
pub struct AsciiIter<'a, I:?Sized>(&'a [u8], PhantomData<&'a I>);
const SPLIT_EVERY: usize = 16;
impl<'a, I: ?Sized+AsRef<[u8]>> Iterator for AsciiIter<'a, I>
{
type Item = char;
fn next(&mut self) -> Option<Self::Item>
{
match match self.0 {
[] => None,
[chr, ..] => Some(ASCII_MAP[*chr as usize]),
} {
x @ Some(_) => {
self.0 = &self.0[1..];
x
},
_ => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.0.len(), Some(self.0.len()))
}
}
impl<'a, I: ?Sized+AsRef<[u8]>> ExactSizeIterator for AsciiIter<'a, I>{}
impl<'a, I: ?Sized+AsRef<[u8]>> std::iter::FusedIterator for AsciiIter<'a, I>{}
impl<'a, I: ?Sized+AsRef<[u8]>> Iterator for HexStringIter<'a, I>
{
type Item = char;
fn next(&mut self) -> Option<Self::Item>
{
match self.1 {
ref mut buf @ (0, 0) => {
// both are taken
if let Some(&byte) = self.0.next() {
*buf = HEX_MAP[byte as usize];
} else {
return None;
}
(Some(buf.0 as char),buf.0 = 0).0
},
(0, ref mut second) => {
// first is taken
(Some(*second as char),*second = 0).0
},
#[cold] (ref mut first, _) => {
// neither are taken, usually shouldn't happen
(Some(*first as char),*first = 0).0
},
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let sz = self.0.size_hint();
(sz.0 * 2, sz.1.map(|x| x*2))
}
}
impl<'a, I: ?Sized+AsRef<[u8]>> ExactSizeIterator for HexStringIter<'a, I>{}
impl<'a, I: ?Sized+AsRef<[u8]>> std::iter::FusedIterator for HexStringIter<'a, I>{}
impl<'a, I: AsRef<[u8]>+?Sized> fmt::Display for AsciiView<'a, I>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
for byte in self.0.as_ref().iter().map(|&byte| ASCII_MAP[byte as usize])
{
use std::fmt::Write;
f.write_char(byte)?;
}
Ok(())
}
}
impl<'a, I: AsRef<[u8]>+?Sized> fmt::Display for HexView<'a, I>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
use std::iter;
let mut abuf = ['\0'; SPLIT_EVERY];
let mut last_n =0 ;
for (i, (n, &byte)) in (0..).zip(iter::repeat(0..SPLIT_EVERY).flatten().zip(self.0.as_ref().iter()))
{
if n== 0 {
write!(f,"0x{:016x}\t", i)?;
}
abuf[n] = ASCII_MAP[byte as usize];
write!(f, "{:02x} ", byte)?;
if n==SPLIT_EVERY-1 {
write!(f, "\t\t")?;
for ch in abuf.iter().filter(|&x| *x!= '\0')
{
write!(f, "{}", ch)?;
}
writeln!(f)?;
abuf = ['\0'; SPLIT_EVERY];
}
last_n = n;
}
if last_n != SPLIT_EVERY-1
{
for _ in 0..(SPLIT_EVERY-last_n)
{
write!(f, " ")?;
}
write!(f, "\t\t")?;
for ch in abuf.iter().filter(|&x| *x!= '\0')
{
write!(f, "{}", ch)?;
}
writeln!(f)?;
}
Ok(())
}
}
impl<'a, I: AsRef<[u8]>+?Sized> fmt::Display for HexStringView<'a, I>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if self.1 {
let mut iter = self.0.as_ref().iter();
if let Some(byte) = iter.next()
{
write!(f, "{:02}", byte)?;
} else {
return Ok(())
}
for byte in iter {
write!(f, " {:02x}", byte)?;
}
} else {
for byte in self.0.as_ref().iter() {
write!(f, "{:02x}", byte)?;
}
}
Ok(())
}
}
/// Extensions on byte slices to print them nicely
pub trait HexStringExt: AsRef<[u8]>
{
/// An iterator that prints readable ascii of each byte
fn iter_ascii(&self) -> AsciiIter<'_, Self>;
/// A `Display` implementor that prints ascii of each byte
fn fmt_ascii(&self) -> AsciiView<'_, Self>;
/// A pretty hex view `Display` implementor of the bytes
fn fmt_view(&self) -> HexView<'_, Self>;
/// A `Display` implementor that prints the hex of each byte in lowercase
fn fmt_hex(&self) -> HexStringView<'_, Self>;
/// An iterator over `char`s that yields the hex of each byte
///
/// # Notes
/// This yields each character one at a time, to get the hex of each byte, chunk it with a window of 2.
fn iter_hex(&self) -> HexStringIter<'_, Self>;
/// Convenience method for creating a hex string.
fn to_hex_string(&self) -> String
{
let mut string = String::with_capacity(self.as_ref().len()*2);
use fmt::Write;
write!(&mut string, "{}", self.fmt_hex()).unwrap();
string
}
/// Convenience method for creating a hex string with each byte broken by a hyphen.
fn to_broken_hex_string(&self) -> String
{
let fmt = HexStringView(
self.fmt_hex().0,
true
);
let mut string = String::with_capacity(self.as_ref().len()*3);
use fmt::Write;
write!(&mut string, "{}", fmt).unwrap();
string
}
/// Convenience method for creating a string from `fmt_view()`
#[inline] fn to_view_string(&self) -> String
{
format!("{}", self.fmt_view())
}
/// Convenience method for creating a string from `fmt_ascii()`
#[inline] fn to_ascii_string(&self) -> String
{
self.iter_ascii().collect()
}
}
impl<T: AsRef<[u8]>+?Sized> HexStringExt for T
{
fn iter_hex(&self) -> HexStringIter<'_, Self>
{
HexStringIter(self.as_ref().iter(), (0,0), PhantomData)
}
fn iter_ascii(&self) -> AsciiIter<'_, Self>
{
AsciiIter(self.as_ref(), PhantomData)
}
fn fmt_ascii(&self) -> AsciiView<'_, Self>
{
AsciiView(&self)
}
fn fmt_hex(&self) -> HexStringView<'_, Self>
{
HexStringView(&self, false)
}
fn fmt_view(&self) -> HexView<'_, Self>
{
HexView(&self)
}
}
#[pin_project]
pub struct ReadAllBytes<'a, T: AsyncRead+Unpin+?Sized>(#[pin] &'a mut T, Option<usize>);
impl<'a, T: AsyncRead+Unpin+?Sized> Future for ReadAllBytes<'a, T>
{
type Output = std::io::Result<Vec<u8>>;
fn poll(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Self::Output>
{
let fut = async move {
let this = self.project();
let mut output = Vec::with_capacity(4096*10);
let mut input = this.0;
let max = *this.1;
let mut buffer =[0u8; 4096];
let mut read;
while {read = input.read(&mut buffer[..]).await?; read!=0} {
output.extend_from_slice(&buffer[..read]);
if let Some(max) = max {
if output.len() >=max {
return Err(std::io::Error::new(std::io::ErrorKind::Other, format!("Attempted to read more than allowed max {} bytes", max)));
}
}
}
Ok(output)
};
tokio::pin!(fut);
fut.poll(ctx)
}
}
pub trait ReadAllBytesExt: AsyncRead+Unpin
{
/// Attempt to read the whole stream to a new `Vec<u8>`.
fn read_whole_stream(&mut self, max: Option<usize>) -> ReadAllBytes<'_, Self>
{
ReadAllBytes(self, max)
}
}
impl<T: AsyncRead+Unpin+?Sized> ReadAllBytesExt for T{}
pub trait FromHexExt
{
fn repl_with_hex<U: AsRef<[u8]>>(&mut self, input: U) -> Result<(), HexDecodeError>;
}
impl<T: AsMut<[u8]>+?Sized> FromHexExt for T
{
fn repl_with_hex<U: AsRef<[u8]>>(&mut self, input: U) -> Result<(), HexDecodeError> {
let out = self.as_mut();
#[inline] fn val(c: u8, idx: usize) -> Result<u8, HexDecodeError> {
match c {
b'A'..=b'F' => Ok(c - b'A' + 10),
b'a'..=b'f' => Ok(c - b'a' + 10),
b'0'..=b'9' => Ok(c - b'0'),
_ => Err(HexDecodeError{
chr: c as char,
idx,
}),
}
}
for (i, (byte, digits)) in (0..).zip(out.iter_mut().zip(input.as_ref().chunks_exact(2)))
{
*byte = val(digits[0], 2*i)? << 4 | val(digits[1], 2 * i + 1)?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct HexDecodeError {
idx: usize,
chr: char,
}
impl error::Error for HexDecodeError{}
impl fmt::Display for HexDecodeError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "Invalid hex at index {} (character was {:?})", self.idx, self.chr)
}
}
#[cfg(test)]
mod tests
{
use super::*;
fn format()
{
let bytes = b"hello world one two three \x142!";
panic!("\n{}\n", bytes.fmt_view());
}
#[test]
fn hex()
{
const INPUT_HEX: [u8; 32] = hex_literal::hex!("d0a2404173bac722b29282652f2c457b573261e3c8701b908bb0bd3ada3d7f2d");
const INPUT_STR: &str = "d0a2404173bac722b29282652f2c457b573261e3c8701b908bb0bd3ada3d7f2d";
let mut output = [0u8; 32];
output.repl_with_hex(INPUT_STR).expect("Failed!");
assert_eq!(&INPUT_HEX[..], &output[..]);
}
#[cfg(nightly)]
mod benchmarks
{
use super::*;
use test::{Bencher, black_box};
#[bench]
fn hex_via_val(b: &mut Bencher)
{
fn repl_with_hex<U: AsRef<[u8]>>(out: &mut [u8], input: U) -> Result<(), HexDecodeError> {
#[inline] fn val(c: u8, idx: usize) -> Result<u8, HexDecodeError> {
match c {
b'A'..=b'F' => Ok(c - b'A' + 10),
b'a'..=b'f' => Ok(c - b'a' + 10),
b'0'..=b'9' => Ok(c - b'0'),
_ => Err(HexDecodeError{
chr: c as char,
idx,
}),
}
}
for (i, (byte, digits)) in (0..).zip(out.iter_mut().zip(input.as_ref().chunks_exact(2)))
{
*byte = val(digits[0], 2*i)? << 4 | val(digits[1], 2 * i + 1)?;
}
Ok(())
}
const INPUT_HEX: [u8; 32] = hex_literal::hex!("d0a2404173bac722b29282652f2c457b573261e3c8701b908bb0bd3ada3d7f2d");
const INPUT_STR: &str = "d0a2404173bac722b29282652f2c457b573261e3c8701b908bb0bd3ada3d7f2d";
let mut output = [0u8; 32];
b.iter(|| {
black_box(repl_with_hex(&mut output[..], INPUT_STR).unwrap());
});
assert_eq!(&INPUT_HEX[..], &output[..]);
}
#[bench]
fn hex_via_lazy(b: &mut Bencher)
{
fn repl_with_hex<U: AsRef<[u8]>>(out: &mut [u8], input: U) -> Result<(), HexDecodeError> {
use smallmap::Map;
lazy_static::lazy_static! {
static ref MAP: Map<u8, u8> = {
let mut map = Map::new();
for c in 0..=255u8
{
map.insert(c, match c {
b'A'..=b'F' => c - b'A' + 10,
b'a'..=b'f' => c - b'a' + 10,
b'0'..=b'9' => c - b'0',
_ => continue,
});
}
map
};
}
#[inline(always)] fn val(c: u8, idx: usize) -> Result<u8, HexDecodeError> {
MAP.get(&c).copied()
.ok_or_else(|| HexDecodeError{idx, chr: c as char})
}
for (i, (byte, digits)) in (0..).zip(out.iter_mut().zip(input.as_ref().chunks_exact(2)))
{
*byte = val(digits[0], 2*i)? << 4 | val(digits[1], 2 * i + 1)?;
}
Ok(())
}
const INPUT_HEX: [u8; 32] = hex_literal::hex!("d0a2404173bac722b29282652f2c457b573261e3c8701b908bb0bd3ada3d7f2d");
const INPUT_STR: &str = "d0a2404173bac722b29282652f2c457b573261e3c8701b908bb0bd3ada3d7f2d";
let mut output = [0u8; 32];
b.iter(|| {
black_box(repl_with_hex(&mut output[..], INPUT_STR).unwrap());
});
assert_eq!(&INPUT_HEX[..], &output[..]);
}
}
}