Skip to content

Commit

Permalink
Add WithoutName wrapper for abi
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Oct 18, 2023
1 parent 324b085 commit b92d205
Show file tree
Hide file tree
Showing 4 changed files with 400 additions and 3 deletions.
88 changes: 87 additions & 1 deletion src/abi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Common ABI implementation.

use std::hash::{BuildHasher, Hash};
use std::str::FromStr;

pub use self::contract::{
Expand All @@ -8,7 +9,7 @@ pub use self::contract::{
};
pub use self::signature::{extend_signature_with_id, sign_with_signature_id};
pub use self::traits::{
FromAbi, FromPlainAbi, IntoAbi, IntoPlainAbi, WithAbiType, WithPlainAbiType,
FromAbi, FromPlainAbi, IgnoreName, IntoAbi, IntoPlainAbi, WithAbiType, WithPlainAbiType,
};
pub use self::ty::{AbiHeaderType, AbiType, NamedAbiType, PlainAbiType};
pub use self::value::{AbiHeader, AbiValue, NamedAbiValue, PlainAbiValue};
Expand Down Expand Up @@ -75,3 +76,88 @@ impl std::fmt::Display for AbiVersion {
write!(f, "{}.{}", self.major, self.minor)
}
}

/// A wrapper around [`AbiType`], [`NamedAbiType`], [`AbiValue`] and [`NamedAbiValue`]
/// that implements hash/comparison traits without name.
#[repr(transparent)]
pub struct WithoutName<T>(pub T);

impl<T> WithoutName<T> {
/// Wraps a reference of the inner type.
pub fn wrap(value: &T) -> &Self {
// SAFETY: HashWithoutName<T> is #[repr(transparent)]
unsafe { &*(value as *const T as *const Self) }
}

/// Wraps a slice of the inner type.
pub fn wrap_slice(value: &[T]) -> &[Self] {
// SAFETY: HashWithoutName<T> is #[repr(transparent)]
unsafe { &*(value as *const [T] as *const [Self]) }
}
}

impl<T: std::fmt::Debug> std::fmt::Debug for WithoutName<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("WithoutName").field(&self.0).finish()
}
}

impl<T: Clone> Clone for WithoutName<T> {
#[inline]
fn clone(&self) -> Self {
WithoutName(self.0.clone())
}
}

impl<T> Eq for WithoutName<T> where WithoutName<T>: PartialEq {}

impl<T> PartialOrd for WithoutName<T>
where
WithoutName<T>: Ord,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl<T> PartialEq for WithoutName<Vec<T>>
where
WithoutName<T>: PartialEq,
{
fn eq(&self, WithoutName(other): &Self) -> bool {
WithoutName::wrap_slice(self.0.as_slice()) == WithoutName::wrap_slice(other.as_slice())
}
}

impl<K, V> PartialEq for WithoutName<std::collections::BTreeMap<K, V>>
where
K: PartialEq,
WithoutName<V>: PartialEq,
{
fn eq(&self, WithoutName(other): &Self) -> bool {
self.0.len() == other.len()
&& self.0.iter().zip(other).all(|((ak, av), (bk, bv))| {
(ak, WithoutName::wrap(av)) == (bk, WithoutName::wrap(bv))
})
}
}

impl<K, V, S> PartialEq for WithoutName<std::collections::HashMap<K, V, S>>
where
K: Eq + Hash,
WithoutName<V>: PartialEq,
S: BuildHasher,
{
fn eq(&self, WithoutName(other): &Self) -> bool {
if self.0.len() != other.len() {
return false;
}

self.0.iter().all(|(key, value)| {
other
.get(key)
.map_or(false, |v| WithoutName::wrap(value) == WithoutName::wrap(v))
})
}
}
169 changes: 168 additions & 1 deletion src/abi/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,154 @@ use bytes::Bytes;
use num_bigint::{BigInt, BigUint};
use num_traits::ToPrimitive;

use super::{AbiType, AbiValue, NamedAbiType, NamedAbiValue, PlainAbiType, PlainAbiValue};
use super::{
AbiType, AbiValue, NamedAbiType, NamedAbiValue, PlainAbiType, PlainAbiValue, WithoutName,
};
use crate::cell::{Cell, HashBytes};
use crate::num::*;

use crate::models::message::{IntAddr, StdAddr, VarAddr};

/// ABI entity wrapper.
pub trait IgnoreName {
/// Wrapped ABI entity.
type Unnamed<'a>
where
Self: 'a;

/// Wraps an ABI entity into [`WithoutName`].
fn ignore_name(&self) -> Self::Unnamed<'_>;
}

impl<T: IgnoreName> IgnoreName for &'_ T {
type Unnamed<'a> = T::Unnamed<'a> where Self: 'a;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
T::ignore_name(self)
}
}

impl<T> IgnoreName for Vec<T>
where
[T]: IgnoreName,
{
type Unnamed<'a> = <[T] as IgnoreName>::Unnamed<'a> where Self: 'a;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
<[T] as IgnoreName>::ignore_name(self.as_slice())
}
}

impl<T: IgnoreName> IgnoreName for Box<T> {
type Unnamed<'a> = T::Unnamed<'a> where Self: 'a;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
T::ignore_name(self.as_ref())
}
}

impl<T: IgnoreName> IgnoreName for Arc<T> {
type Unnamed<'a> = T::Unnamed<'a> where Self: 'a;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
T::ignore_name(self.as_ref())
}
}

impl<T: IgnoreName> IgnoreName for Rc<T> {
type Unnamed<'a> = T::Unnamed<'a> where Self: 'a;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
T::ignore_name(self.as_ref())
}
}

impl<T: IgnoreName> IgnoreName for Option<T> {
type Unnamed<'a> = Option<T::Unnamed<'a>> where Self: 'a;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
self.as_ref().map(|t| T::ignore_name(t))
}
}

impl IgnoreName for AbiType {
type Unnamed<'a> = &'a WithoutName<AbiType>;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
WithoutName::wrap(self)
}
}

impl IgnoreName for [AbiType] {
type Unnamed<'a> = &'a [WithoutName<AbiType>];

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
WithoutName::wrap_slice(self)
}
}

impl IgnoreName for NamedAbiType {
type Unnamed<'a> = &'a WithoutName<NamedAbiType>;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
WithoutName::wrap(self)
}
}

impl IgnoreName for [NamedAbiType] {
type Unnamed<'a> = &'a [WithoutName<NamedAbiType>];

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
WithoutName::wrap_slice(self)
}
}

impl IgnoreName for AbiValue {
type Unnamed<'a> = &'a WithoutName<AbiValue>;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
WithoutName::wrap(self)
}
}

impl IgnoreName for [AbiValue] {
type Unnamed<'a> = &'a [WithoutName<AbiValue>];

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
WithoutName::wrap_slice(self)
}
}

impl IgnoreName for NamedAbiValue {
type Unnamed<'a> = &'a WithoutName<NamedAbiValue>;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
WithoutName::wrap(self)
}
}

impl IgnoreName for [NamedAbiValue] {
type Unnamed<'a> = &'a [WithoutName<NamedAbiValue>];

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
WithoutName::wrap_slice(self)
}
}

/// A type with a known ABI type.
pub trait WithAbiType {
/// Returns a corresponding ABI type.
Expand Down Expand Up @@ -991,6 +1133,8 @@ impl<T: FromAbi> FromAbi for Rc<T> {

#[cfg(test)]
mod tests {
use ahash::HashSet;

use crate::prelude::CellFamily;

use super::*;
Expand All @@ -1011,4 +1155,27 @@ mod tests {

assert_eq!(abi.into_abi(), target_abi);
}

#[test]
fn entities_without_name() {
let only_signatures = HashSet::from_iter(
[
u32::abi_type().named("u32"),
bool::abi_type().named("bool"),
<(u32, bool)>::abi_type().named("(u32,bool)"),
]
.map(WithoutName),
);

assert!(only_signatures.contains(u32::abi_type().named("qwe").ignore_name()));
assert!(only_signatures.contains(u32::abi_type().ignore_name()));

assert!(only_signatures.contains(bool::abi_type().named("asd").ignore_name()));
assert!(only_signatures.contains(bool::abi_type().ignore_name()));

assert!(only_signatures.contains(<(u32, bool)>::abi_type().named("zxc").ignore_name()));
assert!(only_signatures.contains(<(u32, bool)>::abi_type().ignore_name()));

assert!(!only_signatures.contains(u64::abi_type().ignore_name()));
}
}
Loading

0 comments on commit b92d205

Please sign in to comment.