Skip to content

Commit

Permalink
perf: optimize some stuff (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes authored Aug 15, 2023
1 parent fd6399d commit 23d108e
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 94 deletions.
39 changes: 23 additions & 16 deletions crates/dyn-abi/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use alloy_sol_types::token::{PackedSeqToken, TokenType, WordToken};
// NOTE: do not derive `Hash` for this type. The derived version is not
// compatible with the current `PartialEq` implementation. If manually
// implementing `Hash`, ignore the `template` prop in the `DynSeq` variant
#[derive(Debug, Clone)]
#[derive(Clone, Debug)]
pub enum DynToken<'a> {
/// A single word.
Word(Word),
Expand Down Expand Up @@ -135,10 +135,10 @@ impl<'a> DynToken<'a> {
/// Decodes from a decoder, populating the structure with the decoded data.
#[inline]
pub(crate) fn decode_populate(&mut self, dec: &mut Decoder<'a>) -> Result<()> {
let dynamic = self.is_dynamic();
match self {
Self::Word(w) => *w = WordToken::decode_from(dec)?.0,
Self::FixedSeq(..) => {
let dynamic = self.is_dynamic();
let mut child = if dynamic {
dec.take_indirection()?
} else {
Expand All @@ -154,23 +154,31 @@ impl<'a> DynToken<'a> {
Self::DynSeq { contents, template } => {
let mut child = dec.take_indirection()?;
let size = child.take_u32()? as usize;
if size == 0 {
// should already be empty from `empty_dyn_token`
debug_assert!(contents.is_empty());
return Ok(())
}

// This appears to be an unclarity in the solidity spec. The
// spec specifies that offsets are relative to the beginning of
// `enc(X)`. But known-good test vectors have it relative to the
// word AFTER the array size
let mut child = child.raw_child();

let mut new_tokens: Vec<_> = Vec::with_capacity(size);
// This expect is safe because this is only invoked after
// `empty_dyn_token()` which always sets template
let t = template
.take()
.expect("No template. This is a bug, please report it.");
new_tokens.resize(size, *t);
let t = template.take().expect("no template for dynamic sequence");
let mut new_tokens = if size == 1 {
// re-use the box allocation
unsafe { Vec::from_raw_parts(Box::into_raw(t), 1, 1) }
} else {
vec![*t; size]
};

new_tokens
.iter_mut()
.try_for_each(|t| t.decode_populate(&mut child))?;
for t in &mut new_tokens {
t.decode_populate(&mut child)?;
}

*contents = new_tokens.into();
}
Expand All @@ -184,12 +192,11 @@ impl<'a> DynToken<'a> {
#[inline]
pub(crate) fn decode_sequence_populate(&mut self, dec: &mut Decoder<'a>) -> Result<()> {
match self {
Self::FixedSeq(buf, size) => {
for item in buf.to_mut().iter_mut().take(*size) {
item.decode_populate(dec)?;
}
Ok(())
}
Self::FixedSeq(buf, size) => buf
.to_mut()
.iter_mut()
.take(*size)
.try_for_each(|item| item.decode_populate(dec)),
Self::DynSeq { .. } => self.decode_populate(dec),
_ => Err(Error::custom(
"Called decode_sequence_populate on non-sequence token",
Expand Down
51 changes: 20 additions & 31 deletions crates/json-abi/src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,14 @@ impl JsonAbi {
/// Returns an iterator over all of the items in the ABI.
#[inline]
pub fn items(&self) -> Items<'_> {
self.items_with_len(self.len())
}

// `len` must be `self.len()`
#[inline]
fn items_with_len(&self, len: usize) -> Items<'_> {
Items {
len: self.len(),
len,
constructor: self.constructor.as_ref(),
fallback: self.fallback.as_ref(),
receive: self.receive.as_ref(),
Expand All @@ -78,13 +84,15 @@ impl JsonAbi {
}

/// Creates constructor call builder.
#[inline]
pub const fn constructor(&self) -> Option<&Constructor> {
self.constructor.as_ref()
}

/// Parse the ABI json from a `str`. This is a convenience wrapper around
/// [`serde_json::from_str`].
#[cfg(feature = "serde_json")]
#[inline]
pub fn from_json_str(json: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json)
}
Expand All @@ -104,31 +112,37 @@ impl JsonAbi {
}

/// Gets all the functions with the given name.
#[inline]
pub fn function(&self, name: &str) -> Option<&[Function]> {
self.functions.get(name).map(Vec::as_slice)
}

/// Gets all the events with the given name.
#[inline]
pub fn event(&self, name: &str) -> Option<&[Event]> {
self.events.get(name).map(Vec::as_slice)
}

/// Gets all the errors with the given name.
#[inline]
pub fn error(&self, name: &str) -> Option<&[Error]> {
self.errors.get(name).map(Vec::as_slice)
}

/// Iterates over all the functions of the contract in arbitrary order.
#[inline]
pub fn functions(&self) -> Flatten<Values<'_, String, Vec<Function>>> {
self.functions.values().flatten()
}

/// Iterates over all the events of the contract in arbitrary order.
#[inline]
pub fn events(&self) -> Flatten<Values<'_, String, Vec<Event>>> {
self.events.values().flatten()
}

/// Iterates over all the errors of the contract in arbitrary order.
#[inline]
pub fn errors(&self) -> Flatten<Values<'_, String, Vec<Error>>> {
self.errors.values().flatten()
}
Expand Down Expand Up @@ -260,37 +274,12 @@ impl<'de> Deserialize<'de> for JsonAbi {
}

impl Serialize for JsonAbi {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.len()))?;

if let Some(constructor) = &self.constructor {
seq.serialize_element(constructor)?;
}
if let Some(fallback) = &self.fallback {
seq.serialize_element(fallback)?;
}
if let Some(receive) = &self.receive {
seq.serialize_element(receive)?;
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let len = self.len();
let mut seq = serializer.serialize_seq(Some(len))?;
for item in self.items_with_len(len) {
seq.serialize_element(&item)?;
}

self.functions
.values()
.flatten()
.try_for_each(|f| seq.serialize_element(f))?;

self.events
.values()
.flatten()
.try_for_each(|e| seq.serialize_element(e))?;

self.errors
.values()
.flatten()
.try_for_each(|e| seq.serialize_element(e))?;

seq.end()
}
}
Expand Down
16 changes: 6 additions & 10 deletions crates/primitives/src/bits/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,16 @@ impl<'de, const N: usize> Deserialize<'de> for FixedBytes<N> {
}

fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let len_error =
|i| de::Error::invalid_length(i, &format!("exactly {N} bytes").as_str());
let mut bytes = [0u8; N];

bytes.iter_mut().enumerate().try_for_each(|(i, b)| {
*b = seq.next_element()?.ok_or_else(|| {
de::Error::invalid_length(i, &format!("exactly {} bytes", N).as_str())
})?;
Ok(())
})?;
for (i, byte) in bytes.iter_mut().enumerate() {
*byte = seq.next_element()?.ok_or_else(|| len_error(i))?;
}

if let Ok(Some(_)) = seq.next_element::<u8>() {
return Err(de::Error::invalid_length(
N + 1,
&format!("exactly {} bytes", N).as_str(),
))
return Err(len_error(N + 1))
}

Ok(FixedBytes(bytes))
Expand Down
58 changes: 29 additions & 29 deletions crates/sol-types/src/coder/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::{
token::TokenSeq,
utils::{pad_u32, words_for},
TokenType, Word,
};
use crate::{token::TokenSeq, utils, TokenType, Word};
use alloc::vec::Vec;
use core::mem;
use core::{mem, ptr};

/// An ABI encoder.
///
Expand Down Expand Up @@ -111,34 +107,13 @@ impl Encoder {
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn append_indirection(&mut self) {
self.append_word(pad_u32(self.suffix_offset()));
self.append_word(utils::pad_u32(self.suffix_offset()));
}

/// Append a sequence length.
#[inline]
pub fn append_seq_len(&mut self, len: usize) {
self.append_word(pad_u32(len as u32));
}

/// Append a sequence of bytes, padding to the next word.
#[inline]
fn append_bytes(&mut self, bytes: &[u8]) {
let len = words_for(bytes);
for i in 0..len {
let mut padded = Word::ZERO;

let to_copy = match i == len - 1 {
false => 32,
true => match bytes.len() % 32 {
0 => 32,
x => x,
},
};

let offset = 32 * i;
padded[..to_copy].copy_from_slice(&bytes[offset..offset + to_copy]);
self.append_word(padded);
}
self.append_word(utils::pad_u32(len as u32));
}

/// Append a sequence of bytes as a packed sequence with a length prefix.
Expand All @@ -153,6 +128,31 @@ impl Encoder {
pub fn append_head_tail<'a, T: TokenSeq<'a>>(&mut self, token: &T) {
token.encode_sequence(self);
}

/// Append a sequence of bytes, padding to the next word.
#[inline(always)]
fn append_bytes(&mut self, bytes: &[u8]) {
let n_words = utils::words_for(bytes);
self.buf.reserve(n_words);
unsafe {
// set length before copying
// this is fine because we reserved above and we don't panic below
let len = self.buf.len();
self.buf.set_len(len + n_words);

// copy
let cnt = bytes.len();
let dst = self.buf.as_mut_ptr().add(len).cast::<u8>();
ptr::copy_nonoverlapping(bytes.as_ptr(), dst, cnt);

// set remaining bytes to zero if necessary
let rem = cnt % 32;
if rem != 0 {
let pad = 32 - rem;
ptr::write_bytes(dst.add(cnt), 0, pad);
}
}
}
}

/// ABI-encode a token sequence.
Expand Down
8 changes: 4 additions & 4 deletions crates/sol-types/src/types/data_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ impl SolType for Function {
/// Bytes - `bytes`
pub struct Bytes;

impl<T: AsRef<[u8]>> Encodable<Bytes> for T {
impl<T: ?Sized + AsRef<[u8]>> Encodable<Bytes> for T {
#[inline]
fn to_tokens(&self) -> PackedSeqToken<'_> {
PackedSeqToken(self.as_ref())
Expand Down Expand Up @@ -377,10 +377,10 @@ impl<T: SolType> SolType for Array<T> {
/// String - `string`
pub struct String;

impl<T: AsRef<str>> Encodable<String> for T {
impl<T: ?Sized + AsRef<str>> Encodable<String> for T {
#[inline]
fn to_tokens(&self) -> <String as SolType>::TokenType<'_> {
self.as_ref().as_bytes().into()
fn to_tokens(&self) -> PackedSeqToken<'_> {
PackedSeqToken(self.as_ref().as_bytes())
}
}

Expand Down
8 changes: 4 additions & 4 deletions crates/sol-types/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,26 @@ use crate::{Error, Result, Word};

/// Calculates the padded length of a slice by rounding its length to the next
/// word.
#[inline]
#[inline(always)]
pub const fn words_for(data: &[u8]) -> usize {
words_for_len(data.len())
}

/// Calculates the padded length of a slice of a specific length by rounding its
/// length to the next word.
#[inline]
#[inline(always)]
pub const fn words_for_len(len: usize) -> usize {
(len + 31) / 32
}

/// `padded_len` rounds a slice length up to the next multiple of 32
#[inline]
#[inline(always)]
pub(crate) const fn padded_len(data: &[u8]) -> usize {
next_multiple_of_32(data.len())
}

/// See [`usize::next_multiple_of`].
#[inline]
#[inline(always)]
pub const fn next_multiple_of_32(n: usize) -> usize {
match n % 32 {
0 => n,
Expand Down

0 comments on commit 23d108e

Please sign in to comment.