diff --git a/Cargo.lock b/Cargo.lock index dd037654f..ebf2c8822 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1152,6 +1152,12 @@ dependencies = [ "pest", ] +[[package]] +name = "seq-macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99" + [[package]] name = "serde" version = "1.0.126" @@ -1307,6 +1313,7 @@ dependencies = [ name = "svm-abi-decoder" version = "0.0.0" dependencies = [ + "seq-macro", "svm-abi-layout", "svm-sdk-std", "svm-sdk-types", @@ -1316,6 +1323,8 @@ dependencies = [ name = "svm-abi-encoder" version = "0.0.0" dependencies = [ + "num-traits", + "seq-macro", "svm-abi-layout", "svm-sdk-std", "svm-sdk-types", diff --git a/crates/abi/decoder/Cargo.toml b/crates/abi/decoder/Cargo.toml index d7d4c7507..6d48d5502 100644 --- a/crates/abi/decoder/Cargo.toml +++ b/crates/abi/decoder/Cargo.toml @@ -11,18 +11,13 @@ description = "Spacemesh Virtual Machine" publish = false [dependencies] -svm-abi-layout = { path = "./../layout" } -svm-sdk-types = { path = "../../sdk/types", default-features = false } -svm-sdk-std = { path = "../../sdk/std", default-features = false } +seq-macro = "0.2.2" +svm-abi-layout = { path="./../layout" } +svm-sdk-types = { path="../../sdk/types", default-features=false } +svm-sdk-std = { path="../../sdk/std", default-features=false } [features] default = [] debug = ["svm-sdk-types/debug"] -static-alloc = [ - "svm-sdk-types/static-alloc", - "svm-sdk-std/static-alloc" -] -dynamic-alloc = [ - "svm-sdk-types/dynamic-alloc", - "svm-sdk-std/dynamic-alloc" -] +static-alloc = ["svm-sdk-types/static-alloc", "svm-sdk-std/static-alloc"] +dynamic-alloc = ["svm-sdk-types/dynamic-alloc", "svm-sdk-std/dynamic-alloc"] diff --git a/crates/abi/decoder/src/decoder.rs b/crates/abi/decoder/src/decoder.rs index 61c36bf10..3c96ed9e4 100644 --- a/crates/abi/decoder/src/decoder.rs +++ b/crates/abi/decoder/src/decoder.rs @@ -264,75 +264,30 @@ impl Decoder { fn decode_array(&self, cursor: &mut Cursor) -> Result { assert_no_eof!(cursor); - macro_rules! impl_decode { - (0 $cursor:ident $values:ident) => {{ - // - }}; - (1 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(0 $cursor $values); - }}; - (2 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(1 $cursor $values); - }}; - (3 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(2 $cursor $values); - }}; - (4 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(3 $cursor $values); - }}; - (5 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(4 $cursor $values); - }}; - (6 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(5 $cursor $values); - }}; - (7 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(6 $cursor $values); - }}; - (8 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(7 $cursor $values); - }}; - (9 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(8 $cursor $values); - }}; - (10 $cursor:ident $values:ident) => {{ - impl_decode!(@ $cursor $values); - impl_decode!(9 $cursor $values); - }}; - (@ $cursor:ident $values:ident) => {{ - let value = safe_try!(self.decode_primitive($cursor)); - - $values.push(value); - }} - } - let len = safe_try!(self.read_byte(cursor)); let mut values: Vec = Vec::with_capacity(len as usize); - match len { - layout::ARR_0 => impl_decode!(0 cursor values), - layout::ARR_1 => impl_decode!(1 cursor values), - layout::ARR_2 => impl_decode!(2 cursor values), - layout::ARR_3 => impl_decode!(3 cursor values), - layout::ARR_4 => impl_decode!(4 cursor values), - layout::ARR_5 => impl_decode!(5 cursor values), - layout::ARR_6 => impl_decode!(6 cursor values), - layout::ARR_7 => impl_decode!(7 cursor values), - layout::ARR_8 => impl_decode!(8 cursor values), - layout::ARR_9 => impl_decode!(9 cursor values), - layout::ARR_10 => impl_decode!(10 cursor values), + let len = match len { + layout::ARR_0 => 0, + layout::ARR_1 => 1, + layout::ARR_2 => 2, + layout::ARR_3 => 3, + layout::ARR_4 => 4, + layout::ARR_5 => 5, + layout::ARR_6 => 6, + layout::ARR_7 => 7, + layout::ARR_8 => 8, + layout::ARR_9 => 9, + layout::ARR_10 => 10, _ => svm_sdk_std::panic(), }; + seq_macro::seq!(n in 0..11 { + if len > n { + let value = safe_try!(self.decode_primitive(cursor)); + values.push(value); + } + }); let values: Value = values.into(); @@ -350,180 +305,18 @@ impl Decoder { fn read_num(&self, cursor: &mut Cursor, nbytes: usize) -> Result { debug_assert!(nbytes > 0 && nbytes <= 8); - macro_rules! from_be_bytes_1 { - ($ptr:expr) => {{ - let mut n: u64 = 0; - let ptr = $ptr as *const u8; - - let d0 = unsafe { *ptr.offset(0) }; - n = d0 as u64; - - n - }}; - } - - macro_rules! from_be_bytes_2 { - ($ptr:expr) => {{ - let mut n: u64 = 0; - let ptr = $ptr as *const u8; - - let d0 = unsafe { *ptr.offset(0) }; - let d1 = unsafe { *ptr.offset(1) }; - - n = (n << 8) + d0 as u64; - n = (n << 8) + d1 as u64; - - n - }}; - } - - macro_rules! from_be_bytes_3 { - ($ptr:expr) => {{ - let mut n: u64 = 0; - let ptr = $ptr as *const u8; - - let d0 = unsafe { *ptr.offset(0) }; - let d1 = unsafe { *ptr.offset(1) }; - let d2 = unsafe { *ptr.offset(2) }; - - n = (n << 8) + d0 as u64; - n = (n << 8) + d1 as u64; - n = (n << 8) + d2 as u64; - - n - }}; - } - - macro_rules! from_be_bytes_4 { - ($ptr:expr) => {{ - let mut n: u64 = 0; - let ptr = $ptr as *const u8; - - let d0 = unsafe { *ptr.offset(0) }; - let d1 = unsafe { *ptr.offset(1) }; - let d2 = unsafe { *ptr.offset(2) }; - let d3 = unsafe { *ptr.offset(3) }; - - n = (n << 8) + d0 as u64; - n = (n << 8) + d1 as u64; - n = (n << 8) + d2 as u64; - n = (n << 8) + d3 as u64; - - n - }}; - } - - macro_rules! from_be_bytes_5 { - ($ptr:expr) => {{ - let mut n: u64 = 0; - let ptr = $ptr as *const u8; - - let d0 = unsafe { *ptr.offset(0) }; - let d1 = unsafe { *ptr.offset(1) }; - let d2 = unsafe { *ptr.offset(2) }; - let d3 = unsafe { *ptr.offset(3) }; - let d4 = unsafe { *ptr.offset(4) }; - - n = (n << 8) + d0 as u64; - n = (n << 8) + d1 as u64; - n = (n << 8) + d2 as u64; - n = (n << 8) + d3 as u64; - n = (n << 8) + d4 as u64; - - n - }}; - } - - macro_rules! from_be_bytes_6 { - ($ptr:expr) => {{ - let mut n: u64 = 0; - let ptr = $ptr as *const u8; - - let d0 = unsafe { *ptr.offset(0) }; - let d1 = unsafe { *ptr.offset(1) }; - let d2 = unsafe { *ptr.offset(2) }; - let d3 = unsafe { *ptr.offset(3) }; - let d4 = unsafe { *ptr.offset(4) }; - let d5 = unsafe { *ptr.offset(5) }; - - n = (n << 8) + d0 as u64; - n = (n << 8) + d1 as u64; - n = (n << 8) + d2 as u64; - n = (n << 8) + d3 as u64; - n = (n << 8) + d4 as u64; - n = (n << 8) + d5 as u64; - - n - }}; - } - - macro_rules! from_be_bytes_7 { - ($ptr:expr) => {{ - let mut n: u64 = 0; - let ptr = $ptr as *const u8; - - let d0 = unsafe { *ptr.offset(0) }; - let d1 = unsafe { *ptr.offset(1) }; - let d2 = unsafe { *ptr.offset(2) }; - let d3 = unsafe { *ptr.offset(3) }; - let d4 = unsafe { *ptr.offset(4) }; - let d5 = unsafe { *ptr.offset(5) }; - let d6 = unsafe { *ptr.offset(6) }; - - n = (n << 8) + d0 as u64; - n = (n << 8) + d1 as u64; - n = (n << 8) + d2 as u64; - n = (n << 8) + d3 as u64; - n = (n << 8) + d4 as u64; - n = (n << 8) + d5 as u64; - n = (n << 8) + d6 as u64; - - n - }}; - } - - macro_rules! from_be_bytes_8 { - ($ptr:expr) => {{ - let mut n: u64 = 0; - let ptr = $ptr as *const u8; - - let d0 = unsafe { *ptr.offset(0) }; - let d1 = unsafe { *ptr.offset(1) }; - let d2 = unsafe { *ptr.offset(2) }; - let d3 = unsafe { *ptr.offset(3) }; - let d4 = unsafe { *ptr.offset(4) }; - let d5 = unsafe { *ptr.offset(5) }; - let d6 = unsafe { *ptr.offset(6) }; - let d7 = unsafe { *ptr.offset(7) }; - - n = (n << 8) + d0 as u64; - n = (n << 8) + d1 as u64; - n = (n << 8) + d2 as u64; - n = (n << 8) + d3 as u64; - n = (n << 8) + d4 as u64; - n = (n << 8) + d5 as u64; - n = (n << 8) + d6 as u64; - n = (n << 8) + d7 as u64; - - n - }}; - } - - let ptr = safe_try!(self.read_bytes(cursor, nbytes)); - - let num = match nbytes { - 1 => from_be_bytes_1!(ptr), - 2 => from_be_bytes_2!(ptr), - 3 => from_be_bytes_3!(ptr), - 4 => from_be_bytes_4!(ptr), - 5 => from_be_bytes_5!(ptr), - 6 => from_be_bytes_6!(ptr), - 7 => from_be_bytes_7!(ptr), - 8 => from_be_bytes_8!(ptr), - _ => svm_sdk_std::panic(), + let slice = unsafe { + core::slice::from_raw_parts(safe_try!(self.read_bytes(cursor, nbytes)), nbytes) }; + let mut data = [0u8; 8]; - Result::Ok(num) + seq_macro::seq!(i in 0..8 { + if nbytes > i { + data[7 - i] = slice[nbytes - i - 1]; + } + }); + + Result::Ok(u64::from_be_bytes(data)) } #[inline] diff --git a/crates/abi/encoder/Cargo.toml b/crates/abi/encoder/Cargo.toml index d8a07dbb9..2d7b694de 100644 --- a/crates/abi/encoder/Cargo.toml +++ b/crates/abi/encoder/Cargo.toml @@ -11,17 +11,13 @@ description = "Spacemesh Virtual Machine" publish = false [dependencies] -svm-abi-layout = { path = "./../layout" } -svm-sdk-types = { path = "../../sdk/types", default-features = false } -svm-sdk-std = { path = "../../sdk/std", default-features = false } +num-traits = { version="0.2.14", default-features=false } +seq-macro = "0.2.2" +svm-abi-layout = { path="./../layout" } +svm-sdk-types = { path="../../sdk/types", default-features=false } +svm-sdk-std = { path="../../sdk/std", default-features=false } [features] default = [] -static-alloc = [ - "svm-sdk-types/static-alloc", - "svm-sdk-std/static-alloc" -] -dynamic-alloc = [ - "svm-sdk-types/dynamic-alloc", - "svm-sdk-std/dynamic-alloc" -] +static-alloc = ["svm-sdk-types/static-alloc", "svm-sdk-std/static-alloc"] +dynamic-alloc = ["svm-sdk-types/dynamic-alloc", "svm-sdk-std/dynamic-alloc"] diff --git a/crates/abi/encoder/src/lib.rs b/crates/abi/encoder/src/lib.rs index 839ad7ca4..9082e76d5 100644 --- a/crates/abi/encoder/src/lib.rs +++ b/crates/abi/encoder/src/lib.rs @@ -1,10 +1,11 @@ //! This crate is responsible of encoding SVM types (its actual type and their values to be precise), //! according to a simple ABI format. -#![allow(missing_docs)] -#![allow(unused)] -#![allow(dead_code)] -#![allow(unreachable_code)] +#![no_std] +#![deny(missing_docs)] +#![deny(unused)] +#![deny(dead_code)] +#![deny(unreachable_code)] mod traits; mod types; diff --git a/crates/abi/encoder/src/traits.rs b/crates/abi/encoder/src/traits.rs index 6285439e4..e0ed2c8cc 100644 --- a/crates/abi/encoder/src/traits.rs +++ b/crates/abi/encoder/src/traits.rs @@ -1,41 +1,54 @@ -/// A trait used to encoding a value (of `Primitive` or `Composite` type) +use num_traits::AsPrimitive; +/// A trait used to encoding a value (of `Primitive` or `Composite` type) pub trait Encoder { /// Encodes `self` and outputs the data into `w` fn encode(&self, w: &mut W); } -impl Encoder for &T -where - T: Encoder, -{ - fn encode(&self, w: &mut W) { - (**self).encode(w); - } +pub trait Push { + type Item; + + fn push(&mut self, item: Self::Item); } -impl Encoder for &mut T -where - T: Encoder, -{ - fn encode(&self, w: &mut W) { - (**self).encode(w); +impl Push for svm_sdk_std::Vec { + type Item = T; + + fn push(&mut self, item: Self::Item) { + svm_sdk_std::Vec::push(self, item); } } -// This trait has been added to let to-be-encoded values -// to expose how much bytes they will consume. +/// This trait has been added to let to-be-encoded values to expose how much +/// bytes they will consume. // -// A exact byte-size may be dependant on the value to be encoded (a.k.a variable-length encoding). -// Moreover, each Type implementing this trait should have a maximum byte-size that will suffice for encoding any value required. +/// A exact byte-size may be dependant on the value to be encoded (a.k.a +/// variable-length encoding). +/// Moreover, each Type implementing this trait should have a maximum byte-size +/// that will suffice for encoding any value required. // -// This trait has been defined as part of the `fixed-gas` efforts. -// The new `Vec` added by the `svm-sdk-std` crate is always being initialized using `Vec::with_capacity` method. -// In other words, a `Vec` should know in initialization time the maximum size it will need to store it's data. -// By knowing that, the `Vec` implementation has no `resize` / `shrink` code (as in the `std::vec::Vec`) -// which would have resulted in `loop` opcodes when being compiled to Wasm. +/// This trait has been defined as part of the `fixed-gas` efforts. +/// The new `Vec` added by the `svm-sdk-std` crate is always being initialized +/// using `Vec::with_capacity` method. +/// In other words, a `Vec` should know in initialization time the maximum size +/// it will need to store it's data. +/// By knowing that, the `Vec` implementation has no `resize` / `shrink` code +/// (as in the `std::vec::Vec`) which would have resulted in `loop` opcodes when +/// being compiled to Wasm. pub trait ByteSize { + /// Returns the expected size in bytes that will be required to store + /// `self`. This is *not* an estimate and rather must be exact. fn byte_size(&self) -> usize; + /// Returns the absolute maximum space in bytes that might be needed to + /// store any instance of `Self`. fn max_byte_size() -> usize; } + +/// Integer layout type information. This is needed for encoding numeric types +/// and accessing type information about them (we need to cast everything to the +/// unsigned type of the same width). +pub trait Numeric: AsPrimitive { + type Unsigned: Copy + Numeric + AsPrimitive; +} diff --git a/crates/abi/encoder/src/types/address.rs b/crates/abi/encoder/src/types/address.rs index e0ce736dc..04716b190 100644 --- a/crates/abi/encoder/src/types/address.rs +++ b/crates/abi/encoder/src/types/address.rs @@ -1,53 +1,23 @@ +use seq_macro::seq; use svm_abi_layout::layout; use svm_sdk_types::Address; +use crate::traits::Push; use crate::{ByteSize, Encoder}; -macro_rules! impl_primitive_encoder { - ($W:ty) => { - impl Encoder<$W> for Address { - /// Encodes `self` (of type `$ty`) and outputs the data into `w` - fn encode(&self, w: &mut $W) { - w.push(layout::ADDRESS); - - let bytes = self.as_slice(); - - // This code calls for using a loop. - // In order to assure there is no usage of `loop` when compiled to Wasm, - // we manually inject multiple statements. - // - // A future feature will add an ergonomic way to achieve the same - // without this tedious copy-pase ugly code. - // - // TODO: - // There is an issue for that: [Issue #230](https://github.com/spacemeshos/svm/issues/230) - w.push(bytes[0]); - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - w.push(bytes[8]); - w.push(bytes[9]); - w.push(bytes[10]); - w.push(bytes[11]); - w.push(bytes[12]); - w.push(bytes[13]); - w.push(bytes[14]); - w.push(bytes[15]); - w.push(bytes[16]); - w.push(bytes[17]); - w.push(bytes[18]); - w.push(bytes[19]); - } - } - }; +impl Encoder for Address +where + W: Push, +{ + fn encode(&self, w: &mut W) { + w.push(layout::ADDRESS); + let bytes = self.as_slice(); + seq!(N in 0..20 { + w.push(bytes[N]); + }); + } } -impl_primitive_encoder!(svm_sdk_std::Vec); - impl ByteSize for Address { fn byte_size(&self) -> usize { 21 diff --git a/crates/abi/encoder/src/types/amount.rs b/crates/abi/encoder/src/types/amount.rs index fa0e69921..88a507274 100644 --- a/crates/abi/encoder/src/types/amount.rs +++ b/crates/abi/encoder/src/types/amount.rs @@ -1,141 +1,38 @@ -use crate::{ByteSize, Encoder}; - +use seq_macro::seq; use svm_sdk_types::Amount; -macro_rules! encode { - ( $W:ty) => { - impl Encoder<$W> for Amount { - fn encode(&self, w: &mut $W) { - let v = self.0; - let size = self.byte_size(); - - use svm_abi_layout::layout; - - // TODO: - // for a detailed explanation on how to make the following code - // more ergonomic see look at `address.rs` under this module. - // There is also an issue for that: [Issue #230](https://github.com/spacemeshos/svm/issues/230) - - match size { - 2 => { - w.push(layout::AMOUNT_1B); - w.push(v as u8); - } - 3 => { - w.push(layout::AMOUNT_2B); - - let bytes: [u8; 2] = (v as u16).to_be_bytes(); - - w.push(bytes[0]); - w.push(bytes[1]); - } - 4 => { - w.push(layout::AMOUNT_3B); - - let bytes: [u8; 4] = (v as u32).to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - } - 5 => { - w.push(layout::AMOUNT_4B); - - let bytes: [u8; 4] = (v as u32).to_be_bytes(); - - w.push(bytes[0]); - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - } - 6 => { - w.push(layout::AMOUNT_5B); - - let bytes: [u8; 8] = v.to_be_bytes(); +use crate::{traits::Push, ByteSize, Encoder}; - debug_assert_eq!(bytes[0], 0); - debug_assert_eq!(bytes[1], 0); - debug_assert_eq!(bytes[2], 0); +impl Encoder for Amount +where + W: Push, +{ + fn encode(&self, w: &mut W) { + let size = self.byte_size(); - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - } - 7 => { - w.push(layout::AMOUNT_6B); + w.push(layout_amount_b(size as u8 - 2)); + let bytes: [u8; 8] = self.0.to_be_bytes(); - let bytes: [u8; 8] = v.to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - debug_assert_eq!(bytes[1], 0); - - w.push(bytes[2]); - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - } - 8 => { - w.push(layout::AMOUNT_7B); - - let bytes: [u8; 8] = v.to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - } - 9 => { - w.push(layout::AMOUNT_8B); - - let bytes: [u8; 8] = v.to_be_bytes(); - - w.push(bytes[0]); - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - } - _ => svm_sdk_std::panic(), - } + seq!(I in 0..8 { + if size >= 9 - I { + w.push(bytes[I]); } - } - }; + }); + } } -encode!(svm_sdk_std::Vec); - impl ByteSize for Amount { #[inline] fn byte_size(&self) -> usize { - let v = self.0; - - match v { - 0x00..=0xFF => 2, - 0x01_00..=0xFF_FF => 3, - 0x_01_00_00..=0xFF_FF_FF => 4, - 0x_01_00_00_00..=0xFF_FF_FF_FF => 5, - 0x_01_00_00_00_00..=0xFF_FF_FF_FF_FF => 6, - 0x_01_00_00_00_00_00..=0xFF_FF_FF_FF_FF_FF => 7, - 0x_01_00_00_00_00_00_00..=0xFF_FF_FF_FF_FF_FF_FF => 8, - 0x_01_00_00_00_00_00_00_00..=0xFF_FF_FF_FF_FF_FF_FF_FF => 9, - } + self.0.byte_size() } fn max_byte_size() -> usize { - 9 + u64::MAX.byte_size() } } + +#[inline] +const fn layout_amount_b(i: u8) -> u8 { + (i << 4) | 1 +} diff --git a/crates/abi/encoder/src/types/boolean.rs b/crates/abi/encoder/src/types/boolean.rs index 499dfaff2..1ea87ecbf 100644 --- a/crates/abi/encoder/src/types/boolean.rs +++ b/crates/abi/encoder/src/types/boolean.rs @@ -1,23 +1,20 @@ use svm_abi_layout::layout; -use crate::{ByteSize, Encoder}; +use crate::{traits::Push, ByteSize, Encoder}; -macro_rules! impl_bool { - ($W:ty) => { - impl Encoder<$W> for bool { - fn encode(&self, w: &mut $W) { - if *self { - w.push(layout::BOOL_TRUE); - } else { - w.push(layout::BOOL_FALSE); - } - } - } - }; +impl Encoder for bool +where + W: Push, +{ + fn encode(&self, w: &mut W) { + w.push(if *self { + layout::BOOL_TRUE + } else { + layout::BOOL_FALSE + }); + } } -impl_bool!(svm_sdk_std::Vec); - impl ByteSize for bool { fn byte_size(&self) -> usize { 1 diff --git a/crates/abi/encoder/src/types/mod.rs b/crates/abi/encoder/src/types/mod.rs index 3c06df805..3edfcc2ee 100644 --- a/crates/abi/encoder/src/types/mod.rs +++ b/crates/abi/encoder/src/types/mod.rs @@ -31,10 +31,7 @@ mod address; mod amount; mod boolean; -mod num_i16; -mod num_i32; -mod num_i64; -mod num_i8; +mod numeric; mod option; mod small_array; mod tuples; @@ -43,64 +40,58 @@ mod unit; pub use address::*; pub use amount::*; pub use boolean::*; -pub use num_i16::*; -pub use num_i32::*; -pub use num_i64::*; pub use option::*; pub use small_array::*; pub use tuples::*; -use crate::traits::Encoder; +use crate::traits::{Encoder, Push}; use svm_sdk_types::value::{Composite, Primitive, Value}; -macro_rules! encode_value { - ($W:ty) => { - impl Encoder<$W> for Value { - #[inline] - fn encode(&self, w: &mut $W) { - match self { - Value::Primitive(p) => encode_primitive(p, w), - Value::Composite(c) => encode_composite(c, w), - } - } - } - - fn encode_primitive(p: &Primitive, w: &mut $W) { - match p { - Primitive::None => encode_none(w), - Primitive::Unit => encode_unit(w), - Primitive::Address(p) => p.encode(w), - Primitive::Amount(p) => p.encode(w), - Primitive::Bool(p) => p.encode(w), - Primitive::I8(p) => p.encode(w), - Primitive::U8(p) => p.encode(w), - Primitive::I16(p) => p.encode(w), - Primitive::U16(p) => p.encode(w), - Primitive::I32(p) => p.encode(w), - Primitive::U32(p) => p.encode(w), - Primitive::I64(p) => p.encode(w), - Primitive::U64(p) => p.encode(w), - } +impl Encoder for Value +where + W: Push, +{ + #[inline] + fn encode(&self, w: &mut W) { + match self { + Value::Primitive(p) => encode_primitive(p, w), + Value::Composite(c) => encode_composite(c, w), } + } +} - fn encode_composite(c: &Composite, w: &mut $W) { - match c { - Composite::Vec(values) => { - values.as_slice().encode(w); - } - } - } +fn encode_primitive(p: &Primitive, w: &mut impl Push) { + match p { + Primitive::None => encode_none(w), + Primitive::Unit => encode_unit(w), + Primitive::Address(p) => p.encode(w), + Primitive::Amount(p) => p.encode(w), + Primitive::Bool(p) => p.encode(w), + Primitive::I8(p) => p.encode(w), + Primitive::U8(p) => p.encode(w), + Primitive::I16(p) => p.encode(w), + Primitive::U16(p) => p.encode(w), + Primitive::I32(p) => p.encode(w), + Primitive::U32(p) => p.encode(w), + Primitive::I64(p) => p.encode(w), + Primitive::U64(p) => p.encode(w), + } +} - #[inline] - fn encode_none(w: &mut $W) { - svm_sdk_std::Option::::None.encode(w) +fn encode_composite(c: &Composite, w: &mut impl Push) { + match c { + Composite::Vec(values) => { + values.as_slice().encode(w); } + } +} - #[inline] - fn encode_unit(w: &mut $W) { - ().encode(w) - } - }; +#[inline] +fn encode_none(w: &mut impl Push) { + svm_sdk_std::Option::::None.encode(w) } -encode_value!(svm_sdk_std::Vec); +#[inline] +fn encode_unit(w: &mut impl Push) { + ().encode(w) +} diff --git a/crates/abi/encoder/src/types/num_i16.rs b/crates/abi/encoder/src/types/num_i16.rs deleted file mode 100644 index 05b468440..000000000 --- a/crates/abi/encoder/src/types/num_i16.rs +++ /dev/null @@ -1,56 +0,0 @@ -use svm_abi_layout::layout; - -use crate::{ByteSize, Encoder}; - -macro_rules! encode { - ($ty:ty, $W:ty, $MARK_1B:expr, $MARK_2B:expr) => { - impl Encoder<$W> for $ty { - fn encode(&self, w: &mut $W) { - let v = *self as u16; - - let size = self.byte_size(); - - match size { - 2 => { - w.push($MARK_1B); - w.push(v as u8); - } - _ => { - w.push($MARK_2B); - - let bytes: [u8; 2] = v.to_be_bytes(); - - w.push(bytes[0]); - w.push(bytes[1]); - } - }; - } - } - }; -} - -encode!(i16, svm_sdk_std::Vec, layout::I16_1B, layout::I16_2B); -encode!(u16, svm_sdk_std::Vec, layout::U16_1B, layout::U16_2B); - -macro_rules! encode_byte_size { - ($ty:ty) => { - impl ByteSize for $ty { - #[inline] - fn byte_size(&self) -> usize { - let v = *self as u16; - - match v { - 0..=0xFF => 2, - _ => 3, - } - } - - fn max_byte_size() -> usize { - 3 - } - } - }; -} - -encode_byte_size!(i16); -encode_byte_size!(u16); diff --git a/crates/abi/encoder/src/types/num_i32.rs b/crates/abi/encoder/src/types/num_i32.rs deleted file mode 100644 index 9a07d810f..000000000 --- a/crates/abi/encoder/src/types/num_i32.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::{ByteSize, Encoder}; - -use svm_abi_layout::layout; - -macro_rules! encode { - ($ty:ty, $W:ty, $MARK_1B:expr, $MARK_2B:expr, $MARK_3B:expr, $MARK_4B:expr) => { - impl Encoder<$W> for $ty { - fn encode(&self, w: &mut $W) { - let v = *self as u32; - let size = self.byte_size(); - - // TODO: - // for a detailed explanation on how to make the following code - // more ergonomic see look at `address.rs` under this module. - // There is also an issue for that: [Issue #230](https://github.com/spacemeshos/svm/issues/230) - - match size { - 2 => { - w.push($MARK_1B); - w.push(v as u8); - } - 3 => { - w.push($MARK_2B); - - let bytes: [u8; 4] = v.to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - debug_assert_eq!(bytes[1], 0); - - w.push(bytes[2]); - w.push(bytes[3]); - } - 4 => { - w.push($MARK_3B); - - let bytes: [u8; 4] = v.to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - } - 5 => { - w.push($MARK_4B); - - let bytes: [u8; 4] = self.to_be_bytes(); - - w.push(bytes[0]); - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - } - _ => svm_sdk_std::panic(), - } - } - } - }; -} - -encode!( - i32, - svm_sdk_std::Vec, - layout::I32_1B, - layout::I32_2B, - layout::I32_3B, - layout::I32_4B -); - -encode!( - u32, - svm_sdk_std::Vec, - layout::U32_1B, - layout::U32_2B, - layout::U32_3B, - layout::U32_4B -); - -macro_rules! encode_byte_size { - ($ty:ty) => { - impl ByteSize for $ty { - #[inline] - fn byte_size(&self) -> usize { - let v = *self as u32; - - match v { - 0x00..=0xFF => 2, - 0x01_00..=0xFF_FF => 3, - 0x_01_00_00..=0xFF_FF_FF => 4, - 0x01_00_00_00..=0xFF_FF_FF_FF => 5, - } - } - - fn max_byte_size() -> usize { - 5 - } - } - }; -} - -encode_byte_size!(i32); -encode_byte_size!(u32); diff --git a/crates/abi/encoder/src/types/num_i64.rs b/crates/abi/encoder/src/types/num_i64.rs deleted file mode 100644 index ff5d686ec..000000000 --- a/crates/abi/encoder/src/types/num_i64.rs +++ /dev/null @@ -1,170 +0,0 @@ -use svm_abi_layout::layout; - -use crate::{ByteSize, Encoder}; - -macro_rules! encode { - ($ty:ty, $W:ty, $MARK_1B:expr, $MARK_2B:expr, $MARK_3B:expr, $MARK_4B:expr, $MARK_5B:expr, $MARK_6B:expr, $MARK_7B:expr, $MARK_8B:expr) => { - impl Encoder<$W> for $ty { - fn encode(&self, w: &mut $W) { - let v = *self as u64; - let size = self.byte_size(); - - // TODO: - // for a detailed explanation on how to make the following code - // more ergonomic see look at `address.rs` under this module. - // There is also an issue for that: [Issue #230](https://github.com/spacemeshos/svm/issues/230) - - match size { - 2 => { - w.push($MARK_1B); - w.push(v as u8); - } - 3 => { - w.push($MARK_2B); - - let bytes: [u8; 2] = (v as u16).to_be_bytes(); - - w.push(bytes[0]); - w.push(bytes[1]); - } - 4 => { - w.push($MARK_3B); - - let bytes: [u8; 4] = (v as u32).to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - } - 5 => { - w.push($MARK_4B); - - let bytes: [u8; 4] = (v as u32).to_be_bytes(); - - w.push(bytes[0]); - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - } - 6 => { - w.push($MARK_5B); - - let bytes: [u8; 8] = v.to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - debug_assert_eq!(bytes[1], 0); - debug_assert_eq!(bytes[2], 0); - - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - } - 7 => { - w.push($MARK_6B); - - let bytes: [u8; 8] = v.to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - debug_assert_eq!(bytes[1], 0); - - w.push(bytes[2]); - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - } - 8 => { - w.push($MARK_7B); - - let bytes: [u8; 8] = v.to_be_bytes(); - - debug_assert_eq!(bytes[0], 0); - - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - } - 9 => { - w.push($MARK_8B); - - let bytes: [u8; 8] = v.to_be_bytes(); - - w.push(bytes[0]); - w.push(bytes[1]); - w.push(bytes[2]); - w.push(bytes[3]); - w.push(bytes[4]); - w.push(bytes[5]); - w.push(bytes[6]); - w.push(bytes[7]); - } - _ => svm_sdk_std::panic(), - } - } - } - }; -} - -encode!( - i64, - svm_sdk_std::Vec, - layout::I64_1B, - layout::I64_2B, - layout::I64_3B, - layout::I64_4B, - layout::I64_5B, - layout::I64_6B, - layout::I64_7B, - layout::I64_8B -); - -encode!( - u64, - svm_sdk_std::Vec, - layout::U64_1B, - layout::U64_2B, - layout::U64_3B, - layout::U64_4B, - layout::U64_5B, - layout::U64_6B, - layout::U64_7B, - layout::U64_8B -); - -macro_rules! encode_byte_size { - ($ty:ty) => { - impl ByteSize for $ty { - #[inline] - fn byte_size(&self) -> usize { - let v = *self as u64; - - match v { - 0x00..=0xFF => 2, - 0x01_00..=0xFF_FF => 3, - 0x_01_00_00..=0xFF_FF_FF => 4, - 0x_01_00_00_00..=0xFF_FF_FF_FF => 5, - 0x_01_00_00_00_00..=0xFF_FF_FF_FF_FF => 6, - 0x_01_00_00_00_00_00..=0xFF_FF_FF_FF_FF_FF => 7, - 0x_01_00_00_00_00_00_00..=0xFF_FF_FF_FF_FF_FF_FF => 8, - 0x_01_00_00_00_00_00_00_00..=0xFF_FF_FF_FF_FF_FF_FF_FF => 9, - } - } - - fn max_byte_size() -> usize { - 9 - } - } - }; -} - -encode_byte_size!(i64); -encode_byte_size!(u64); diff --git a/crates/abi/encoder/src/types/num_i8.rs b/crates/abi/encoder/src/types/num_i8.rs deleted file mode 100644 index 5fd76e38a..000000000 --- a/crates/abi/encoder/src/types/num_i8.rs +++ /dev/null @@ -1,41 +0,0 @@ -use svm_abi_layout::layout; - -use crate::{ByteSize, Encoder}; - -macro_rules! encode { - ($W:ty) => { - impl Encoder<$W> for u8 { - fn encode(&self, w: &mut $W) { - w.push(layout::U8); - w.push(*self); - } - } - - impl Encoder<$W> for i8 { - #[inline] - fn encode(&self, w: &mut $W) { - w.push(layout::I8); - w.push(*self as u8); - } - } - }; -} - -encode!(svm_sdk_std::Vec); - -macro_rules! impl_byte_size { - ($ty:ty) => { - impl ByteSize for $ty { - fn byte_size(&self) -> usize { - 2 - } - - fn max_byte_size() -> usize { - 2 - } - } - }; -} - -impl_byte_size!(i8); -impl_byte_size!(u8); diff --git a/crates/abi/encoder/src/types/numeric.rs b/crates/abi/encoder/src/types/numeric.rs new file mode 100644 index 000000000..6b4dd0ac0 --- /dev/null +++ b/crates/abi/encoder/src/types/numeric.rs @@ -0,0 +1,162 @@ +use num_traits::{AsPrimitive, Bounded}; + +use crate::traits::{Numeric, Push}; +use crate::{ByteSize, Encoder}; + +impl Encoder for T +where + T: Numeric + ByteSize + num_traits::PrimInt, + W: Push, +{ + fn encode(&self, w: &mut W) { + // We need three pieces of information to generate the layout marker + // bytes on the fly: + // + // 1. Sign. Is the number type signed or unsigned? + // 2. Max. width. How many bytes is it? (e.g. u32 is 4 bytes). + // 3. Actual width. How many bytes do we need to store this specific + // number? (e.g. 0-127 only takes one byte, regardless of the number + // type). + let type_is_signed = T::min_value() < T::zero(); + + // We get the payload size by subtracing 1, which is the space required + // by the layout marker byte. Same for the max. width. + let payload_size = self.byte_size() - 1; + let layout_marker = layout_integer( + T::max_byte_size() as u32 - 1, + payload_size as u32, + type_is_signed, + ); + w.push(layout_marker); + + // Encoding is tricky and requires first to cast to the relevant + // unsigned type, then extending to 64 bits. Note: you can't cast + // directly as some numbers will result in different representations. + let self_unsigned: T::Unsigned = self.as_(); + let self_u64: u64 = self_unsigned.as_(); + let bytes: [u8; 8] = self_u64.to_be_bytes(); + + // Finally, push all relevant bytes. + seq_macro::seq!(i in 0..8 { + if payload_size >= 8 - i { + w.push(bytes[i]); + } + }); + } +} + +impl ByteSize for T +where + T: Numeric + Bounded, +{ + fn byte_size(&self) -> usize { + let self_unsigned: T::Unsigned = self.as_(); + let self_u64: u64 = self_unsigned.as_(); + + match self_u64 { + 0..=0xFF => 2, + 0..=0xFF_FF => 3, + 0..=0xFF_FF_FF => 4, + 0..=0xFF_FF_FF_FF => 5, + 0..=0xFF_FF_FF_FF_FF => 6, + 0..=0xFF_FF_FF_FF_FF_FF => 7, + 0..=0xFF_FF_FF_FF_FF_FF_FF => 8, + 0..=0xFF_FF_FF_FF_FF_FF_FF_FF => 9, + } + } + + fn max_byte_size() -> usize { + Self::max_value().byte_size() + } +} + +impl Numeric for i8 { + type Unsigned = u8; +} + +impl Numeric for u8 { + type Unsigned = u8; +} + +impl Numeric for i16 { + type Unsigned = u16; +} + +impl Numeric for u16 { + type Unsigned = u16; +} + +impl Numeric for i32 { + type Unsigned = u32; +} + +impl Numeric for u32 { + type Unsigned = u32; +} + +impl Numeric for i64 { + type Unsigned = u64; +} + +impl Numeric for u64 { + type Unsigned = u64; +} + +/// Integer layout marker bytes generator. It looks scary, but all it does is it +/// dynamically encodes the layout formula specificied by the ABI. +pub fn layout_integer(max_width_in_bytes: u32, width_in_bytes: u32, signed: bool) -> u8 { + use svm_abi_layout::layout::*; + debug_assert!(width_in_bytes <= max_width_in_bytes); + debug_assert!(max_width_in_bytes <= 8); + match (max_width_in_bytes, (width_in_bytes - 1) as u8, signed) { + (1, 0, true) => I8, + (1, 0, false) => U8, + (2, n, true) => I16_1B | ((n & 0b1) << 4), + (2, n, false) => U16_1B | ((n & 0b1) << 4), + (4, n, true) => I32_1B | ((n & 0b11) << 4), + (4, n, false) => U32_1B | ((n & 0b11) << 4), + (8, n, true) => I64_1B | ((n & 0b111) << 4), + (8, n, false) => U64_1B | ((n & 0b111) << 4), + _ => panic!("Invalid argument for layout information."), + } +} + +#[cfg(test)] +mod test { + use super::*; + use svm_abi_layout::layout; + + #[test] + fn integer_layouts() { + assert_eq!(layout_integer(1, 1, true), layout::I8); + assert_eq!(layout_integer(1, 1, false), layout::U8); + assert_eq!(layout_integer(2, 1, true), layout::I16_1B); + assert_eq!(layout_integer(2, 2, true), layout::I16_2B); + assert_eq!(layout_integer(2, 1, false), layout::U16_1B); + assert_eq!(layout_integer(2, 2, false), layout::U16_2B); + assert_eq!(layout_integer(4, 1, true), layout::I32_1B); + assert_eq!(layout_integer(4, 2, true), layout::I32_2B); + assert_eq!(layout_integer(4, 3, true), layout::I32_3B); + assert_eq!(layout_integer(4, 4, true), layout::I32_4B); + assert_eq!(layout_integer(4, 1, false), layout::U32_1B); + assert_eq!(layout_integer(4, 2, false), layout::U32_2B); + assert_eq!(layout_integer(4, 3, false), layout::U32_3B); + assert_eq!(layout_integer(4, 4, false), layout::U32_4B); + assert_eq!(layout_integer(8, 1, true), layout::I64_1B); + assert_eq!(layout_integer(8, 2, true), layout::I64_2B); + assert_eq!(layout_integer(8, 3, true), layout::I64_3B); + assert_eq!(layout_integer(8, 4, true), layout::I64_4B); + assert_eq!(layout_integer(8, 5, true), layout::I64_5B); + assert_eq!(layout_integer(8, 6, true), layout::I64_6B); + assert_eq!(layout_integer(8, 7, true), layout::I64_7B); + assert_eq!(layout_integer(8, 8, true), layout::I64_8B); + assert_eq!(layout_integer(8, 1, false), layout::U64_1B); + assert_eq!(layout_integer(8, 2, false), layout::U64_2B); + assert_eq!(layout_integer(8, 3, false), layout::U64_3B); + assert_eq!(layout_integer(8, 4, false), layout::U64_4B); + assert_eq!(layout_integer(8, 5, false), layout::U64_5B); + assert_eq!(layout_integer(8, 6, false), layout::U64_6B); + assert_eq!(layout_integer(8, 7, false), layout::U64_7B); + assert_eq!(layout_integer(8, 8, false), layout::U64_8B); + } +} diff --git a/crates/abi/encoder/src/types/option.rs b/crates/abi/encoder/src/types/option.rs index aebddc5be..43868470c 100644 --- a/crates/abi/encoder/src/types/option.rs +++ b/crates/abi/encoder/src/types/option.rs @@ -1,29 +1,24 @@ -use crate::traits::ByteSize; - use svm_sdk_std::Option; -macro_rules! encode { - ($W:ty) => { - impl crate::traits::Encoder<$W> for svm_sdk_std::Option - where - T: crate::traits::Encoder<$W>, - { - fn encode(&self, w: &mut $W) { - match self { - svm_sdk_std::Option::None => { - use svm_abi_layout::layout; +use crate::traits::{ByteSize, Encoder, Push}; - w.push(layout::NONE); - } - svm_sdk_std::Option::Some(val) => val.encode(w), - } +impl Encoder for svm_sdk_std::Option +where + T: Encoder, + W: Push, +{ + fn encode(&self, w: &mut W) { + match self { + svm_sdk_std::Option::None => { + use svm_abi_layout::layout; + + w.push(layout::NONE); } + svm_sdk_std::Option::Some(val) => val.encode(w), } - }; + } } -encode!(svm_sdk_std::Vec); - impl ByteSize for Option where T: ByteSize, diff --git a/crates/abi/encoder/src/types/small_array.rs b/crates/abi/encoder/src/types/small_array.rs index 655e6c724..7cf262e36 100644 --- a/crates/abi/encoder/src/types/small_array.rs +++ b/crates/abi/encoder/src/types/small_array.rs @@ -1,128 +1,35 @@ +use crate::traits::Push; use crate::{ByteSize, Encoder}; -// TODO: -// for a detailed explanation on how to make the following code -// more ergonomic see look at `address.rs` under this module. -// There is also an issue for that: [Issue #230](https://github.com/spacemeshos/svm/issues/230) - -macro_rules! impl_encode { - ($W:ty) => { - impl Encoder<$W> for &[T] - where T: Encoder<$W> - { - fn encode(&self, w: &mut $W) { - use svm_abi_layout::layout; - - assert!(self.len() < 11); - - let marker = match self.len() { - 0 => layout::ARR_0, - 1 => layout::ARR_1, - 2 => layout::ARR_2, - 3 => layout::ARR_3, - 4 => layout::ARR_4, - 5 => layout::ARR_5, - 6 => layout::ARR_6, - 7 => layout::ARR_7, - 8 => layout::ARR_8, - 9 => layout::ARR_9, - 10 => layout::ARR_10, - _ => svm_sdk_std::panic(), - }; - - w.push(marker); +impl Encoder for &[T] +where + T: Encoder, + W: Push, +{ + fn encode(&self, w: &mut W) { + assert!(self.len() < 11); - let mut iter = self.iter(); + w.push(layout_array(self.len())); - match self.len() { - 0 => impl_encode!(0 iter w), - 1 => impl_encode!(1 iter w), - 2 => impl_encode!(2 iter w), - 3 => impl_encode!(3 iter w), - 4 => impl_encode!(4 iter w), - 5 => impl_encode!(5 iter w), - 6 => impl_encode!(6 iter w), - 7 => impl_encode!(7 iter w), - 8 => impl_encode!(8 iter w), - 9 => impl_encode!(9 iter w), - 10 => impl_encode!(10 iter w), - _ => svm_sdk_std::panic(), - }; + seq_macro::seq!(i in 0..11 { + if self.len() > i { + self[i].encode(w); } - } - }; - - (0 $iter:ident $w:ident) => {{ }}; - (1 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(0 $iter $w); - }}; - (2 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(1 $iter $w); - }}; - (3 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(2 $iter $w); - }}; - (4 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(3 $iter $w); - }}; - (5 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(4 $iter $w); - }}; - (6 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(5 $iter $w); - }}; - (7 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(6 $iter $w); - }}; - (8 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(7 $iter $w); - }}; - (9 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(8 $iter $w); - }}; - (10 $iter:ident $w:ident) => {{ - impl_encode!(@ $iter $w); - impl_encode!(9 $iter $w); - }}; - (@ $iter:ident $w:ident) => {{ - let item: svm_sdk_std::Option<_> = $iter.next().into(); - - let item = item.unwrap(); - - item.encode($w); - }} + }); + } } -impl_encode!(svm_sdk_std::Vec); - -macro_rules! impl_array_encode { - ($W:ty => $($N:expr),*) => { - $( impl_array_encode!{@ $W => $N} )* - }; - - (@ $W:ty => $N:expr) => { - impl Encoder<$W> for [T; $N] - where T: Encoder<$W> - { - #[inline] - fn encode(&self, w: &mut $W) { - (&self[..]).encode(w) - } - } - }; +impl Encoder for [T; N] +where + T: Encoder, + W: Push, +{ + #[inline] + fn encode(&self, w: &mut W) { + (&self[..]).encode(w) + } } -impl_array_encode!(svm_sdk_std::Vec => 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - impl ByteSize for [T; N] where T: ByteSize, @@ -130,76 +37,16 @@ where fn byte_size(&self) -> usize { assert!(N < 11); - 1 + match N { - 0 => 0, - 1 => self[0].byte_size(), - 2 => self[0].byte_size() + self[1].byte_size(), - 3 => self[0].byte_size() + self[1].byte_size() + self[2].byte_size(), - 4 => { - self[0].byte_size() - + self[1].byte_size() - + self[2].byte_size() - + self[3].byte_size() - } - 5 => { - self[0].byte_size() - + self[1].byte_size() - + self[2].byte_size() - + self[3].byte_size() - + self[4].byte_size() - } - 6 => { - self[0].byte_size() - + self[1].byte_size() - + self[2].byte_size() - + self[3].byte_size() - + self[4].byte_size() - + self[5].byte_size() - } - 7 => { - self[0].byte_size() - + self[1].byte_size() - + self[2].byte_size() - + self[3].byte_size() - + self[4].byte_size() - + self[5].byte_size() - + self[6].byte_size() + let mut payload_size = 0; + seq_macro::seq!(i in 0..11 { + // The compiler complains, but it's wrong! The following comparison + // might be useless or not, depending on the array const generic. + #[allow(unused_comparisons)] + if N >= i { + payload_size += self[i].byte_size(); } - 8 => { - self[0].byte_size() - + self[1].byte_size() - + self[2].byte_size() - + self[3].byte_size() - + self[4].byte_size() - + self[5].byte_size() - + self[6].byte_size() - + self[7].byte_size() - } - 9 => { - self[0].byte_size() - + self[1].byte_size() - + self[2].byte_size() - + self[3].byte_size() - + self[4].byte_size() - + self[5].byte_size() - + self[6].byte_size() - + self[7].byte_size() - + self[8].byte_size() - } - 10 => { - self[0].byte_size() - + self[1].byte_size() - + self[2].byte_size() - + self[3].byte_size() - + self[4].byte_size() - + self[5].byte_size() - + self[6].byte_size() - + self[7].byte_size() - + self[8].byte_size() - + self[9].byte_size() - } - _ => svm_sdk_std::panic(), - } + }); + 1 + payload_size } fn max_byte_size() -> usize { @@ -207,6 +54,15 @@ where } } +/// Calculates the layout marker byte of an array of size `len`. +const fn layout_array(len: usize) -> u8 { + if len < 8 { + 0b_0_000_0110 | (len << 4) as u8 + } else { + 0b_0_000_0111 | ((len - 8) << 4) as u8 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/abi/encoder/src/types/unit.rs b/crates/abi/encoder/src/types/unit.rs index da20fcbe9..4e282bd78 100644 --- a/crates/abi/encoder/src/types/unit.rs +++ b/crates/abi/encoder/src/types/unit.rs @@ -1,19 +1,16 @@ use svm_abi_layout::layout; -use crate::{ByteSize, Encoder}; +use crate::{traits::Push, ByteSize, Encoder}; -macro_rules! encode { - ($W:ty) => { - impl Encoder<$W> for () { - fn encode(&self, w: &mut $W) { - w.push(layout::UNIT); - } - } - }; +impl Encoder for () +where + W: Push, +{ + fn encode(&self, w: &mut W) { + w.push(layout::UNIT); + } } -encode!(svm_sdk_std::Vec); - impl ByteSize for () { fn byte_size(&self) -> usize { 1 diff --git a/crates/sdk/std/src/panic.rs b/crates/sdk/std/src/panic.rs index 492950d23..e71711a62 100644 --- a/crates/sdk/std/src/panic.rs +++ b/crates/sdk/std/src/panic.rs @@ -1,10 +1,11 @@ -/// When code is compiled for Wasm, we want to abort execution immediately without any proper unwinding +/// When code is compiled for Wasm, we want to abort execution immediately +/// without any proper unwinding. #[cfg(target_arch = "wasm32")] pub fn panic() -> ! { core::intrinsics::abort(); } -/// When code isn't compiled for Wasm, we just fallback to `panic!` +/// When code isn't compiled for Wasm, we just fallback to `panic!`. #[cfg(not(target_arch = "wasm32"))] pub fn panic() -> ! { core::panic!() diff --git a/crates/sdk/types/src/blob.rs b/crates/sdk/types/src/blob.rs index 74151fc48..073cda1e5 100644 --- a/crates/sdk/types/src/blob.rs +++ b/crates/sdk/types/src/blob.rs @@ -72,8 +72,6 @@ macro_rules! impl_blob_type { impl From> for $ty { #[inline] fn from(vec: Vec) -> Self { - // TODO: leak `vec` - ensure!(vec.len() == Self::len()); let slice = vec.leak();