#![cfg_attr(test, allow(non_upper_case_globals))]
use crate::xdr::{ScError, ScVal, ScValType};
use crate::{
declare_tag_based_object_wrapper, declare_tag_based_wrapper,
impl_tryfroms_and_tryfromvals_delegating_to_valconvert, impl_val_wrapper_base, Compare, I32Val,
SymbolSmall, SymbolStr, U32Val,
};
use super::{Env, Error, TryFromVal};
use core::{cmp::Ordering, convert::Infallible, fmt::Debug, str};
extern crate static_assertions as sa;
#[allow(dead_code)]
const WORD_BITS: usize = 64;
pub(crate) const TAG_BITS: usize = 8;
const TAG_MASK: u64 = (1u64 << TAG_BITS) - 1;
sa::const_assert!(TAG_MASK == 0xff);
#[allow(dead_code)]
pub(crate) const BODY_BITS: usize = WORD_BITS - TAG_BITS;
sa::const_assert!(BODY_BITS == 56);
#[allow(dead_code)]
const MAJOR_BITS: usize = 32;
const MINOR_BITS: usize = 24;
#[allow(dead_code)]
const MAJOR_MASK: u64 = (1u64 << MAJOR_BITS) - 1;
const MINOR_MASK: u64 = (1u64 << MINOR_BITS) - 1;
sa::const_assert!(MAJOR_MASK == 0xffff_ffff);
sa::const_assert!(MINOR_MASK == 0x00ff_ffff);
sa::const_assert!(MAJOR_BITS + MINOR_BITS == BODY_BITS);
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[cfg_attr(test, derive(num_enum::TryFromPrimitive))]
pub enum Tag {
False = 0,
True = 1,
Void = 2,
Error = 3,
U32Val = 4,
I32Val = 5,
U64Small = 6,
I64Small = 7,
TimepointSmall = 8,
DurationSmall = 9,
U128Small = 10,
I128Small = 11,
U256Small = 12,
I256Small = 13,
SymbolSmall = 14,
SmallCodeUpperBound = 15,
ObjectCodeLowerBound = 63,
U64Object = 64,
I64Object = 65,
TimepointObject = 66,
DurationObject = 67,
U128Object = 68,
I128Object = 69,
U256Object = 70,
I256Object = 71,
BytesObject = 72,
StringObject = 73,
SymbolObject = 74,
VecObject = 75,
MapObject = 76,
AddressObject = 77,
ObjectCodeUpperBound = 78,
Bad = 0x7f,
}
impl Tag {
#[inline(always)]
pub const fn val_mask() -> i64 {
TAG_MASK as i64
}
#[inline(always)]
pub fn val_const(&self) -> i64 {
*self as i64
}
#[inline(always)]
pub(crate) const fn u8_is_object(x: u8) -> bool {
x > (Tag::ObjectCodeLowerBound as u8) && x < (Tag::ObjectCodeUpperBound as u8)
}
#[inline(always)]
pub const fn is_object(self) -> bool {
Self::u8_is_object(self as u8)
}
#[inline(always)]
pub const fn from_u8(tag: u8) -> Tag {
const A: u8 = Tag::SmallCodeUpperBound as u8;
const B: u8 = Tag::ObjectCodeLowerBound as u8;
const C: u8 = Tag::ObjectCodeUpperBound as u8;
if !((tag < A) || (B < tag && tag < C)) {
return Tag::Bad;
}
unsafe { ::core::mem::transmute(tag) }
}
#[inline(always)]
pub const fn get_scval_type(&self) -> Option<ScValType> {
match *self {
Tag::False => Some(ScValType::Bool),
Tag::True => Some(ScValType::Bool),
Tag::Void => Some(ScValType::Void),
Tag::Error => Some(ScValType::Error),
Tag::U32Val => Some(ScValType::U32),
Tag::I32Val => Some(ScValType::I32),
Tag::U64Small => Some(ScValType::U64),
Tag::I64Small => Some(ScValType::I64),
Tag::TimepointSmall => Some(ScValType::Timepoint),
Tag::DurationSmall => Some(ScValType::Duration),
Tag::U128Small => Some(ScValType::U128),
Tag::I128Small => Some(ScValType::I128),
Tag::U256Small => Some(ScValType::U256),
Tag::I256Small => Some(ScValType::I256),
Tag::SymbolSmall => Some(ScValType::Symbol),
Tag::SmallCodeUpperBound => None,
Tag::ObjectCodeLowerBound => None,
Tag::U64Object => Some(ScValType::U64),
Tag::I64Object => Some(ScValType::I64),
Tag::TimepointObject => Some(ScValType::Timepoint),
Tag::DurationObject => Some(ScValType::Duration),
Tag::U128Object => Some(ScValType::U128),
Tag::I128Object => Some(ScValType::I128),
Tag::U256Object => Some(ScValType::U256),
Tag::I256Object => Some(ScValType::I256),
Tag::BytesObject => Some(ScValType::Bytes),
Tag::StringObject => Some(ScValType::String),
Tag::SymbolObject => Some(ScValType::Symbol),
Tag::VecObject => Some(ScValType::Vec),
Tag::MapObject => Some(ScValType::Map),
Tag::AddressObject => Some(ScValType::Address),
Tag::ObjectCodeUpperBound => None,
Tag::Bad => None,
}
}
}
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct Val(u64);
impl Default for Val {
fn default() -> Self {
Self::from_void().into()
}
}
impl AsRef<Val> for Val {
fn as_ref(&self) -> &Val {
self
}
}
impl AsMut<Val> for Val {
fn as_mut(&mut self) -> &mut Val {
self
}
}
impl<E: Env> TryFromVal<E, Val> for Val {
type Error = ConversionError;
fn try_from_val(_env: &E, val: &Val) -> Result<Self, Self::Error> {
Ok(*val)
}
}
impl<E: Env> TryFromVal<E, &Val> for Val {
type Error = ConversionError;
fn try_from_val(_env: &E, val: &&Val) -> Result<Self, Self::Error> {
Ok(**val)
}
}
declare_tag_based_wrapper!(Void);
impl From<()> for Void {
fn from(_value: ()) -> Self {
Val::VOID
}
}
impl<E: Env> Compare<Void> for E {
type Error = E::Error;
fn compare(&self, _a: &Void, _b: &Void) -> Result<Ordering, Self::Error> {
Ok(Ordering::Equal)
}
}
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct Bool(Val);
impl_val_wrapper_base!(Bool);
impl From<bool> for Bool {
fn from(value: bool) -> Self {
Val::from_bool(value)
}
}
impl From<Bool> for bool {
fn from(value: Bool) -> Self {
value.0.is_true()
}
}
impl ValConvert for Bool {
fn is_val_type(v: Val) -> bool {
v.is_true() || v.is_false()
}
unsafe fn unchecked_from_val(v: Val) -> Self {
Self(v)
}
}
impl<E: Env> Compare<Bool> for E {
type Error = E::Error;
fn compare(&self, a: &Bool, b: &Bool) -> Result<Ordering, Self::Error> {
let a: bool = (*a).into();
let b: bool = (*b).into();
Ok(a.cmp(&b))
}
}
declare_tag_based_object_wrapper!(VecObject);
declare_tag_based_object_wrapper!(MapObject);
declare_tag_based_object_wrapper!(AddressObject);
#[derive(Debug, Eq, PartialEq)]
pub struct ConversionError;
impl From<Infallible> for ConversionError {
fn from(_: Infallible) -> Self {
unreachable!()
}
}
impl From<crate::xdr::Error> for ConversionError {
fn from(_: crate::xdr::Error) -> Self {
ConversionError
}
}
impl From<crate::Error> for ConversionError {
fn from(_: crate::Error) -> Self {
ConversionError
}
}
pub(crate) trait ValConvert: Into<Val> + TryFrom<Val> {
fn is_val_type(v: Val) -> bool;
unsafe fn unchecked_from_val(v: Val) -> Self;
#[inline(always)]
fn try_convert(v: Val) -> Option<Self> {
if Self::is_val_type(v) {
Some(unsafe { Self::unchecked_from_val(v) })
} else {
None
}
}
}
impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(());
impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(bool);
impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(u32);
impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(i32);
impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(Error);
#[cfg(feature = "wasmi")]
pub trait WasmiMarshal: Sized {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self>;
fn marshal_from_self(self) -> wasmi::Value;
}
#[cfg(feature = "wasmi")]
impl WasmiMarshal for Val {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
if let wasmi::Value::I64(i) = v {
let v = Val::from_payload(i as u64);
if v.is_good() {
Some(v)
} else {
None
}
} else {
None
}
}
fn marshal_from_self(self) -> wasmi::Value {
wasmi::Value::I64(self.get_payload() as i64)
}
}
#[cfg(feature = "wasmi")]
impl WasmiMarshal for u64 {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
if let wasmi::Value::I64(i) = v {
Some(i as u64)
} else {
None
}
}
fn marshal_from_self(self) -> wasmi::Value {
wasmi::Value::I64(self as i64)
}
}
#[cfg(feature = "wasmi")]
impl WasmiMarshal for i64 {
fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
if let wasmi::Value::I64(i) = v {
Some(i)
} else {
None
}
}
fn marshal_from_self(self) -> wasmi::Value {
wasmi::Value::I64(self)
}
}
impl ValConvert for () {
#[inline(always)]
fn is_val_type(v: Val) -> bool {
v.has_tag(Tag::Void)
}
#[inline(always)]
unsafe fn unchecked_from_val(_v: Val) -> Self {}
}
impl ValConvert for bool {
#[inline(always)]
fn is_val_type(v: Val) -> bool {
v.has_tag(Tag::True) || v.has_tag(Tag::False)
}
#[inline(always)]
unsafe fn unchecked_from_val(v: Val) -> Self {
v.has_tag(Tag::True)
}
#[inline(always)]
fn try_convert(v: Val) -> Option<Self> {
if v.has_tag(Tag::True) {
Some(true)
} else if v.has_tag(Tag::False) {
Some(false)
} else {
None
}
}
}
impl ValConvert for u32 {
#[inline(always)]
fn is_val_type(v: Val) -> bool {
v.has_tag(Tag::U32Val)
}
#[inline(always)]
unsafe fn unchecked_from_val(v: Val) -> Self {
v.get_major()
}
}
impl ValConvert for i32 {
#[inline(always)]
fn is_val_type(v: Val) -> bool {
v.has_tag(Tag::I32Val)
}
#[inline(always)]
unsafe fn unchecked_from_val(v: Val) -> Self {
v.get_major() as i32
}
}
impl From<bool> for Val {
#[inline(always)]
fn from(b: bool) -> Self {
Val::from_bool(b).into()
}
}
impl From<()> for Val {
#[inline(always)]
fn from(_: ()) -> Self {
Val::from_void().into()
}
}
impl From<&()> for Val {
#[inline(always)]
fn from(_: &()) -> Self {
Val::from_void().into()
}
}
impl From<u32> for Val {
#[inline(always)]
fn from(u: u32) -> Self {
Val::from_u32(u).into()
}
}
impl From<&u32> for Val {
#[inline(always)]
fn from(u: &u32) -> Self {
Val::from_u32(*u).into()
}
}
impl From<i32> for Val {
#[inline(always)]
fn from(i: i32) -> Self {
Val::from_i32(i).into()
}
}
impl From<&i32> for Val {
#[inline(always)]
fn from(i: &i32) -> Self {
Val::from_i32(*i).into()
}
}
impl From<ScError> for Val {
fn from(er: ScError) -> Self {
let e: Error = er.into();
e.to_val()
}
}
impl From<&ScError> for Val {
fn from(er: &ScError) -> Self {
let e: Error = er.clone().into();
e.to_val()
}
}
impl Val {
pub const fn can_represent_scval_type(scv_ty: ScValType) -> bool {
match scv_ty {
ScValType::Bool
| ScValType::Void
| ScValType::Error
| ScValType::U32
| ScValType::I32
| ScValType::U64
| ScValType::I64
| ScValType::Timepoint
| ScValType::Duration
| ScValType::U128
| ScValType::I128
| ScValType::U256
| ScValType::I256
| ScValType::Bytes
| ScValType::String
| ScValType::Symbol
| ScValType::Vec
| ScValType::Map
| ScValType::Address => true,
ScValType::ContractInstance
| ScValType::LedgerKeyContractInstance
| ScValType::LedgerKeyNonce => false,
}
}
pub fn can_represent_scval(scv: &ScVal) -> bool {
match scv {
ScVal::Vec(None) => return false,
ScVal::Map(None) => return false,
_ => Self::can_represent_scval_type(scv.discriminant()),
}
}
pub fn can_represent_scval_recursive(scv: &ScVal) -> bool {
match scv {
ScVal::Vec(None) => return false,
ScVal::Map(None) => return false,
ScVal::Vec(Some(v)) => {
return v.0.iter().all(|x| Val::can_represent_scval_recursive(x))
}
ScVal::Map(Some(m)) => {
return m.0.iter().all(|e| {
Val::can_represent_scval_recursive(&e.key)
&& Val::can_represent_scval_recursive(&e.val)
})
}
_ => Self::can_represent_scval_type(scv.discriminant()),
}
}
pub fn is_good(self) -> bool {
match self.get_tag() {
Tag::Bad
| Tag::SmallCodeUpperBound
| Tag::ObjectCodeLowerBound
| Tag::ObjectCodeUpperBound => false,
Tag::True | Tag::False | Tag::Void => self.has_body(0),
Tag::I32Val | Tag::U32Val => self.has_minor(0),
Tag::Error => ScError::try_from(unsafe { Error::unchecked_from_val(self) }).is_ok(),
Tag::SymbolSmall => SymbolSmall::try_from_body(self.get_body()).is_ok(),
Tag::U64Small
| Tag::I64Small
| Tag::TimepointSmall
| Tag::DurationSmall
| Tag::U128Small
| Tag::I128Small
| Tag::U256Small
| Tag::I256Small => true,
Tag::U64Object
| Tag::I64Object
| Tag::TimepointObject
| Tag::DurationObject
| Tag::U128Object
| Tag::I128Object
| Tag::U256Object
| Tag::I256Object
| Tag::BytesObject
| Tag::StringObject
| Tag::SymbolObject
| Tag::VecObject
| Tag::MapObject
| Tag::AddressObject => self.has_minor(0),
}
}
#[inline(always)]
pub const fn get_payload(self) -> u64 {
self.0
}
#[inline(always)]
pub const fn from_payload(x: u64) -> Self {
Self(x)
}
#[inline(always)]
pub const fn shallow_eq(&self, other: &Self) -> bool {
self.0 == other.0
}
#[inline(always)]
const fn get_tag_u8(self) -> u8 {
(self.0 & TAG_MASK) as u8
}
#[inline(always)]
pub const fn get_tag(self) -> Tag {
let tag = self.get_tag_u8();
Tag::from_u8(tag)
}
#[inline(always)]
pub(crate) const fn get_body(self) -> u64 {
self.0 >> TAG_BITS
}
#[inline(always)]
pub(crate) const fn has_body(self, body: u64) -> bool {
self.get_body() == body
}
#[inline(always)]
pub(crate) const fn get_signed_body(self) -> i64 {
(self.0 as i64) >> TAG_BITS
}
#[inline(always)]
pub(crate) const fn has_tag(self, tag: Tag) -> bool {
self.get_tag_u8() == tag as u8
}
#[inline(always)]
pub(crate) const unsafe fn from_body_and_tag(body: u64, tag: Tag) -> Val {
Val((body << TAG_BITS) | (tag as u64))
}
#[inline(always)]
pub(crate) const unsafe fn from_major_minor_and_tag(major: u32, minor: u32, tag: Tag) -> Val {
let major = major as u64;
let minor = minor as u64;
Self::from_body_and_tag((major << MINOR_BITS) | minor, tag)
}
#[inline(always)]
pub(crate) const fn has_minor(self, minor: u32) -> bool {
self.get_minor() == minor
}
#[inline(always)]
pub(crate) const fn has_major(self, major: u32) -> bool {
self.get_major() == major
}
#[inline(always)]
pub(crate) const fn get_minor(self) -> u32 {
(self.get_body() & MINOR_MASK) as u32
}
#[inline(always)]
pub(crate) const fn get_major(self) -> u32 {
(self.get_body() >> MINOR_BITS) as u32
}
#[inline(always)]
pub const fn is_object(self) -> bool {
Tag::u8_is_object(self.get_tag_u8())
}
#[inline(always)]
pub const fn from_void() -> Void {
unsafe { Void(Val::from_body_and_tag(0, Tag::Void)) }
}
#[inline(always)]
pub const fn from_bool(b: bool) -> Bool {
let tag = if b { Tag::True } else { Tag::False };
unsafe { Bool(Val::from_body_and_tag(0, tag)) }
}
#[inline(always)]
pub const fn is_void(self) -> bool {
self.shallow_eq(&Self::VOID.0)
}
#[inline(always)]
pub const fn is_true(self) -> bool {
self.shallow_eq(&Self::TRUE.0)
}
#[inline(always)]
pub const fn is_false(self) -> bool {
self.shallow_eq(&Self::FALSE.0)
}
}
impl Val {
pub const I32_ZERO: I32Val = Val::from_i32(0);
pub const I32_MIN: I32Val = Val::from_i32(i32::MIN);
pub const I32_MAX: I32Val = Val::from_i32(i32::MAX);
pub const U32_ZERO: U32Val = Val::from_u32(0);
pub const U32_ONE: U32Val = Val::from_u32(1);
pub const U32_MIN: U32Val = Val::from_u32(u32::MIN);
pub const U32_MAX: U32Val = Val::from_u32(u32::MAX);
pub const VOID: Void = Val::from_void();
pub const TRUE: Bool = Val::from_bool(true);
pub const FALSE: Bool = Val::from_bool(false);
}
impl Debug for Val {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
fn fmt_obj(name: &str, r: &Val, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}(obj#{})", name, r.get_major())
}
match self.get_tag() {
Tag::U32Val => write!(f, "U32({})", self.get_major()),
Tag::I32Val => write!(f, "I32({})", self.get_major() as i32),
Tag::False => write!(f, "False"),
Tag::True => write!(f, "True"),
Tag::Void => write!(f, "Void"),
Tag::Error => unsafe { <Error as ValConvert>::unchecked_from_val(*self) }.fmt(f),
Tag::U64Small => write!(f, "U64({})", self.get_body()),
Tag::I64Small => write!(f, "I64({})", self.get_signed_body()),
Tag::TimepointSmall => write!(f, "Timepoint({})", self.get_body()),
Tag::DurationSmall => write!(f, "Duration({})", self.get_body()),
Tag::U128Small => write!(f, "U128({})", self.get_body()),
Tag::I128Small => write!(f, "I128({})", { self.get_signed_body() }),
Tag::U256Small => write!(f, "U256({})", self.get_body()),
Tag::I256Small => write!(f, "I256({})", { self.get_signed_body() }),
Tag::SymbolSmall => {
let ss: SymbolStr =
unsafe { <SymbolSmall as ValConvert>::unchecked_from_val(*self) }.into();
let s: &str = ss.as_ref();
write!(f, "Symbol({})", s)
}
Tag::U64Object => fmt_obj("U64", self, f),
Tag::I64Object => fmt_obj("I64", self, f),
Tag::TimepointObject => fmt_obj("Timepoint", self, f),
Tag::DurationObject => fmt_obj("Duration", self, f),
Tag::U128Object => fmt_obj("U128", self, f),
Tag::I128Object => fmt_obj("I128", self, f),
Tag::U256Object => fmt_obj("U256", self, f),
Tag::I256Object => fmt_obj("I256", self, f),
Tag::BytesObject => fmt_obj("Bytes", self, f),
Tag::StringObject => fmt_obj("String", self, f),
Tag::SymbolObject => fmt_obj("Symbol", self, f),
Tag::VecObject => fmt_obj("Vec", self, f),
Tag::MapObject => fmt_obj("Map", self, f),
Tag::AddressObject => fmt_obj("Address", self, f),
Tag::Bad
| Tag::SmallCodeUpperBound
| Tag::ObjectCodeLowerBound
| Tag::ObjectCodeUpperBound => {
write!(
f,
"Bad(tag={:x},body={:x})",
self.get_tag_u8(),
self.get_body()
)
}
}
}
}
#[test]
#[cfg(feature = "std")]
fn test_debug() {
use super::{Error, Object, SymbolSmall};
use crate::{
xdr::{ScError, ScErrorCode},
I64Small, U64Small,
};
assert_eq!(format!("{:?}", Val::from_void()), "Void");
assert_eq!(format!("{:?}", Val::from_bool(true)), "True");
assert_eq!(format!("{:?}", Val::from_bool(false)), "False");
assert_eq!(format!("{:?}", Val::from_i32(10)), "I32(10)");
assert_eq!(format!("{:?}", Val::from_i32(-10)), "I32(-10)");
assert_eq!(format!("{:?}", Val::from_u32(10)), "U32(10)");
assert_eq!(format!("{:?}", I64Small::try_from(10).unwrap()), "I64(10)");
assert_eq!(
format!("{:?}", I64Small::try_from(-10).unwrap()),
"I64(-10)"
);
assert_eq!(format!("{:?}", U64Small::try_from(10).unwrap()), "U64(10)");
assert_eq!(
format!("{:?}", SymbolSmall::try_from_str("hello").unwrap()),
"Symbol(hello)"
);
assert_eq!(
format!("{:?}", Object::from_handle_and_tag(7, Tag::VecObject)),
"Vec(obj#7)"
);
assert_eq!(
format!(
"{:?}",
Error::from_scerror(ScError::Value(ScErrorCode::InvalidInput))
),
"Error(Value, InvalidInput)"
);
}
#[test]
fn test_tag_from_u8() {
use num_enum::TryFromPrimitive;
for i in 0_u8..=255 {
let expected_tag = Tag::try_from_primitive(i);
let actual_tag = Tag::from_u8(i);
match expected_tag {
Ok(
Tag::SmallCodeUpperBound | Tag::ObjectCodeLowerBound | Tag::ObjectCodeUpperBound,
) => {
assert_eq!(actual_tag, Tag::Bad);
}
Ok(expected_tag) => {
assert_eq!(expected_tag, actual_tag);
let i_again = actual_tag as u8;
assert_eq!(i, i_again);
}
Err(_) => {
assert_eq!(actual_tag, Tag::Bad);
}
}
}
}