From 9a9fd5c936c174e13164895f85f06b4081db6fb3 Mon Sep 17 00:00:00 2001 From: Artem Vorotnikov Date: Mon, 21 Feb 2022 23:57:18 +0300 Subject: [PATCH] Serde support w/o impl-serde --- Cargo.toml | 5 +-- src/impls/serde.rs | 77 +++++++++++++++++++++++++++++++++++++++------- src/lib.rs | 2 +- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b6a5c06..1f6aa41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 = [ diff --git a/src/impls/serde.rs b/src/impls/serde.rs index 0431858..1c6284a 100644 --- a/src/impls/serde.rs +++ b/src/impls/serde.rs @@ -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}, }; @@ -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]) -> &'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 { + 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)) + } } } @@ -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(self, v: &str) -> Result { + 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(self, v: String) -> Result { + self.visit_str(&v) + } + } + + deserializer.deserialize_str(Visitor) } } @@ -46,5 +97,9 @@ mod tests { serde_json::from_str::("\"0x12345\"").unwrap(), U256::new(0x12345), ); + assert_eq!( + serde_json::from_str::("\"12345\"").unwrap(), + U256::new(12345), + ); } } diff --git a/src/lib.rs b/src/lib.rs index ba6c9fd..5c37b93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ #![deny(missing_docs)] #![no_std] -#[cfg(test)] +#[cfg(any(test, feature = "alloc"))] extern crate alloc; #[macro_use]