Skip to content

Commit

Permalink
Serde support w/o impl-serde
Browse files Browse the repository at this point in the history
  • Loading branch information
vorot93 committed Feb 21, 2022
1 parent cd9407d commit 880afb6
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 14 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ categories = ["cryptography::cryptocurrencies", "mathematics", "no-std"]
members = ["bench", "fuzz", "intrinsics"]

[features]
alloc = []
llvm-intrinsics = ["ethnum-intrinsics"]
serde = ["impl-serde", "crate-serde"]
serde = ["crate-serde"]
scale = ["parity-scale-codec"]
std = ["alloc"]

[dependencies]
ethnum-intrinsics = { version = "1", path = "intrinsics", optional = true }
impl-serde = { version = "0.3", default-features = false, optional = true }
rlp = { version = "0.5", optional = true }
crate-serde = { package = "serde", version = "1", optional = true }
parity-scale-codec = { version = "3", features = [
Expand Down
77 changes: 66 additions & 11 deletions src/impls/serde.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::U256;
use alloc::string::String;
use core::{fmt, str::FromStr};
use crate_serde::{
de::{Deserialize, Deserializer},
de::{self, Deserialize, Deserializer},
ser::{Serialize, Serializer},
};

Expand All @@ -9,9 +11,43 @@ impl Serialize for U256 {
where
S: Serializer,
{
static CHARS: &[u8] = b"0123456789abcdef";

fn to_hex_raw<'a>(v: &'a mut [u8], bytes: &[u8], skip_leading_zero: bool) -> &'a str {
assert!(v.len() > 1 + bytes.len() * 2);

v[0] = b'0';
v[1] = b'x';

let mut idx = 2;
let first_nibble = bytes[0] >> 4;
if first_nibble != 0 || !skip_leading_zero {
v[idx] = CHARS[first_nibble as usize];
idx += 1;
}
v[idx] = CHARS[(bytes[0] & 0xf) as usize];
idx += 1;

for &byte in bytes.iter().skip(1) {
v[idx] = CHARS[(byte >> 4) as usize];
v[idx + 1] = CHARS[(byte & 0xf) as usize];
idx += 2;
}

// SAFETY: all characters come either from CHARS or "0x", therefore valid UTF8
unsafe { core::str::from_utf8_unchecked(&v[0..idx]) }
}

let mut slice = [0u8; 66];
let bytes = self.to_be_bytes();
impl_serde::serialize::serialize_uint(&mut slice, &bytes, serializer)

let non_zero = bytes.iter().take_while(|b| **b == 0).count();
let bytes = &bytes[non_zero..];
if bytes.is_empty() {
serializer.serialize_str("0x0")
} else {
serializer.serialize_str(to_hex_raw(&mut slice, bytes, true))
}
}
}

Expand All @@ -20,15 +56,30 @@ impl<'de> Deserialize<'de> for U256 {
where
D: Deserializer<'de>,
{
let mut bytes = [0u8; 32];
let wrote = impl_serde::serialize::deserialize_check_len(
deserializer,
impl_serde::serialize::ExpectedLen::Between(0, &mut bytes),
)?;
let mut padded = [0u8; 32];
padded[32 - wrote..].copy_from_slice(&bytes[..wrote]);

Ok(U256::from_be_bytes(padded))
struct Visitor;

impl<'a> de::Visitor<'a> for Visitor {
type Value = U256;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a (both 0x-prefixed or not) hex string",)
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(if let Some(v) = v.strip_prefix("0x") {
U256::from_str_radix(v, 16).map_err(de::Error::custom)?
} else {
U256::from_str(v).map_err(de::Error::custom)?
})
}

#[cfg(feature = "alloc")]
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
self.visit_str(&v)
}
}

deserializer.deserialize_str(Visitor)
}
}

Expand All @@ -46,5 +97,9 @@ mod tests {
serde_json::from_str::<U256>("\"0x12345\"").unwrap(),
U256::new(0x12345),
);
assert_eq!(
serde_json::to_value(&U256::new(0x12345)).unwrap(),
"0x12345",
);
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#![deny(missing_docs)]
#![no_std]

#[cfg(test)]
#[cfg(any(test, feature = "alloc"))]
extern crate alloc;

#[macro_use]
Expand Down

0 comments on commit 880afb6

Please sign in to comment.