Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial sketch 160-bit (32+128) RawVal #570

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

137 changes: 137 additions & 0 deletions soroban-env-common/src/abi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
pub trait BufReadWrite: Sized {
type MemBuf;
const ZERO_BUF: Self::MemBuf;
fn buf_as_slice(b: &Self::MemBuf) -> &[u8];
fn buf_as_mut_slice(b: &mut Self::MemBuf) -> &mut [u8];
fn buf_write(self, b: &mut Self::MemBuf);
fn buf_read(b: &Self::MemBuf) -> Self;
}

pub trait V160: BufReadWrite {
fn v160_explode(self) -> (u32, u64, u64);
fn v160_implode(a: u32, b: u64, c: u64) -> Self;
}

pub trait V128: BufReadWrite {
fn v128_explode(self) -> (u64, u64);
fn v128_implode(a: u64, b: u64) -> Self;
}

// We implement ABIs for various primitive types here
// so that other impls can use them by ref and so that
// host functions can take primitive types as well.

impl BufReadWrite for u128 {
type MemBuf = [u8; 16];
const ZERO_BUF: Self::MemBuf = [0; 16];

fn buf_write(self, b: &mut Self::MemBuf) {
*b = u128::to_le_bytes(self)
}

fn buf_read(b: &Self::MemBuf) -> Self {
u128::from_le_bytes(*b)
}

fn buf_as_slice(b: &Self::MemBuf) -> &[u8] {
b.as_slice()
}

fn buf_as_mut_slice(b: &mut Self::MemBuf) -> &mut [u8] {
b.as_mut_slice()
}
}

impl BufReadWrite for i128 {
type MemBuf = [u8; 16];
const ZERO_BUF: Self::MemBuf = [0; 16];

fn buf_write(self, b: &mut Self::MemBuf) {
*b = i128::to_le_bytes(self)
}

fn buf_read(b: &Self::MemBuf) -> Self {
i128::from_le_bytes(*b)
}

fn buf_as_slice(b: &Self::MemBuf) -> &[u8] {
b.as_slice()
}

fn buf_as_mut_slice(b: &mut Self::MemBuf) -> &mut [u8] {
b.as_mut_slice()
}
}

impl V128 for u128 {
fn v128_explode(self) -> (u64, u64) {
(self as u64, (self >> 64) as u64)
}

fn v128_implode(a: u64, b: u64) -> Self {
a as u128 | ((b as u128) << 64)
}
}

impl V128 for i128 {
fn v128_explode(self) -> (u64, u64) {
(self as u64, (self >> 64) as u64)
}

fn v128_implode(a: u64, b: u64) -> Self {
a as i128 | ((b as i128) << 64)
}
}

pub trait Direct {
type Repr;
fn to_repr(self) -> Self::Repr;
fn from_repr(r: Self::Repr) -> Self;
}

macro_rules! decl_trivial_direct_return {
($T:ty, $repr:ty) => {
impl Direct for $T {
type Repr = $repr;
fn to_repr(self) -> Self::Repr {
self as $repr
}
fn from_repr(r: Self::Repr) -> Self {
r as Self
}
}
};
}

decl_trivial_direct_return!(u32, i32);
decl_trivial_direct_return!(i32, i32);
decl_trivial_direct_return!(u64, i64);
decl_trivial_direct_return!(i64, i64);

#[doc(hidden)]
#[macro_export]
macro_rules! decl_wrapper_direct_abi_support {
($wrapper:ident, $wrapper_ty:ty, $wasmi_tag:ident, $wasmi_ty:ty) => {
#[cfg(feature = "vm")]
impl wasmi::core::FromValue for $wrapper {
fn from_value(val: wasmi::core::Value) -> Option<Self> {
<$wrapper_ty as wasmi::core::FromValue>::from_value(val).map(|x| $wrapper(x))
}
}
#[cfg(feature = "vm")]
impl From<$wrapper> for wasmi::core::Value {
fn from(v: $wrapper) -> Self {
wasmi::core::Value::$wasmi_tag(v.0 as $wasmi_ty)
}
}
impl crate::abi::Direct for $wrapper {
type Repr = $wasmi_ty;
fn to_repr(self) -> Self::Repr {
self.0 as Self::Repr
}
fn from_repr(r: Self::Repr) -> Self {
Self(r as $wrapper_ty)
}
}
};
}
9 changes: 4 additions & 5 deletions soroban-env-common/src/array.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::xdr::ScHostValErrorCode;
use stellar_xdr::ScObjectType;
use stellar_xdr::{ScObjectType, ScHostValErrorCode};

use crate::{
ConversionError, Env, Object, RawVal, RawValConvertible, Status, TryFromVal, TryIntoVal,
Expand All @@ -17,12 +16,12 @@ impl<E: Env, const N: usize> TryFromVal<E, RawVal> for [u8; N] {
}
let env = env.clone();
let bytes = unsafe { Object::unchecked_from_val(val) };
let len = unsafe { u32::unchecked_from_val(env.bytes_len(bytes)) };
let len = env.bytes_len(bytes);
if len as usize != N {
return Err(ConversionError.into());
}
let mut arr = [0u8; N];
env.bytes_copy_to_slice(bytes, RawVal::U32_ZERO, &mut arr)?;
env.bytes_copy_to_slice(bytes, 0, &mut arr)?;
Ok(arr)
}
}
Expand All @@ -39,7 +38,7 @@ impl<E: Env> TryIntoVal<E, RawVal> for &[u8] {
type Error = Status;
#[inline(always)]
fn try_into_val(self, env: &E) -> Result<RawVal, Self::Error> {
Ok(env.bytes_new_from_slice(self)?.to_raw())
Ok(env.bytes_new_from_slice(self)?.into())
}
}

Expand Down
105 changes: 57 additions & 48 deletions soroban-env-common/src/bitset.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,90 @@
use crate::{decl_tagged_val_wrapper_methods, ConversionError, Env, EnvVal, RawVal, Tag};
use core::cmp::Ordering;
use crate::{decl_tagged_val_wrapper_methods, Env, EnvVal, RawVal, Tag};
use core::fmt::Debug;
use core::hash::{Hash, Hasher};

/// Wrapper for a [RawVal] that is tagged with [Tag::BitSet], interpreting the
/// [RawVal]'s body as a small bitset (60-bits or fewer).
#[derive(Copy, Clone)]
pub struct BitSet(RawVal);
///
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct BitSet(u128);

decl_tagged_val_wrapper_methods!(BitSet);
impl crate::abi::BufReadWrite for BitSet {
type MemBuf = <u128 as crate::abi::BufReadWrite>::MemBuf;

/// Errors related to operations on the [BitSet] type.
#[derive(Debug)]
pub enum BitSetError {
/// Returned when attempting to form a [BitSet] from a [u64] with more than
/// 60 bits set.
TooManyBits(u64),
}
const ZERO_BUF: Self::MemBuf = <u128 as crate::abi::BufReadWrite>::ZERO_BUF;

impl From<BitSetError> for ConversionError {
fn from(_: BitSetError) -> Self {
ConversionError
fn buf_write(self, b: &mut Self::MemBuf) {
self.0.buf_write(b)
}
}

impl Hash for BitSet {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_raw().get_payload().hash(state);
fn buf_read(b: &Self::MemBuf) -> Self {
Self(<u128 as crate::abi::BufReadWrite>::buf_read(b))
}

fn buf_as_slice(b: &Self::MemBuf) -> &[u8] {
b.as_slice()
}

fn buf_as_mut_slice(b: &mut Self::MemBuf) -> &mut [u8] {
b.as_mut_slice()
}
}

impl PartialEq for BitSet {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.as_raw().get_payload() == other.as_raw().get_payload()
impl crate::abi::V128 for BitSet {
fn v128_explode(self) -> (u64, u64) {
self.0.v128_explode()
}

fn v128_implode(a: u64, b: u64) -> Self {
Self(<u128 as crate::abi::V128>::v128_implode(a, b))
}
}

impl Eq for BitSet {}
decl_tagged_val_wrapper_methods!(BitSet);

impl From<BitSet> for RawVal {
fn from(obj: BitSet) -> Self {
unsafe { RawVal::from_payload_and_tag(obj.0, Tag::BitSet) }
}
}

impl PartialOrd for BitSet {
impl crate::RawValConvertible for BitSet {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
fn is_val_type(v: RawVal) -> bool {
v.has_tag(Tag::BitSet)
}
#[inline(always)]
unsafe fn unchecked_from_val(v: RawVal) -> Self {
BitSet(v.payload)
}
}

impl Ord for BitSet {
impl Hash for BitSet {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
self.as_raw().get_body().cmp(&other.as_raw().get_body())
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}

impl Debug for BitSet {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "BitSet({:#b})", self.as_raw().get_body())
write!(f, "BitSet({:#b})", self.0)
}
}

impl BitSet {
/// Attempt to construct a [BitSet] from a u64, succeeding only if
/// the most significant 4 bits of the u64 are clear. In other words,
/// this function accepts only "small" bitsets of 60 or fewer bits.
#[inline(always)]
pub const fn try_from_u64(u: u64) -> Result<BitSet, BitSetError> {
if u & 0x0fff_ffff_ffff_ffff == u {
Ok(unsafe { Self::from_body(u) })
} else {
Err(BitSetError::TooManyBits(u))
}
impl From<u128> for BitSet {
fn from(bits: u128) -> Self {
BitSet(bits)
}
}

impl Into<u128> for BitSet {
fn into(self) -> u128 {
self.0
}
}

impl BitSet {
#[inline(always)]
// Returns the 60-bit "body" of the contained [RawVal] as a [u64].
pub const fn to_u64(&self) -> u64 {
self.0.get_body()
pub const fn to_u128(&self) -> u128 {
self.0
}
}
Loading