internals: Remove hex module

Remove the now unused `hex` module from internals, this functionality is
now provided by the `hex-conservative` crate.
This commit is contained in:
Tobin C. Harding 2023-07-21 10:39:27 +10:00
parent 2268b44911
commit bb8bd16302
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
4 changed files with 0 additions and 760 deletions

View File

@ -1,396 +0,0 @@
// SPDX-License-Identifier: CC0-1.0
//! Implements a buffered encoder.
//!
//! The main type of this module is [`BufEncoder`] which provides buffered hex encoding. Such is
//! faster than the usual `write!(f, "{02x}", b)?` in a for loop because it reduces dynamic
//! dispatch and decreases the number of allocations if a `String` is being created.
use core::borrow::Borrow;
pub use out_bytes::OutBytes;
use super::Case;
/// Trait for types that can be soundly converted to `OutBytes`.
///
/// To protect the API from future breakage this sealed trait guards which types can be used with
/// the `Encoder`. Currently it is implemented for byte arrays of various interesting lengths.
///
/// ## Safety
///
/// This is not `unsafe` yet but the `as_out_bytes` should always return the same reference if the
/// same reference is supplied. IOW the returned memory address and length should be the same if
/// the input memory address and length are the same.
///
/// If the trait ever becomes `unsafe` this will be required for soundness.
pub trait AsOutBytes: out_bytes::Sealed {
/// Performs the conversion.
fn as_out_bytes(&self) -> &OutBytes;
/// Performs the conversion.
fn as_mut_out_bytes(&mut self) -> &mut OutBytes;
}
/// A buffer with compile-time-known length.
///
/// This is essentially `Default + AsOutBytes` but supports lengths 1.41 doesn't.
pub trait FixedLenBuf: Sized + AsOutBytes {
/// Creates an uninitialized buffer.
///
/// The current implementtions initialize the buffer with zeroes but it should be treated a
/// uninitialized anyway.
fn uninit() -> Self;
}
/// Implements `OutBytes`
///
/// This prevents the rest of the crate from accessing the field of `OutBytes`.
mod out_bytes {
use super::AsOutBytes;
/// A byte buffer that can only be written-into.
///
/// You shouldn't concern yourself with this, just call `BufEncoder::new` with your array.
///
/// This prepares the API for potential future support of `[MaybeUninit<u8>]`. We don't want to use
/// `unsafe` until it's proven to be needed but if it does we have an easy, compatible upgrade
/// option.
///
/// Warning: `repr(transparent)` is an internal implementation detail and **must not** be
/// relied on!
#[repr(transparent)]
pub struct OutBytes([u8]);
impl OutBytes {
/// Returns the first `len` bytes as initialized.
///
/// Not `unsafe` because we don't use `unsafe` (yet).
///
/// ## Panics
///
/// The method panics if `len` is out of bounds.
#[track_caller]
pub(crate) fn assume_init(&self, len: usize) -> &[u8] { &self.0[..len] }
/// Writes given bytes into the buffer.
///
/// ## Panics
///
/// The method panics if pos is out of bounds or `bytes` don't fit into the buffer.
#[track_caller]
pub(crate) fn write(&mut self, pos: usize, bytes: &[u8]) {
self.0[pos..(pos + bytes.len())].copy_from_slice(bytes);
}
/// Returns the length of the buffer.
pub(crate) fn len(&self) -> usize { self.0.len() }
fn from_bytes(slice: &[u8]) -> &Self {
// SAFETY: copied from std
// conversion of reference to pointer of the same referred type is always sound,
// including in unsized types.
// Thanks to repr(transparent) the types have the same layout making the other
// conversion sound.
// The pointer was just created from a reference that's still alive so dereferencing is
// sound.
unsafe { &*(slice as *const [u8] as *const Self) }
}
fn from_mut_bytes(slice: &mut [u8]) -> &mut Self {
// SAFETY: copied from std
// conversion of reference to pointer of the same referred type is always sound,
// including in unsized types.
// Thanks to repr(transparent) the types have the same layout making the other
// conversion sound.
// The pointer was just created from a reference that's still alive so dereferencing is
// sound.
unsafe { &mut *(slice as *mut [u8] as *mut Self) }
}
}
macro_rules! impl_from_array {
($($len:expr),* $(,)?) => {
$(
impl super::FixedLenBuf for [u8; $len] {
fn uninit() -> Self {
[0u8; $len]
}
}
impl AsOutBytes for [u8; $len] {
fn as_out_bytes(&self) -> &OutBytes {
OutBytes::from_bytes(self)
}
fn as_mut_out_bytes(&mut self) -> &mut OutBytes {
OutBytes::from_mut_bytes(self)
}
}
impl Sealed for [u8; $len] {}
impl<'a> super::super::display::DisplayHex for &'a [u8; $len / 2] {
type Display = super::super::display::DisplayArray<core::slice::Iter<'a, u8>, [u8; $len]>;
fn as_hex(self) -> Self::Display {
super::super::display::DisplayArray::new(self.iter())
}
fn hex_reserve_suggestion(self) -> usize {
$len
}
}
)*
}
}
impl<T: AsOutBytes + ?Sized> AsOutBytes for &'_ mut T {
fn as_out_bytes(&self) -> &OutBytes { (**self).as_out_bytes() }
fn as_mut_out_bytes(&mut self) -> &mut OutBytes { (**self).as_mut_out_bytes() }
}
impl<T: AsOutBytes + ?Sized> Sealed for &'_ mut T {}
impl AsOutBytes for OutBytes {
fn as_out_bytes(&self) -> &OutBytes { self }
fn as_mut_out_bytes(&mut self) -> &mut OutBytes { self }
}
impl Sealed for OutBytes {}
// As a sanity check we only provide conversions for even, non-empty arrays.
// Weird lengths 66 and 130 are provided for serialized public keys.
impl_from_array!(
2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 40, 64, 66, 128, 130, 256, 512,
1024, 2048, 4096, 8192
);
/// Prevents outside crates from implementing the trait
pub trait Sealed {}
}
/// Hex-encodes bytes into the provided buffer.
///
/// This is an important building block for fast hex-encoding. Because string writing tools
/// provided by `core::fmt` involve dynamic dispatch and don't allow reserving capacity in strings
/// buffering the hex and then formatting it is significantly faster.
pub struct BufEncoder<T: AsOutBytes> {
buf: T,
pos: usize,
}
impl<T: AsOutBytes> BufEncoder<T> {
/// Creates an empty `BufEncoder`.
///
/// This is usually used with uninitialized (zeroed) byte array allocated on stack.
/// This can only be constructed with an even-length, non-empty array.
#[inline]
pub fn new(buf: T) -> Self { BufEncoder { buf, pos: 0 } }
/// Encodes `byte` as hex in given `case` and appends it to the buffer.
///
/// ## Panics
///
/// The method panics if the buffer is full.
#[inline]
#[track_caller]
pub fn put_byte(&mut self, byte: u8, case: Case) {
self.buf.as_mut_out_bytes().write(self.pos, &super::byte_to_hex(byte, case.table()));
self.pos += 2;
}
/// Encodes `bytes` as hex in given `case` and appends them to the buffer.
///
/// ## Panics
///
/// The method panics if the bytes wouldn't fit the buffer.
#[inline]
#[track_caller]
pub fn put_bytes<I>(&mut self, bytes: I, case: Case)
where
I: IntoIterator,
I::Item: Borrow<u8>,
{
self.put_bytes_inner(bytes.into_iter(), case)
}
#[inline]
#[track_caller]
fn put_bytes_inner<I>(&mut self, bytes: I, case: Case)
where
I: Iterator,
I::Item: Borrow<u8>,
{
// May give the compiler better optimization opportunity
if let Some(max) = bytes.size_hint().1 {
assert!(max <= self.space_remaining());
}
for byte in bytes {
self.put_byte(*byte.borrow(), case);
}
}
/// Encodes as many `bytes` as fit into the buffer as hex and return the remainder.
///
/// This method works just like `put_bytes` but instead of panicking it returns the unwritten
/// bytes. The method returns an empty slice if all bytes were written
#[must_use = "this may write only part of the input buffer"]
#[inline]
#[track_caller]
pub fn put_bytes_min<'a>(&mut self, bytes: &'a [u8], case: Case) -> &'a [u8] {
let to_write = self.space_remaining().min(bytes.len());
self.put_bytes(&bytes[..to_write], case);
&bytes[to_write..]
}
/// Returns true if no more bytes can be written into the buffer.
#[inline]
pub fn is_full(&self) -> bool { self.pos == self.buf.as_out_bytes().len() }
/// Returns the written bytes as a hex `str`.
#[inline]
pub fn as_str(&self) -> &str {
core::str::from_utf8(self.buf.as_out_bytes().assume_init(self.pos))
.expect("we only write ASCII")
}
/// Resets the buffer to become empty.
#[inline]
pub fn clear(&mut self) { self.pos = 0; }
/// How many bytes can be written to this buffer.
///
/// Note that this returns the number of bytes before encoding, not number of hex digits.
#[inline]
pub fn space_remaining(&self) -> usize { (self.buf.as_out_bytes().len() - self.pos) / 2 }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty() {
let mut buf = [0u8; 2];
let encoder = BufEncoder::new(&mut buf);
assert_eq!(encoder.as_str(), "");
assert!(!encoder.is_full());
}
#[test]
fn single_byte_exact_buf() {
let mut buf = [0u8; 2];
let mut encoder = BufEncoder::new(&mut buf);
assert_eq!(encoder.space_remaining(), 1);
encoder.put_byte(42, Case::Lower);
assert_eq!(encoder.as_str(), "2a");
assert_eq!(encoder.space_remaining(), 0);
assert!(encoder.is_full());
encoder.clear();
assert_eq!(encoder.space_remaining(), 1);
assert!(!encoder.is_full());
encoder.put_byte(42, Case::Upper);
assert_eq!(encoder.as_str(), "2A");
assert_eq!(encoder.space_remaining(), 0);
assert!(encoder.is_full());
}
#[test]
fn single_byte_oversized_buf() {
let mut buf = [0u8; 4];
let mut encoder = BufEncoder::new(&mut buf);
assert_eq!(encoder.space_remaining(), 2);
encoder.put_byte(42, Case::Lower);
assert_eq!(encoder.space_remaining(), 1);
assert_eq!(encoder.as_str(), "2a");
assert!(!encoder.is_full());
encoder.clear();
assert_eq!(encoder.space_remaining(), 2);
encoder.put_byte(42, Case::Upper);
assert_eq!(encoder.as_str(), "2A");
assert_eq!(encoder.space_remaining(), 1);
assert!(!encoder.is_full());
}
#[test]
fn two_bytes() {
let mut buf = [0u8; 4];
let mut encoder = BufEncoder::new(&mut buf);
encoder.put_byte(42, Case::Lower);
assert_eq!(encoder.space_remaining(), 1);
encoder.put_byte(255, Case::Lower);
assert_eq!(encoder.space_remaining(), 0);
assert_eq!(encoder.as_str(), "2aff");
assert!(encoder.is_full());
encoder.clear();
assert!(!encoder.is_full());
encoder.put_byte(42, Case::Upper);
encoder.put_byte(255, Case::Upper);
assert_eq!(encoder.as_str(), "2AFF");
assert!(encoder.is_full());
}
#[test]
fn put_bytes_min() {
let mut buf = [0u8; 2];
let mut encoder = BufEncoder::new(&mut buf);
let remainder = encoder.put_bytes_min(b"", Case::Lower);
assert_eq!(remainder, b"");
assert_eq!(encoder.as_str(), "");
let remainder = encoder.put_bytes_min(b"*", Case::Lower);
assert_eq!(remainder, b"");
assert_eq!(encoder.as_str(), "2a");
encoder.clear();
let remainder = encoder.put_bytes_min(&[42, 255], Case::Lower);
assert_eq!(remainder, &[255]);
assert_eq!(encoder.as_str(), "2a");
}
#[test]
fn same_as_fmt() {
use core::fmt::{self, Write};
struct Writer {
buf: [u8; 2],
pos: usize,
}
impl Writer {
fn as_str(&self) -> &str { core::str::from_utf8(&self.buf[..self.pos]).unwrap() }
}
impl Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
assert!(self.pos <= 2);
if s.len() > 2 - self.pos {
Err(fmt::Error)
} else {
self.buf[self.pos..(self.pos + s.len())].copy_from_slice(s.as_bytes());
self.pos += s.len();
Ok(())
}
}
}
let mut writer = Writer { buf: [0u8; 2], pos: 0 };
let mut buf = [0u8; 2];
let mut encoder = BufEncoder::new(&mut buf);
for i in 0..=255 {
write!(writer, "{:02x}", i).unwrap();
encoder.put_byte(i, Case::Lower);
assert_eq!(encoder.as_str(), writer.as_str());
writer.pos = 0;
encoder.clear();
}
for i in 0..=255 {
write!(writer, "{:02X}", i).unwrap();
encoder.put_byte(i, Case::Upper);
assert_eq!(encoder.as_str(), writer.as_str());
writer.pos = 0;
encoder.clear();
}
}
}

View File

@ -1,303 +0,0 @@
// SPDX-License-Identifier: CC0-1.0
//! Helpers for displaying bytes as hex strings.
//!
//! This module provides a trait for displaying things as hex as well as an implementation for
//! `&[u8]`.
use core::borrow::Borrow;
use core::fmt;
use super::buf_encoder::{BufEncoder, OutBytes};
use super::Case;
use crate::hex::buf_encoder::FixedLenBuf;
#[cfg(feature = "alloc")]
use crate::prelude::*;
/// Extension trait for types that can be displayed as hex.
///
/// Types that have a single, obvious text representation being hex should **not** implement this
/// trait and simply implement `Display` instead.
///
/// This trait should be generally implemented for references only. We would prefer to use GAT but
/// that is beyond our MSRV. As a lint we require the `IsRef` trait which is implemented for all
/// references.
pub trait DisplayHex: Copy + sealed::IsRef {
/// The type providing [`fmt::Display`] implementation.
///
/// This is usually a wrapper type holding a reference to `Self`.
type Display: fmt::LowerHex + fmt::UpperHex;
/// Display `Self` as a continuous sequence of ASCII hex chars.
fn as_hex(self) -> Self::Display;
/// Create a lower-hex-encoded string.
///
/// A shorthand for `to_hex_string(Case::Lower)`, so that `Case` doesn't need to be imported.
///
/// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`.
#[cfg(feature = "alloc")]
fn to_lower_hex_string(self) -> String { self.to_hex_string(Case::Lower) }
/// Create an upper-hex-encoded string.
///
/// A shorthand for `to_hex_string(Case::Upper)`, so that `Case` doesn't need to be imported.
///
/// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`.
#[cfg(feature = "alloc")]
fn to_upper_hex_string(self) -> String { self.to_hex_string(Case::Upper) }
/// Create a hex-encoded string.
///
/// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`.
#[cfg(feature = "alloc")]
fn to_hex_string(self, case: Case) -> String {
let mut string = String::new();
self.append_hex_to_string(case, &mut string);
string
}
/// Appends hex-encoded content to an existing `String`.
///
/// This may be faster than `write!(string, "{:x}", self.display_hex())` because it uses
/// `reserve_sugggestion`.
#[cfg(feature = "alloc")]
fn append_hex_to_string(self, case: Case, string: &mut String) {
use fmt::Write;
string.reserve(self.hex_reserve_suggestion());
match case {
Case::Lower => write!(string, "{:x}", self.as_hex()),
Case::Upper => write!(string, "{:X}", self.as_hex()),
}
.unwrap_or_else(|_| {
let name = core::any::type_name::<Self::Display>();
// We don't expect `std` to ever be buggy, so the bug is most likely in the `Display`
// impl of `Self::Display`.
panic!("The implementation of Display for {} returned an error when it shouldn't", name)
})
}
/// Hints how much bytes to reserve when creating a `String`.
///
/// Implementors that know the number of produced bytes upfront should override this.
/// Defaults to 0.
///
// We prefix the name with `hex_` to avoid potential collision with other methods.
fn hex_reserve_suggestion(self) -> usize { 0 }
}
mod sealed {
/// Trait marking a shared reference.
pub trait IsRef: Copy {}
impl<T: ?Sized> IsRef for &'_ T {}
}
impl<'a> DisplayHex for &'a [u8] {
type Display = DisplayByteSlice<'a>;
#[inline]
fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
#[inline]
fn hex_reserve_suggestion(self) -> usize {
// Since the string wouldn't fit into address space if this overflows (actually even for
// smaller amounts) it's better to panic right away. It should also give the optimizer
// better opportunities.
self.len().checked_mul(2).expect("the string wouldn't fit into address space")
}
}
#[cfg(feature = "alloc")]
impl<'a> DisplayHex for &'a alloc::vec::Vec<u8> {
type Display = DisplayByteSlice<'a>;
#[inline]
fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
#[inline]
fn hex_reserve_suggestion(self) -> usize {
// Since the string wouldn't fit into address space if this overflows (actually even for
// smaller amounts) it's better to panic right away. It should also give the optimizer
// better opportunities.
self.len().checked_mul(2).expect("the string wouldn't fit into address space")
}
}
/// Displays byte slice as hex.
///
/// Created by [`<&[u8] as DisplayHex>::as_hex`](DisplayHex::as_hex).
pub struct DisplayByteSlice<'a> {
// pub because we want to keep lengths in sync
pub(crate) bytes: &'a [u8],
}
impl<'a> DisplayByteSlice<'a> {
fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
let mut buf = [0u8; 1024];
let mut encoder = super::BufEncoder::new(&mut buf);
let mut chunks = self.bytes.chunks_exact(512);
for chunk in &mut chunks {
encoder.put_bytes(chunk, case);
f.write_str(encoder.as_str())?;
encoder.clear();
}
encoder.put_bytes(chunks.remainder(), case);
f.write_str(encoder.as_str())
}
}
impl<'a> fmt::LowerHex for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
}
impl<'a> fmt::UpperHex for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
}
/// Displays byte array as hex.
///
/// Created by [`<&[u8; LEN] as DisplayHex>::as_hex`](DisplayHex::as_hex).
pub struct DisplayArray<A: Clone + IntoIterator, B: FixedLenBuf>
where
A::Item: Borrow<u8>,
{
array: A,
_buffer_marker: core::marker::PhantomData<B>,
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
/// Creates the wrapper.
pub fn new(array: A) -> Self { DisplayArray { array, _buffer_marker: Default::default() } }
fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
let mut buf = B::uninit();
let mut encoder = super::BufEncoder::new(&mut buf);
encoder.put_bytes(self.array.clone(), case);
f.pad_integral(true, "0x", encoder.as_str())
}
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::LowerHex for DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::UpperHex for DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
}
/// Format known-length array as hex.
///
/// This supports all formatting options of formatter and may be faster than calling
/// `display_as_hex()` on an arbitrary `&[u8]`. Note that the implementation intentionally keeps
/// leading zeros even when not requested. This is designed to display values such as hashes and
/// keys and removing leading zeros would be confusing.
///
/// ## Parameters
///
/// * `$formatter` - a [`fmt::Formatter`].
/// * `$len` known length of `$bytes`, must be a const expression.
/// * `$bytes` - bytes to be encoded, most likely a reference to an array.
/// * `$case` - value of type [`Case`] determining whether to format as lower or upper case.
///
/// ## Panics
///
/// This macro panics if `$len` is not equal to `$bytes.len()`. It also fails to compile if `$len`
/// is more than half of `usize::MAX`.
#[macro_export]
macro_rules! fmt_hex_exact {
($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
// statically check $len
#[allow(deprecated)]
const _: () = [()][($len > usize::MAX / 2) as usize];
assert_eq!($bytes.len(), $len);
let mut buf = [0u8; $len * 2];
let buf = $crate::hex::buf_encoder::AsOutBytes::as_mut_out_bytes(&mut buf);
$crate::hex::display::fmt_hex_exact_fn($formatter, buf, $bytes, $case)
}};
}
pub use fmt_hex_exact;
// Implementation detail of `write_hex_exact` macro to de-duplicate the code
#[doc(hidden)]
#[inline]
pub fn fmt_hex_exact_fn<I>(
f: &mut fmt::Formatter,
buf: &mut OutBytes,
bytes: I,
case: Case,
) -> fmt::Result
where
I: IntoIterator,
I::Item: Borrow<u8>,
{
let mut encoder = BufEncoder::new(buf);
encoder.put_bytes(bytes, case);
f.pad_integral(true, "0x", encoder.as_str())
}
#[cfg(test)]
mod tests {
#[cfg(feature = "alloc")]
use super::*;
#[cfg(feature = "alloc")]
mod alloc {
use super::*;
fn check_encoding(bytes: &[u8]) {
use core::fmt::Write;
let s1 = bytes.to_lower_hex_string();
let mut s2 = String::with_capacity(bytes.len() * 2);
for b in bytes {
write!(s2, "{:02x}", b).unwrap();
}
assert_eq!(s1, s2);
}
#[test]
fn empty() { check_encoding(b""); }
#[test]
fn single() { check_encoding(b"*"); }
#[test]
fn two() { check_encoding(b"*x"); }
#[test]
fn just_below_boundary() { check_encoding(&[42; 512]); }
#[test]
fn just_above_boundary() { check_encoding(&[42; 513]); }
#[test]
fn just_above_double_boundary() { check_encoding(&[42; 1025]); }
#[test]
fn fmt_exact_macro() {
use crate::alloc::string::ToString;
struct Dummy([u8; 32]);
impl fmt::Display for Dummy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_hex_exact!(f, 32, &self.0, Case::Lower)
}
}
assert_eq!(Dummy([42; 32]).to_string(), "2a".repeat(32));
}
}
}

View File

@ -1,54 +0,0 @@
// SPDX-License-Identifier: CC0-1.0
//! Helpers for encoding bytes as hex strings.
pub mod buf_encoder;
pub mod display;
pub use buf_encoder::BufEncoder;
/// Reexports of extension traits.
pub mod exts {
pub use super::display::DisplayHex;
}
/// Possible case of hex.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Case {
/// Produce lower-case chars (`[0-9a-f]`).
///
/// This is the default.
Lower,
/// Produce upper-case chars (`[0-9A-F]`).
Upper,
}
impl Default for Case {
fn default() -> Self { Case::Lower }
}
impl Case {
/// Returns the encoding table.
///
/// The returned table may only contain displayable ASCII chars.
#[inline]
#[rustfmt::skip]
pub(crate) fn table(self) -> &'static [u8; 16] {
static LOWER: [u8; 16] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f'];
static UPPER: [u8; 16] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F'];
match self {
Case::Lower => &LOWER,
Case::Upper => &UPPER,
}
}
}
/// Encodes single byte as two ASCII chars using the given table.
///
/// The function guarantees only returning values from the provided table.
#[inline]
pub(crate) fn byte_to_hex(byte: u8, table: &[u8; 16]) -> [u8; 2] {
[table[usize::from(byte.wrapping_shr(4))], table[usize::from(byte & 0x0F)]]
}

View File

@ -19,13 +19,6 @@ extern crate alloc;
extern crate std; extern crate std;
pub mod error; pub mod error;
pub mod hex;
pub mod macros; pub mod macros;
mod parse; mod parse;
pub mod serde; pub mod serde;
/// Mainly reexports based on features.
pub(crate) mod prelude {
#[cfg(feature = "alloc")]
pub(crate) use alloc::string::String;
}