#![warn(missing_docs)]
use core::iter::FromIterator;
mod internal {
use crate::{
decode::{hex_decode_array_with_case, hex_decode_with_case, CheckCase},
encode::encode,
};
use alloc::{borrow::Cow, string::String, vec, vec::Vec};
use core::{fmt, iter::FromIterator, mem::MaybeUninit};
use serde::{
de::{Error, Unexpected, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
struct Text<'a>(Cow<'a, str>);
impl<'de> Deserialize<'de> for Text<'de> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct TextVisitor;
impl<'de> Visitor<'de> for TextVisitor {
type Value = Text<'de>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a string")
}
fn visit_str<E: Error>(self, text: &str) -> Result<Self::Value, E> {
Ok(Text(Cow::Owned(text.into())))
}
fn visit_borrowed_str<E: Error>(self, text: &'de str) -> Result<Self::Value, E> {
Ok(Text(Cow::Borrowed(text)))
}
fn visit_string<E: Error>(self, text: String) -> Result<Self::Value, E> {
Ok(Text(Cow::Owned(text)))
}
fn visit_bytes<E: Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
match core::str::from_utf8(bytes) {
Ok(text) => self.visit_str(text),
Err(_) => Err(E::invalid_value(Unexpected::Bytes(bytes), &self)),
}
}
fn visit_borrowed_bytes<E: Error>(
self,
bytes: &'de [u8],
) -> Result<Self::Value, E> {
match core::str::from_utf8(bytes) {
Ok(text) => self.visit_borrowed_str(text),
Err(_) => Err(E::invalid_value(Unexpected::Bytes(bytes), &self)),
}
}
fn visit_byte_buf<E: Error>(self, bytes: Vec<u8>) -> Result<Self::Value, E> {
match String::from_utf8(bytes) {
Ok(text) => self.visit_string(text),
Err(error) => {
Err(E::invalid_value(Unexpected::Bytes(error.as_bytes()), &self))
}
}
}
}
deserializer.deserialize_string(TextVisitor)
}
}
pub(crate) fn serialize<S, T>(
data: T,
serializer: S,
with_prefix: bool,
case: CheckCase,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: AsRef<[u8]>,
{
let src = data.as_ref();
let prefix: &[u8] = if with_prefix { b"0x" } else { b"" };
let len = src
.len()
.checked_mul(2)
.and_then(|len| len.checked_add(prefix.len()))
.ok_or_else(|| serde::ser::Error::custom(crate::Error::Overflow))?;
let mut stack;
let mut heap;
let dst = if len <= 132 {
stack = [MaybeUninit::uninit(); 132];
&mut stack[..len]
} else {
heap = Vec::<u8>::with_capacity(len);
&mut heap.spare_capacity_mut()[..len]
};
for (slot, &byte) in dst.iter_mut().zip(prefix) {
slot.write(byte);
}
encode(src, &mut dst[prefix.len()..], case == CheckCase::Upper)
.map_err(serde::ser::Error::custom)?;
serializer.serialize_str(unsafe {
core::str::from_utf8_unchecked(core::slice::from_raw_parts(dst.as_ptr().cast(), len))
})
}
fn payload<E: Error>(text: &str, with_prefix: bool) -> Result<&str, E> {
let text = if with_prefix {
text.strip_prefix("0x")
.ok_or_else(|| E::custom("invalid prefix"))?
} else {
text
};
if !text.len().is_multiple_of(2) {
return Err(E::custom("invalid length"));
}
Ok(text)
}
fn decode<E: Error>(
text: &str,
with_prefix: bool,
case: CheckCase,
max_bytes: usize,
) -> Result<Vec<u8>, E> {
let text = payload::<E>(text, with_prefix)?;
let len = text.len() / 2;
if len > max_bytes {
return Err(E::custom(format_args!(
"expected at most {max_bytes} decoded bytes, got {len}"
)));
}
let mut bytes = vec![0; len];
hex_decode_with_case(text.as_bytes(), &mut bytes, case).map_err(E::custom)?;
Ok(bytes)
}
fn decode_array<E: Error, const N: usize>(
text: &str,
with_prefix: bool,
case: CheckCase,
) -> Result<[u8; N], E> {
let text = payload::<E>(text, with_prefix)?;
hex_decode_array_with_case(text.as_bytes(), case).map_err(E::custom)
}
pub(crate) fn deserialize_array<'de, D, const N: usize>(
deserializer: D,
with_prefix: bool,
case: CheckCase,
) -> Result<[u8; N], D::Error>
where
D: Deserializer<'de>,
{
let text = Text::deserialize(deserializer)?;
decode_array(&text.0, with_prefix, case)
}
pub(crate) fn deserialize_option_array<'de, D, const N: usize>(
deserializer: D,
with_prefix: bool,
case: CheckCase,
) -> Result<Option<[u8; N]>, D::Error>
where
D: Deserializer<'de>,
{
Option::<Text>::deserialize(deserializer)?
.map(|text| decode_array(&text.0, with_prefix, case))
.transpose()
}
pub(crate) fn deserialize<'de, D, T>(
deserializer: D,
with_prefix: bool,
check_case: CheckCase,
max_bytes: usize,
) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: FromIterator<u8>,
{
let text = Text::deserialize(deserializer)?;
decode(&text.0, with_prefix, check_case, max_bytes).map(|bytes| bytes.into_iter().collect())
}
pub(crate) fn serialize_option<S, T>(
data: &Option<T>,
serializer: S,
with_prefix: bool,
case: CheckCase,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: AsRef<[u8]>,
{
struct HexValue<'a> {
data: &'a [u8],
with_prefix: bool,
case: CheckCase,
}
impl Serialize for HexValue<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serialize(self.data, serializer, self.with_prefix, self.case)
}
}
match data {
Some(data) => serializer.serialize_some(&HexValue {
data: data.as_ref(),
with_prefix,
case,
}),
None => serializer.serialize_none(),
}
}
pub(crate) fn deserialize_option<'de, D, T>(
deserializer: D,
with_prefix: bool,
check_case: CheckCase,
max_bytes: usize,
) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: FromIterator<u8>,
{
let bytes = Option::<Text>::deserialize(deserializer)?
.map(|text| decode(&text.0, with_prefix, check_case, max_bytes))
.transpose()?;
Ok(bytes.map(|bytes| bytes.into_iter().collect()))
}
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
T: AsRef<[u8]>,
{
withpfx_ignorecase::serialize(data, serializer)
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: serde::Deserializer<'de>,
T: FromIterator<u8>,
{
withpfx_ignorecase::deserialize(deserializer)
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub fn deserialize_bounded<'de, const MAX: usize, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: serde::Deserializer<'de>,
T: FromIterator<u8>,
{
withpfx_ignorecase::deserialize_bounded::<MAX, D, T>(deserializer)
}
macro_rules! serde_adapters {
($mod_name:ident, $option_name:ident, $with_pfx:expr, $check_case:expr, $prefix:literal, $description:literal) => {
#[doc = $description]
#[doc = concat!(
r###"
Use `#[serde(with = "...")]` for byte collections. Serialization reads one
[`AsRef<[u8]>`] view; deserialization collects into [`FromIterator<u8>`] after
validating the complete input. All formats use strings, including binary formats.
A required prefix is exactly `0x`. Payloads contain only ASCII hex digits;
empty payloads are accepted. For arrays use this module's [`array`] adapter;
for a decoded-byte limit use [`deserialize_bounded`].
# Examples
```
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
struct Record {
#[serde(with = "faster_hex::"###, stringify!($mod_name), r###"")]
bytes: Vec<u8>,
}
let record = Record { bytes: vec![0x12, 0x34] };
let json = serde_json::to_string(&record)?;
assert_eq!(json, r#"{"bytes":""###, $prefix, r###"1234"}"#);
assert_eq!(serde_json::from_str::<Record>(&json)?, record);
# Ok::<(), serde_json::Error>(())
```
"###
)]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub mod $mod_name {
use crate::decode::CheckCase;
use crate::serde::internal;
use core::iter::FromIterator;
pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
T: AsRef<[u8]>,
{
internal::serialize(data, serializer, $with_pfx, $check_case)
}
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: serde::Deserializer<'de>,
T: FromIterator<u8>,
{
internal::deserialize(deserializer, $with_pfx, $check_case, usize::MAX)
}
pub fn deserialize_bounded<'de, const MAX: usize, D, T>(
deserializer: D,
) -> Result<T, D::Error>
where
D: serde::Deserializer<'de>,
T: FromIterator<u8>,
{
internal::deserialize(deserializer, $with_pfx, $check_case, MAX)
}
#[doc = concat!(
r###"
# Examples
```
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
struct Record {
#[serde(with = "faster_hex::"###, stringify!($mod_name), r###"::array")]
id: [u8; 2],
}
let record = Record { id: [0x12, 0x34] };
let json = serde_json::to_string(&record)?;
assert_eq!(serde_json::from_str::<Record>(&json)?, record);
# Ok::<(), serde_json::Error>(())
```
"###
)]
pub mod array {
use super::{internal, CheckCase};
pub use super::serialize;
pub fn deserialize<'de, D, const N: usize>(
deserializer: D,
) -> Result<[u8; N], D::Error>
where
D: serde::Deserializer<'de>,
{
internal::deserialize_array(deserializer, $with_pfx, $check_case)
}
}
}
#[doc = concat!($description, " Optional values.")]
#[doc = concat!(
r###"
Present values follow [`"###, stringify!($mod_name), r###"`](crate::"###,
stringify!($mod_name), r###"). Absent values use Serde's `None`
representation (`null` in JSON). Binary formats retain their normal [`Option`]
tags, so `Some(empty)` remains distinct from `None`.
Use `#[serde(default)]` to accept a missing struct field. For fixed arrays use
[`array`]; for a limit on present values use [`deserialize_bounded`].
# Examples
```
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
struct Record {
#[serde(default, with = "faster_hex::"###, stringify!($option_name), r###"")]
bytes: Option<Vec<u8>>,
}
let record = Record { bytes: Some(vec![0x12, 0x34]) };
let json = serde_json::to_string(&record)?;
assert_eq!(serde_json::from_str::<Record>(&json)?, record);
assert_eq!(serde_json::from_str::<Record>("{}")?.bytes, None);
# Ok::<(), serde_json::Error>(())
```
"###
)]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub mod $option_name {
use crate::decode::CheckCase;
use crate::serde::internal;
use core::iter::FromIterator;
pub fn serialize<S, T>(data: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
T: AsRef<[u8]>,
{
internal::serialize_option(data, serializer, $with_pfx, $check_case)
}
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: FromIterator<u8>,
{
internal::deserialize_option(deserializer, $with_pfx, $check_case, usize::MAX)
}
pub fn deserialize_bounded<'de, const MAX: usize, D, T>(
deserializer: D,
) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: FromIterator<u8>,
{
internal::deserialize_option(deserializer, $with_pfx, $check_case, MAX)
}
pub mod array {
use super::{internal, CheckCase};
pub use super::serialize;
pub fn deserialize<'de, D, const N: usize>(
deserializer: D,
) -> Result<Option<[u8; N]>, D::Error>
where
D: serde::Deserializer<'de>,
{
internal::deserialize_option_array(deserializer, $with_pfx, $check_case)
}
}
}
};
}
serde_adapters!(
withpfx_ignorecase,
option_withpfx_ignorecase,
true,
CheckCase::None,
"0x",
"Lowercase serialization with a 0x prefix; accepts either letter case."
);
serde_adapters!(
nopfx_ignorecase,
option_nopfx_ignorecase,
false,
CheckCase::None,
"",
"Lowercase serialization without a prefix; accepts either letter case."
);
serde_adapters!(
withpfx_lowercase,
option_withpfx_lowercase,
true,
CheckCase::Lower,
"0x",
"Lowercase hex with a required 0x prefix."
);
serde_adapters!(
nopfx_lowercase,
option_nopfx_lowercase,
false,
CheckCase::Lower,
"",
"Lowercase hex without a prefix."
);
serde_adapters!(
withpfx_uppercase,
option_withpfx_uppercase,
true,
CheckCase::Upper,
"0x",
"Uppercase hex with a required 0x prefix."
);
serde_adapters!(
nopfx_uppercase,
option_nopfx_uppercase,
false,
CheckCase::Upper,
"",
"Uppercase hex without a prefix."
);