Skip to content

Commit

Permalink
feat(abi): add bytes specialization for [u8]-like types
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Oct 23, 2024
1 parent 8ca119b commit d6a45e7
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 29 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ sha2 = "0.10"
smallvec = { version = "1.9", features = ["union"] }
thiserror = "1.0"
tl-proto = { version = "0.4", optional = true }
typeid = { version = "1.0", optional = true }

everscale-types-proc = { version = "=0.1.4", path = "proc" }

Expand Down Expand Up @@ -85,6 +86,7 @@ abi = [
"dep:num-bigint",
"dep:num-traits",
"dep:serde",
"dep:typeid",
"models",
]
venom = []
Expand Down
159 changes: 130 additions & 29 deletions src/abi/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ pub trait IgnoreName {
}

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

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
Expand All @@ -41,7 +44,10 @@ impl<T> IgnoreName for Vec<T>
where
[T]: IgnoreName,
{
type Unnamed<'a> = <[T] as IgnoreName>::Unnamed<'a> where Self: 'a;
type Unnamed<'a>
= <[T] as IgnoreName>::Unnamed<'a>
where
Self: 'a;

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
Expand All @@ -50,7 +56,10 @@ where
}

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

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
Expand All @@ -59,7 +68,10 @@ impl<T: IgnoreName> IgnoreName for Box<T> {
}

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

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
Expand All @@ -68,7 +80,10 @@ impl<T: IgnoreName> IgnoreName for Arc<T> {
}

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

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
Expand All @@ -77,7 +92,10 @@ impl<T: IgnoreName> IgnoreName for Rc<T> {
}

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

#[inline]
fn ignore_name(&self) -> Self::Unnamed<'_> {
Expand Down Expand Up @@ -248,19 +266,31 @@ impl<T: WithAbiType> WithAbiType for Option<T> {

impl<T: WithAbiType> WithAbiType for Vec<T> {
fn abi_type() -> AbiType {
AbiType::Array(Arc::new(T::abi_type()))
if typeid::of::<T>() == typeid::of::<u8>() {
AbiType::Bytes
} else {
AbiType::Array(Arc::new(T::abi_type()))
}
}
}

impl<T: WithAbiType> WithAbiType for [T] {
fn abi_type() -> AbiType {
AbiType::Array(Arc::new(T::abi_type()))
if typeid::of::<T>() == typeid::of::<u8>() {
AbiType::Bytes
} else {
AbiType::Array(Arc::new(T::abi_type()))
}
}
}

impl<T: WithAbiType, const N: usize> WithAbiType for [T; N] {
fn abi_type() -> AbiType {
AbiType::FixedArray(Arc::new(T::abi_type()), N)
if typeid::of::<T>() == typeid::of::<u8>() {
AbiType::FixedBytes(N)
} else {
AbiType::FixedArray(Arc::new(T::abi_type()), N)
}
}
}

Expand Down Expand Up @@ -632,10 +662,16 @@ impl IntoAbi for str {

impl<T: WithAbiType + IntoAbi> IntoAbi for [T] {
fn as_abi(&self) -> AbiValue {
AbiValue::Array(
Arc::new(T::abi_type()),
self.iter().map(T::as_abi).collect(),
)
if typeid::of::<T>() == typeid::of::<u8>() {
// SAFETY: T is definitely u8
let bytes = unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len()) };
AbiValue::Bytes(Bytes::copy_from_slice(bytes))
} else {
AbiValue::Array(
Arc::new(T::abi_type()),
self.iter().map(T::as_abi).collect(),
)
}
}

#[inline]
Expand All @@ -647,22 +683,56 @@ impl<T: WithAbiType + IntoAbi> IntoAbi for [T] {
}
}

impl<T: WithAbiType + IntoAbi, const N: usize> IntoAbi for [T; N] {
fn as_abi(&self) -> AbiValue {
if typeid::of::<T>() == typeid::of::<u8>() {
// SAFETY: T is definitely u8
let bytes = unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len()) };
AbiValue::FixedBytes(Bytes::copy_from_slice(bytes))
} else {
AbiValue::FixedArray(
Arc::new(T::abi_type()),
self.iter().map(T::as_abi).collect(),
)
}
}

fn into_abi(self) -> AbiValue
where
Self: Sized,
{
if typeid::of::<T>() == typeid::of::<u8>() {
// SAFETY: T is definitely u8
let bytes = unsafe { std::slice::from_raw_parts(self.as_ptr().cast(), self.len()) };
AbiValue::FixedBytes(Bytes::copy_from_slice(bytes))
} else {
AbiValue::FixedArray(
Arc::new(T::abi_type()),
self.into_iter().map(T::into_abi).collect(),
)
}
}
}

impl<T: WithAbiType + IntoAbi> IntoAbi for Vec<T> {
#[inline]
fn as_abi(&self) -> AbiValue {
AbiValue::Array(
Arc::new(T::abi_type()),
self.iter().map(T::as_abi).collect(),
)
<[T]>::as_abi(self.as_slice())
}

fn into_abi(self) -> AbiValue
where
Self: Sized,
{
AbiValue::Array(
Arc::new(T::abi_type()),
self.into_iter().map(T::into_abi).collect(),
)
if typeid::of::<T>() == typeid::of::<u8>() {
// SAFETY: `T` is the same type as `u8`.
AbiValue::Bytes(Bytes::from(unsafe { cast_vec::<T, u8>(self) }))
} else {
AbiValue::Array(
Arc::new(T::abi_type()),
self.into_iter().map(T::into_abi).collect(),
)
}
}
}

Expand Down Expand Up @@ -1019,15 +1089,26 @@ impl FromPlainAbi for VarAddr {

impl<T: FromAbi> FromAbi for Vec<T> {
fn from_abi(value: AbiValue) -> Result<Self> {
let items = match value {
AbiValue::Array(_, items) | AbiValue::FixedArray(_, items) => items,
value => return Err(expected_type("array", &value)),
};
let mut result = Vec::with_capacity(items.len());
for item in items {
result.push(ok!(T::from_abi(item)));
if typeid::of::<T>() == typeid::of::<u8>() {
match value {
AbiValue::Bytes(bytes) | AbiValue::FixedBytes(bytes) => {
let bytes = Vec::<u8>::from(bytes);
// SAFETY: `T` is the same type as `u8`.
Ok(unsafe { cast_vec::<u8, T>(bytes) })
}
value => Err(expected_type("bytes or fixedbytes", &value)),
}
} else {
let items = match value {
AbiValue::Array(_, items) | AbiValue::FixedArray(_, items) => items,
value => return Err(expected_type("array", &value)),
};
let mut result = Vec::with_capacity(items.len());
for item in items {
result.push(ok!(T::from_abi(item)));
}
Ok(result)
}
Ok(result)
}
}

Expand Down Expand Up @@ -1159,6 +1240,26 @@ where
}
}

/// # Safety
///
/// The following must be true:
/// - `T1` must have the same memory layout as `T2`.
unsafe fn cast_vec<T1, T2>(v: Vec<T1>) -> Vec<T2> {
// The code is the same as in the offical example:
// https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.from_raw_parts

// Prevent running `self`'s destructor so we are in complete control
// of the allocation.
let mut v = std::mem::ManuallyDrop::new(v);

// Pull out the various important pieces of information about `v`
let p = v.as_mut_ptr().cast::<T2>();
let len = v.len();
let cap = v.capacity();

Vec::<T2>::from_raw_parts(p, len, cap)
}

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

0 comments on commit d6a45e7

Please sign in to comment.