Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "serde-hex"
name = "stremio-serde-hex"
version = "0.1.0"
authors = ["Forrest Marshall <fourthetrees@gmail.com>"]
description = "Hexadecimal encoding/decoding with serde."
Expand All @@ -9,11 +9,13 @@ keywords = ["serde","hex","hexadecimal"]
license = "MIT/Apache-2.0"
readme = "README.md"

edition = "2021"

[dependencies]
array-init = "0.0.4"
smallvec = "0.6"
serde = "1.0"

[dev-dependencies]
serde_derive = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
270 changes: 217 additions & 53 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
//! `serde-derive`:
//!
//! ```rust
//! #[macro_use]
//! extern crate serde_derive;
//! extern crate serde_hex;
//! use serde_hex::{SerHex,StrictPfx};
//! use stremio_serde_hex::{SerHex,StrictPfx};
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Debug,Serialize,Deserialize)]
//! #[derive(Debug, Serialize, Deserialize)]
//! struct Foo {
//! #[serde(with = "SerHex::<StrictPfx>")]
//! bar: [u8;32]
Expand All @@ -36,10 +34,6 @@
//!
#![warn(missing_docs)]

extern crate array_init;
extern crate serde;
extern crate smallvec;

#[macro_use]
pub mod macros;
pub mod config;
Expand All @@ -49,10 +43,12 @@ pub mod utils;
pub use config::*;
pub use types::{Error, ParseHexError};

use serde::{Deserialize, Deserializer, Serializer};
use serde::__private::PhantomData;
use serde::de::Visitor;
use serde::{Deserializer, Serializer};
use smallvec::SmallVec;
use std::iter::FromIterator;
use std::{error, io};
use std::{error, fmt, io};

/// Trait specifying custom serialization and deserialization logic from a
/// hexadecimal string to some arbitrary type. This trait can be used to apply
Expand Down Expand Up @@ -122,26 +118,66 @@ where
where
D: Deserializer<'de>,
{
use serde::de::Error;
let buff: &[u8] = Deserialize::deserialize(deserializer)?;
let rslt = Self::from_hex_raw(buff).map_err(D::Error::custom)?;
let rslt = deserializer.deserialize_any(HexBytesVisitor::default())?;
Ok(rslt)
}
}

struct HexBytesVisitor<S, C> {
_phantom: PhantomData<(S, C)>,
}

impl<S, C> Default for HexBytesVisitor<S, C> {
fn default() -> Self {
Self {
_phantom: PhantomData,
}
}
}

impl<'de, S, C> Visitor<'de> for HexBytesVisitor<S, C>
where
S: SerHex<C>,
C: HexConf,
{
type Value = S;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a hex string")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
S::from_hex_raw(v).map_err(E::custom)
}

fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
S::from_hex_raw(v).map_err(E::custom)
}

fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
S::from_hex_raw(v).map_err(E::custom)
}
}

/// Variant of `SerHex` for serializing/deserializing `Option` types.
///
/// Any type `T` which implements `SerHex<C>` implements `SerHexOpt<C>`
/// automatically.
///
/// ```rust
/// # #[macro_use]
/// # extern crate serde_derive;
/// # extern crate serde_json;
/// # extern crate serde_hex;
/// # use serde_hex::{SerHexOpt,CompactPfx};
/// # use serde::{Serialize, Deserialize};
/// # use stremio_serde_hex::{SerHexOpt,CompactPfx};
/// #
/// #[derive(Debug,PartialEq,Eq,Serialize,Deserialize)]
/// #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
/// struct MaybeNum {
/// #[serde(with = "SerHexOpt::<CompactPfx>")]
/// num: Option<u64>
Expand Down Expand Up @@ -185,22 +221,91 @@ where
where
D: Deserializer<'de>,
{
use serde::de::Error;
let option: Option<&[u8]> = Deserialize::deserialize(deserializer)?;
if let Some(ref buff) = option {
let rslt = Self::from_hex_raw(buff).map_err(D::Error::custom)?;
Ok(Some(rslt))
} else {
Ok(None)
let option = deserializer.deserialize_any(OptHexBytesVisitor::default())?;

Ok(option)
}
}

impl<S, C> SerHexOpt<C> for S
where
S: Sized + SerHex<C>,
C: HexConf,
{
}

struct OptHexBytesVisitor<S, C> {
_phantom: PhantomData<(S, C)>,
}

impl<T, C> Default for OptHexBytesVisitor<T, C> {
fn default() -> Self {
Self {
_phantom: PhantomData,
}
}
}

impl<T, C> SerHexOpt<C> for T
impl<'de, S, C> Visitor<'de> for OptHexBytesVisitor<S, C>
where
T: Sized + SerHex<C>,
S: SerHexOpt<C>,
C: HexConf,
{
type Value = Option<S>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a hex string")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let s = S::from_hex_raw(v).map_err(E::custom)?;

Ok(Some(s))
}

fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let s = S::from_hex_raw(v).map_err(E::custom)?;

Ok(Some(s))
}

fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let s = S::from_hex_raw(v).map_err(E::custom)?;

Ok(Some(s))
}

fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}

fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}

fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
let result = deserializer.deserialize_bytes(self)?;

Ok(result)
}
}

/// Variant of `SerHex` for serializing/deserializing sequence types as
Expand All @@ -211,13 +316,10 @@ where
/// encoding.
///
/// ```rust
/// # #[macro_use]
/// # extern crate serde_derive;
/// # extern crate serde_json;
/// # extern crate serde_hex;
/// # use serde_hex::{SerHexSeq,StrictPfx};
/// # use serde::{Serialize, Deserialize};
/// # use stremio_serde_hex::{SerHexSeq,StrictPfx};
/// #
/// #[derive(Debug,PartialEq,Eq,Serialize,Deserialize)]
/// #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
/// struct Bytes(#[serde(with = "SerHexSeq::<StrictPfx>")] Vec<u8>);
///
/// # fn main() {
Expand Down Expand Up @@ -266,25 +368,87 @@ where
D: Deserializer<'de>,
T: FromIterator<Self>,
{
use serde::de::Error;
let raw: &[u8] = Deserialize::deserialize(deserializer)?;
let src = if raw.starts_with(b"0x") {
&raw[2..]
} else {
&raw[..]
};
let hexsize = Self::size() * 2;
if src.len() % hexsize == 0 {
let mut buff = Vec::with_capacity(src.len() / hexsize);
for chunk in src.chunks(hexsize) {
let elem =
<Self as SerHex<Strict>>::from_hex_raw(chunk).map_err(D::Error::custom)?;
buff.push(elem);
}
Ok(buff.into_iter().collect())
} else {
Err(D::Error::custom("bad hexadecimal sequence size"))
deserializer.deserialize_bytes(SeqHexBytesVisitor::<Self, C, T>::default())
}
}

struct SeqHexBytesVisitor<S, C, T> {
_phantom: PhantomData<(S, C, T)>,
}

impl<S, C, T> Default for SeqHexBytesVisitor<S, C, T> {
fn default() -> Self {
Self {
_phantom: PhantomData,
}
}
}

impl<'de, S, C, T> Visitor<'de> for SeqHexBytesVisitor<S, C, T>
where
S: SerHexSeq<C>,
C: HexConf,
T: FromIterator<S>,
{
type Value = T;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a hex string")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
seq_from_bytes(v.as_bytes(), S::size())
}

fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
seq_from_bytes(v.as_bytes(), S::size())
}

fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
seq_from_bytes(v, S::size())
}

fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
seq_from_bytes(v, S::size())
}
}

fn seq_from_bytes<S, E, T>(raw: &[u8], size_hint: usize) -> Result<T, E>
where
S: SerHex<Strict>,
E: serde::de::Error,
T: FromIterator<S>,
{
let src = if raw.starts_with(b"0x") {
&raw[2..]
} else {
&raw
};

let hexsize = size_hint * 2;
if src.len() % hexsize == 0 && hexsize != 0 && !src.is_empty() {
// if src.len() % hexsize == 0 {
let mut buff = Vec::with_capacity(src.len() / hexsize);
// if chunk size is 0 then chunks() will panic!
for chunk in src.chunks(hexsize) {
let elem = S::from_hex_raw(chunk).map_err(E::custom)?;
buff.push(elem);
}
Ok(buff.into_iter().collect())
} else {
Err(E::custom("bad hexadecimal sequence size"))
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/macros/hex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ macro_rules! impl_serhex_bytearray {

#[cfg(test)]
mod tests {
use {
use crate::{
Compact, CompactCap, CompactCapPfx, CompactPfx, SerHex, Strict, StrictCap, StrictCapPfx,
StrictPfx,
};
Expand Down
Loading