soroban_env_common/
val.rs

1// This permits globals prouced by derive(num_enum::TryFromPrimitive) below.
2#![cfg_attr(test, allow(non_upper_case_globals))]
3
4use crate::xdr::{ScError, ScVal, ScValType};
5use crate::{
6    declare_tag_based_object_wrapper, declare_tag_based_wrapper,
7    impl_tryfroms_and_tryfromvals_delegating_to_valconvert, impl_val_wrapper_base, Compare, I32Val,
8    SymbolSmall, SymbolStr, U32Val,
9};
10
11use super::{Env, Error, TryFromVal};
12use core::{cmp::Ordering, convert::Infallible, fmt::Debug, str};
13
14extern crate static_assertions as sa;
15
16// A Soroban Val is a 64-bit word with the low 8 bits assigned to a `tag`
17// indicating its type and the high 56 bits reserved for its `body`
18
19#[allow(dead_code)]
20const WORD_BITS: usize = 64;
21pub(crate) const TAG_BITS: usize = 8;
22const TAG_MASK: u64 = (1u64 << TAG_BITS) - 1;
23sa::const_assert!(TAG_MASK == 0xff);
24
25#[allow(dead_code)]
26pub(crate) const BODY_BITS: usize = WORD_BITS - TAG_BITS;
27sa::const_assert!(BODY_BITS == 56);
28
29// The body is sometimes further subdivided into two fields:
30// a 32-bit `major` part and a 24-bit `minor` part.
31
32#[allow(dead_code)]
33const MAJOR_BITS: usize = 32;
34const MINOR_BITS: usize = 24;
35#[allow(dead_code)]
36const MAJOR_MASK: u64 = (1u64 << MAJOR_BITS) - 1;
37const MINOR_MASK: u64 = (1u64 << MINOR_BITS) - 1;
38sa::const_assert!(MAJOR_MASK == 0xffff_ffff);
39sa::const_assert!(MINOR_MASK == 0x00ff_ffff);
40sa::const_assert!(MAJOR_BITS + MINOR_BITS == BODY_BITS);
41
42/// Code values for the 8 `tag` bits in the bit-packed representation
43/// of [Val]. These don't coincide with tag numbers in the SCVal XDR
44/// but cover all those cases as well as some optimized refinements for
45/// special cases (boolean true and false, small-value forms).
46#[repr(u8)]
47#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
48#[cfg_attr(test, derive(num_enum::TryFromPrimitive))]
49pub enum Tag {
50    /// Tag for a [Val] that encodes [bool] `false`. The bool type is refined to
51    /// two single-value subtypes in order for each tag number to coincide with
52    /// the WASM encoding of a boolean.
53    False = 0,
54
55    /// Tag for a [Val] that encodes [bool] `true`.
56    True = 1,
57
58    /// Tag for a [Val] that is empty/absent (eg. void, null, nil, undefined, None)
59    Void = 2,
60
61    /// Tag for a [Val] that is contains an error code.
62    Error = 3,
63
64    /// Tag for a [Val] that contains a [u32] number.
65    U32Val = 4,
66
67    /// Tag for a [Val] that contains an [i32] number.
68    I32Val = 5,
69
70    /// Tag for a [Val] that contains a [u64] small enough to fit in 56 bits.
71    U64Small = 6,
72
73    /// Tag for a [Val] that contains an [i64] small enough to fit in 56 bits.
74    I64Small = 7,
75
76    /// Tag for a [Val] that contains a [u64] timepoint small enough to fit
77    /// in 56 bits.
78    TimepointSmall = 8,
79
80    /// Tag for a [Val] that contains a [u64] duration small enough to fit in
81    /// 56 bits.
82    DurationSmall = 9,
83
84    /// Tag for a [Val] that contains a [u128] small enough to fit in 56 bits.
85    U128Small = 10,
86
87    /// Tag for a [Val] that contains an [i128] small enough to fit in 56 bits.
88    I128Small = 11,
89
90    /// Tag for a [Val] that contains a [u256](ethnum::u256) small enough to fit in 56 bits.
91    U256Small = 12,
92
93    /// Tag for a [Val] that contains an [i256](ethnum::i256) small enough to fit in 56 bits.
94    I256Small = 13,
95
96    /// Tag for a [Val] that contains up to 9 character symbols.
97    SymbolSmall = 14,
98
99    /// Code delimiting the upper boundary of "small" types.
100    SmallCodeUpperBound = 15,
101
102    /// Tag reserved to indicate boundary between tags for "small" types with
103    /// their payload packed into the remaining 56 bits of the [Val] and
104    /// "object" types that are stored as host objects and referenced by
105    /// [Object](crate::Object) handle.
106    ObjectCodeLowerBound = 63,
107
108    /// Tag for a [Val] that refers to a host-side [u64] number.
109    U64Object = 64,
110
111    /// Tag for a [Val] that refers to a host-side [i64] number.
112    I64Object = 65,
113
114    /// Tag for a [Val] that refers to a host-side [u64] number encoding a
115    /// time-point (a count of seconds since the Unix epoch, Jan 1 1970 UTC).
116    TimepointObject = 66,
117
118    /// Tag for a [Val] that refers to a host-side [i64] number encoding a
119    /// duration (a count of seconds).
120    DurationObject = 67,
121
122    /// Tag for a [Val] that refers to a host-side [u128] number.
123    U128Object = 68,
124
125    /// Tag for a [Val] that refers to a host-side [i128] number.
126    I128Object = 69,
127
128    /// Tag for a [Val] that refers to a host-side [u256](ethnum::u256) number.
129    U256Object = 70,
130
131    /// Tag for a [Val] that refers to a host-side [i256](ethnum::i256) number.
132    I256Object = 71,
133
134    /// Tag for a [Val] that refers to a host-side byte array.
135    BytesObject = 72,
136
137    /// Tag for a [Val] that refers to a host-side string.
138    StringObject = 73,
139
140    /// Tag for a [Val] that refers to a host-side symbol (see [`Symbol`](crate::Symbol)).
141    SymbolObject = 74,
142
143    /// Tag for a [Val] that refers to a host-side vector of [Val]s.
144    VecObject = 75,
145
146    /// Tag for a [Val] that refers to a host-side map from [Val]s to [Val]s.
147    MapObject = 76,
148
149    /// Tag for a [Val] that refers to a host-side contract address.
150    AddressObject = 77,
151
152    /// Code delimiting the upper boundary of "object" types.
153    ObjectCodeUpperBound = 78,
154
155    /// Code reserved to indicate mis-tagged [`Val`]s.
156    Bad = 0x7f,
157}
158
159impl Tag {
160    #[inline(always)]
161    pub const fn val_mask() -> i64 {
162        TAG_MASK as i64
163    }
164
165    #[inline(always)]
166    pub fn val_const(&self) -> i64 {
167        *self as i64
168    }
169
170    #[inline(always)]
171    pub(crate) const fn u8_is_object(x: u8) -> bool {
172        x > (Tag::ObjectCodeLowerBound as u8) && x < (Tag::ObjectCodeUpperBound as u8)
173    }
174
175    #[inline(always)]
176    pub const fn is_object(self) -> bool {
177        Self::u8_is_object(self as u8)
178    }
179
180    #[inline(always)]
181    pub const fn from_u8(tag: u8) -> Tag {
182        const A: u8 = Tag::SmallCodeUpperBound as u8;
183        const B: u8 = Tag::ObjectCodeLowerBound as u8;
184        const C: u8 = Tag::ObjectCodeUpperBound as u8;
185        if !((tag < A) || (B < tag && tag < C)) {
186            return Tag::Bad;
187        }
188
189        // Transmuting an integer to an enum is UB if outside the defined enum
190        // value set, so we need to test above to be safe. Note that it's ok for
191        // this to be a _little_ slow since it's not called in a lot
192        // of small/tight paths, only when doing a switch-based comparison. Most
193        // small paths call `has_tag` which tests a _known_ enum case against
194        // the tag byte, and therefore doesn't need the range check.
195        //
196        // The `test_tag_from_u8` test should ensure this cast is correct.
197        unsafe { ::core::mem::transmute(tag) }
198    }
199
200    /// Get the ScValType of the XDR type that corresponds to this tag.
201    ///
202    /// For use in the `Host::obj_cmp` comparison function so that comparison
203    /// based on tags can be done identically to the `ScVal` type.
204    ///
205    /// Returns `None` for `Tag::Bad`, and for the three marker tags
206    /// `SmallCodeUpperBound`, `ObjectCodeLowerBound`, `ObjectCodeUpperBound`.
207    #[inline(always)]
208    pub const fn get_scval_type(&self) -> Option<ScValType> {
209        match *self {
210            Tag::False => Some(ScValType::Bool),
211            Tag::True => Some(ScValType::Bool),
212            Tag::Void => Some(ScValType::Void),
213            Tag::Error => Some(ScValType::Error),
214            Tag::U32Val => Some(ScValType::U32),
215            Tag::I32Val => Some(ScValType::I32),
216            Tag::U64Small => Some(ScValType::U64),
217            Tag::I64Small => Some(ScValType::I64),
218            Tag::TimepointSmall => Some(ScValType::Timepoint),
219            Tag::DurationSmall => Some(ScValType::Duration),
220            Tag::U128Small => Some(ScValType::U128),
221            Tag::I128Small => Some(ScValType::I128),
222            Tag::U256Small => Some(ScValType::U256),
223            Tag::I256Small => Some(ScValType::I256),
224            Tag::SymbolSmall => Some(ScValType::Symbol),
225            Tag::SmallCodeUpperBound => None,
226            Tag::ObjectCodeLowerBound => None,
227            Tag::U64Object => Some(ScValType::U64),
228            Tag::I64Object => Some(ScValType::I64),
229            Tag::TimepointObject => Some(ScValType::Timepoint),
230            Tag::DurationObject => Some(ScValType::Duration),
231            Tag::U128Object => Some(ScValType::U128),
232            Tag::I128Object => Some(ScValType::I128),
233            Tag::U256Object => Some(ScValType::U256),
234            Tag::I256Object => Some(ScValType::I256),
235            Tag::BytesObject => Some(ScValType::Bytes),
236            Tag::StringObject => Some(ScValType::String),
237            Tag::SymbolObject => Some(ScValType::Symbol),
238            Tag::VecObject => Some(ScValType::Vec),
239            Tag::MapObject => Some(ScValType::Map),
240            Tag::AddressObject => Some(ScValType::Address),
241            Tag::ObjectCodeUpperBound => None,
242            Tag::Bad => None,
243        }
244    }
245}
246
247#[repr(transparent)]
248#[derive(Copy, Clone)]
249pub struct Val(u64);
250
251impl Default for Val {
252    fn default() -> Self {
253        Self::from_void().into()
254    }
255}
256
257// Impl AsRef/AsMut and TryFromVal<Val> so that clients can abstract over a
258// wrapper-or-Val because all wrappers also impl these.
259impl AsRef<Val> for Val {
260    fn as_ref(&self) -> &Val {
261        self
262    }
263}
264
265impl AsMut<Val> for Val {
266    fn as_mut(&mut self) -> &mut Val {
267        self
268    }
269}
270
271impl<E: Env> TryFromVal<E, Val> for Val {
272    type Error = ConversionError;
273    fn try_from_val(_env: &E, val: &Val) -> Result<Self, Self::Error> {
274        Ok(*val)
275    }
276}
277
278impl<E: Env> TryFromVal<E, &Val> for Val {
279    type Error = ConversionError;
280    fn try_from_val(_env: &E, val: &&Val) -> Result<Self, Self::Error> {
281        Ok(**val)
282    }
283}
284
285// Declare a few extra small-value wrapper types that don't live anywhere else.
286declare_tag_based_wrapper!(Void);
287
288impl From<()> for Void {
289    fn from(_value: ()) -> Self {
290        Val::VOID
291    }
292}
293
294impl<E: Env> Compare<Void> for E {
295    type Error = E::Error;
296    fn compare(&self, _a: &Void, _b: &Void) -> Result<Ordering, Self::Error> {
297        Ok(Ordering::Equal)
298    }
299}
300
301#[repr(transparent)]
302#[derive(Copy, Clone)]
303pub struct Bool(Val);
304impl_val_wrapper_base!(Bool);
305
306impl From<bool> for Bool {
307    fn from(value: bool) -> Self {
308        Val::from_bool(value)
309    }
310}
311impl From<Bool> for bool {
312    fn from(value: Bool) -> Self {
313        value.0.is_true()
314    }
315}
316
317impl ValConvert for Bool {
318    fn is_val_type(v: Val) -> bool {
319        v.is_true() || v.is_false()
320    }
321
322    unsafe fn unchecked_from_val(v: Val) -> Self {
323        Self(v)
324    }
325}
326
327impl<E: Env> Compare<Bool> for E {
328    type Error = E::Error;
329    fn compare(&self, a: &Bool, b: &Bool) -> Result<Ordering, Self::Error> {
330        let a: bool = (*a).into();
331        let b: bool = (*b).into();
332        Ok(a.cmp(&b))
333    }
334}
335
336// Declare a few extra object wrapper types that don't live anywhere else.
337declare_tag_based_object_wrapper!(VecObject);
338declare_tag_based_object_wrapper!(MapObject);
339declare_tag_based_object_wrapper!(AddressObject);
340
341// This is a 0-arg struct rather than an enum to ensure it completely compiles
342// away, the same way `()` would, while remaining a separate type to allow
343// conversion to a more-structured error code at a higher level.
344
345/// Error type indicating a failure to convert some type to another; details of
346/// the failed conversion may be written to the debug log, when possible.
347///
348/// This is intentionally minimal and uninformative to minimize impact of its
349/// use on wasm codesize. It converts to `Error(ScErrorType::Value,
350/// ScErrorCode::UnexpectedType)` when converted to a full `Error`, and ideally
351/// it should only be used in ubiquitous cases that will occur in wasm, like
352/// small-number or tag conversions, where code size is paramount and the
353/// information-loss from using it is not too bad.
354#[derive(Debug, Eq, PartialEq)]
355pub struct ConversionError;
356
357impl From<Infallible> for ConversionError {
358    fn from(_: Infallible) -> Self {
359        unreachable!()
360    }
361}
362
363impl From<crate::xdr::Error> for ConversionError {
364    fn from(_: crate::xdr::Error) -> Self {
365        ConversionError
366    }
367}
368
369impl From<crate::Error> for ConversionError {
370    fn from(_: crate::Error) -> Self {
371        ConversionError
372    }
373}
374
375/// Trait abstracting over types that can be converted into [Val], similar to
376/// [TryFrom] but with a different signature that enables generating slightly
377/// more efficient conversion code.
378pub(crate) trait ValConvert: Into<Val> + TryFrom<Val> {
379    /// Returns `true` if `v` is in a union state compatible with `Self`.
380    fn is_val_type(v: Val) -> bool;
381
382    /// Converts the bits making up a `Val` into `Self` _without_ checking
383    /// that the `Val` is tagged correctly, assuming that such a check has
384    /// been performed elsewhere. It is the caller's responsibility to arrange
385    /// that such checks have occurred before calling `unchecked_from_val`,
386    /// which is why it is marked as `unsafe` (it does not represent a risk of
387    /// memory-unsafety, merely "serious logic errors").
388    unsafe fn unchecked_from_val(v: Val) -> Self;
389
390    /// Attempt a conversion from `Val` to `Self`, returning `None` if the
391    /// provided `Val` is not tagged correctly. By default this calls
392    /// `Self::is_val_type` and `Self::unchecked_from_val`, but it can be
393    /// customized on a type-by-type basis to avoid redundant tag tests and
394    /// produce more efficient code, as it is done for `Static` values like
395    /// `bool`.
396    #[inline(always)]
397    fn try_convert(v: Val) -> Option<Self> {
398        if Self::is_val_type(v) {
399            Some(unsafe { Self::unchecked_from_val(v) })
400        } else {
401            None
402        }
403    }
404}
405
406impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(());
407impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(bool);
408impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(u32);
409impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(i32);
410impl_tryfroms_and_tryfromvals_delegating_to_valconvert!(Error);
411
412#[cfg(feature = "wasmi")]
413pub trait WasmiMarshal: Sized {
414    fn try_marshal_from_value(v: wasmi::Value) -> Option<Self>;
415    fn marshal_from_self(self) -> wasmi::Value;
416}
417
418#[cfg(feature = "wasmi")]
419impl WasmiMarshal for Val {
420    fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
421        if let wasmi::Value::I64(i) = v {
422            let v = Val::from_payload(i as u64);
423            if v.is_good() {
424                Some(v)
425            } else {
426                None
427            }
428        } else {
429            None
430        }
431    }
432
433    fn marshal_from_self(self) -> wasmi::Value {
434        wasmi::Value::I64(self.get_payload() as i64)
435    }
436}
437
438#[cfg(feature = "wasmi")]
439impl WasmiMarshal for u64 {
440    fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
441        if let wasmi::Value::I64(i) = v {
442            Some(i as u64)
443        } else {
444            None
445        }
446    }
447
448    fn marshal_from_self(self) -> wasmi::Value {
449        wasmi::Value::I64(self as i64)
450    }
451}
452
453#[cfg(feature = "wasmi")]
454impl WasmiMarshal for i64 {
455    fn try_marshal_from_value(v: wasmi::Value) -> Option<Self> {
456        if let wasmi::Value::I64(i) = v {
457            Some(i)
458        } else {
459            None
460        }
461    }
462
463    fn marshal_from_self(self) -> wasmi::Value {
464        wasmi::Value::I64(self)
465    }
466}
467
468// Manually implement all the residual pieces: ValConverts
469// and Froms.
470
471impl ValConvert for () {
472    #[inline(always)]
473    fn is_val_type(v: Val) -> bool {
474        v.has_tag(Tag::Void)
475    }
476    #[inline(always)]
477    unsafe fn unchecked_from_val(_v: Val) -> Self {}
478}
479
480impl ValConvert for bool {
481    #[inline(always)]
482    fn is_val_type(v: Val) -> bool {
483        v.has_tag(Tag::True) || v.has_tag(Tag::False)
484    }
485    #[inline(always)]
486    unsafe fn unchecked_from_val(v: Val) -> Self {
487        v.has_tag(Tag::True)
488    }
489    #[inline(always)]
490    fn try_convert(v: Val) -> Option<Self> {
491        if v.has_tag(Tag::True) {
492            Some(true)
493        } else if v.has_tag(Tag::False) {
494            Some(false)
495        } else {
496            None
497        }
498    }
499}
500
501impl ValConvert for u32 {
502    #[inline(always)]
503    fn is_val_type(v: Val) -> bool {
504        v.has_tag(Tag::U32Val)
505    }
506    #[inline(always)]
507    unsafe fn unchecked_from_val(v: Val) -> Self {
508        v.get_major()
509    }
510}
511
512impl ValConvert for i32 {
513    #[inline(always)]
514    fn is_val_type(v: Val) -> bool {
515        v.has_tag(Tag::I32Val)
516    }
517    #[inline(always)]
518    unsafe fn unchecked_from_val(v: Val) -> Self {
519        v.get_major() as i32
520    }
521}
522
523impl From<bool> for Val {
524    #[inline(always)]
525    fn from(b: bool) -> Self {
526        Val::from_bool(b).into()
527    }
528}
529
530impl From<()> for Val {
531    #[inline(always)]
532    fn from(_: ()) -> Self {
533        Val::from_void().into()
534    }
535}
536
537impl From<&()> for Val {
538    #[inline(always)]
539    fn from(_: &()) -> Self {
540        Val::from_void().into()
541    }
542}
543
544impl From<u32> for Val {
545    #[inline(always)]
546    fn from(u: u32) -> Self {
547        Val::from_u32(u).into()
548    }
549}
550
551impl From<&u32> for Val {
552    #[inline(always)]
553    fn from(u: &u32) -> Self {
554        Val::from_u32(*u).into()
555    }
556}
557
558impl From<i32> for Val {
559    #[inline(always)]
560    fn from(i: i32) -> Self {
561        Val::from_i32(i).into()
562    }
563}
564
565impl From<&i32> for Val {
566    #[inline(always)]
567    fn from(i: &i32) -> Self {
568        Val::from_i32(*i).into()
569    }
570}
571
572impl From<ScError> for Val {
573    fn from(er: ScError) -> Self {
574        let e: Error = er.into();
575        e.to_val()
576    }
577}
578
579impl From<&ScError> for Val {
580    fn from(er: &ScError) -> Self {
581        let e: Error = er.clone().into();
582        e.to_val()
583    }
584}
585
586// Utility methods
587
588impl Val {
589    /// Some ScVals are not representable as Vals at all,
590    /// and only exist in the XDR to serve as special storage
591    /// system key or value payloads managed by the Host.
592    pub const fn can_represent_scval_type(scv_ty: ScValType) -> bool {
593        match scv_ty {
594            ScValType::Bool
595            | ScValType::Void
596            | ScValType::Error
597            | ScValType::U32
598            | ScValType::I32
599            | ScValType::U64
600            | ScValType::I64
601            | ScValType::Timepoint
602            | ScValType::Duration
603            | ScValType::U128
604            | ScValType::I128
605            | ScValType::U256
606            | ScValType::I256
607            | ScValType::Bytes
608            | ScValType::String
609            | ScValType::Symbol
610            | ScValType::Vec
611            | ScValType::Map
612            | ScValType::Address => true,
613            ScValType::ContractInstance
614            | ScValType::LedgerKeyContractInstance
615            | ScValType::LedgerKeyNonce => false,
616        }
617    }
618
619    /// *Non-recursively* checks whether `ScVal` can be represented as `Val`.
620    /// Since conversions from `ScVal` are recursive themselves, this should
621    /// be called at every recursion level during conversion.
622    pub fn can_represent_scval(scv: &ScVal) -> bool {
623        match scv {
624            // Map/vec can't be validated just based on the discriminant,
625            // as their contents can't be `None`.
626            ScVal::Vec(None) => return false,
627            ScVal::Map(None) => return false,
628            _ => Self::can_represent_scval_type(scv.discriminant()),
629        }
630    }
631
632    /// *Recursively* checks whether `ScVal` can be represented as `Val`.
633    /// This should only be used once per top-level `ScVal`.
634    pub fn can_represent_scval_recursive(scv: &ScVal) -> bool {
635        match scv {
636            // Handle recursive types first
637            ScVal::Vec(None) => return false,
638            ScVal::Map(None) => return false,
639            ScVal::Vec(Some(v)) => {
640                return v.0.iter().all(|x| Val::can_represent_scval_recursive(x))
641            }
642            ScVal::Map(Some(m)) => {
643                return m.0.iter().all(|e| {
644                    Val::can_represent_scval_recursive(&e.key)
645                        && Val::can_represent_scval_recursive(&e.val)
646                })
647            }
648            _ => Self::can_represent_scval_type(scv.discriminant()),
649        }
650    }
651
652    /// We define a "good" Val as one that has one of the allowed tag values,
653    /// all the defined body-bits for its case set to valid values, and all the
654    /// undefined body-bits set to zero.
655    pub fn is_good(self) -> bool {
656        match self.get_tag() {
657            // Technically Tag::Bad is the only one that can occur here -- the other
658            // 3 are mapped to it -- but we check for them just in case.
659            Tag::Bad
660            | Tag::SmallCodeUpperBound
661            | Tag::ObjectCodeLowerBound
662            | Tag::ObjectCodeUpperBound => false,
663            Tag::True | Tag::False | Tag::Void => self.has_body(0),
664            Tag::I32Val | Tag::U32Val => self.has_minor(0),
665            Tag::Error => ScError::try_from(unsafe { Error::unchecked_from_val(self) }).is_ok(),
666            Tag::SymbolSmall => SymbolSmall::try_from_body(self.get_body()).is_ok(),
667            Tag::U64Small
668            | Tag::I64Small
669            | Tag::TimepointSmall
670            | Tag::DurationSmall
671            | Tag::U128Small
672            | Tag::I128Small
673            | Tag::U256Small
674            | Tag::I256Small => true,
675            Tag::U64Object
676            | Tag::I64Object
677            | Tag::TimepointObject
678            | Tag::DurationObject
679            | Tag::U128Object
680            | Tag::I128Object
681            | Tag::U256Object
682            | Tag::I256Object
683            | Tag::BytesObject
684            | Tag::StringObject
685            | Tag::SymbolObject
686            | Tag::VecObject
687            | Tag::MapObject
688            | Tag::AddressObject => self.has_minor(0),
689        }
690    }
691
692    #[inline(always)]
693    pub const fn get_payload(self) -> u64 {
694        self.0
695    }
696
697    #[inline(always)]
698    pub const fn from_payload(x: u64) -> Self {
699        Self(x)
700    }
701
702    #[inline(always)]
703    pub const fn shallow_eq(&self, other: &Self) -> bool {
704        self.0 == other.0
705    }
706
707    #[inline(always)]
708    const fn get_tag_u8(self) -> u8 {
709        (self.0 & TAG_MASK) as u8
710    }
711
712    #[inline(always)]
713    pub const fn get_tag(self) -> Tag {
714        let tag = self.get_tag_u8();
715        Tag::from_u8(tag)
716    }
717
718    #[inline(always)]
719    pub(crate) const fn get_body(self) -> u64 {
720        self.0 >> TAG_BITS
721    }
722
723    #[inline(always)]
724    pub(crate) const fn has_body(self, body: u64) -> bool {
725        self.get_body() == body
726    }
727
728    #[inline(always)]
729    pub(crate) const fn get_signed_body(self) -> i64 {
730        (self.0 as i64) >> TAG_BITS
731    }
732
733    #[inline(always)]
734    pub(crate) const fn has_tag(self, tag: Tag) -> bool {
735        self.get_tag_u8() == tag as u8
736    }
737
738    #[inline(always)]
739    // This does no checking, so it can be used in const fns
740    // below; it should not be made public.
741    pub(crate) const unsafe fn from_body_and_tag(body: u64, tag: Tag) -> Val {
742        Val((body << TAG_BITS) | (tag as u64))
743    }
744
745    #[inline(always)]
746    // This also does not checking, is a crate-local helper.
747    pub(crate) const unsafe fn from_major_minor_and_tag(major: u32, minor: u32, tag: Tag) -> Val {
748        let major = major as u64;
749        let minor = minor as u64;
750        Self::from_body_and_tag((major << MINOR_BITS) | minor, tag)
751    }
752
753    #[inline(always)]
754    pub(crate) const fn has_minor(self, minor: u32) -> bool {
755        self.get_minor() == minor
756    }
757
758    #[inline(always)]
759    pub(crate) const fn has_major(self, major: u32) -> bool {
760        self.get_major() == major
761    }
762
763    #[inline(always)]
764    pub(crate) const fn get_minor(self) -> u32 {
765        (self.get_body() & MINOR_MASK) as u32
766    }
767
768    #[inline(always)]
769    pub(crate) const fn get_major(self) -> u32 {
770        (self.get_body() >> MINOR_BITS) as u32
771    }
772
773    #[inline(always)]
774    pub const fn is_object(self) -> bool {
775        Tag::u8_is_object(self.get_tag_u8())
776    }
777
778    #[inline(always)]
779    pub const fn from_void() -> Void {
780        unsafe { Void(Val::from_body_and_tag(0, Tag::Void)) }
781    }
782
783    #[inline(always)]
784    pub const fn from_bool(b: bool) -> Bool {
785        let tag = if b { Tag::True } else { Tag::False };
786        unsafe { Bool(Val::from_body_and_tag(0, tag)) }
787    }
788
789    #[inline(always)]
790    pub const fn is_void(self) -> bool {
791        self.shallow_eq(&Self::VOID.0)
792    }
793
794    #[inline(always)]
795    pub const fn is_true(self) -> bool {
796        self.shallow_eq(&Self::TRUE.0)
797    }
798
799    #[inline(always)]
800    pub const fn is_false(self) -> bool {
801        self.shallow_eq(&Self::FALSE.0)
802    }
803}
804
805impl Val {
806    pub const I32_ZERO: I32Val = Val::from_i32(0);
807    pub const I32_MIN: I32Val = Val::from_i32(i32::MIN);
808    pub const I32_MAX: I32Val = Val::from_i32(i32::MAX);
809
810    pub const U32_ZERO: U32Val = Val::from_u32(0);
811    pub const U32_ONE: U32Val = Val::from_u32(1);
812    pub const U32_MIN: U32Val = Val::from_u32(u32::MIN);
813    pub const U32_MAX: U32Val = Val::from_u32(u32::MAX);
814
815    pub const VOID: Void = Val::from_void();
816
817    pub const TRUE: Bool = Val::from_bool(true);
818    pub const FALSE: Bool = Val::from_bool(false);
819}
820
821impl Debug for Val {
822    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
823        fn fmt_obj(name: &str, r: &Val, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
824            write!(f, "{}(obj#{})", name, r.get_major())
825        }
826
827        match self.get_tag() {
828            Tag::U32Val => write!(f, "U32({})", self.get_major()),
829            Tag::I32Val => write!(f, "I32({})", self.get_major() as i32),
830            Tag::False => write!(f, "False"),
831            Tag::True => write!(f, "True"),
832            Tag::Void => write!(f, "Void"),
833            Tag::Error => unsafe { <Error as ValConvert>::unchecked_from_val(*self) }.fmt(f),
834            Tag::U64Small => write!(f, "U64({})", self.get_body()),
835            Tag::I64Small => write!(f, "I64({})", self.get_signed_body()),
836            Tag::TimepointSmall => write!(f, "Timepoint({})", self.get_body()),
837            Tag::DurationSmall => write!(f, "Duration({})", self.get_body()),
838            // These can't be bigger than u64/i64 so just cast to them.
839            Tag::U128Small => write!(f, "U128({})", self.get_body()),
840            Tag::I128Small => write!(f, "I128({})", { self.get_signed_body() }),
841            // These can't be bigger than u64/i64 so just cast to them.
842            Tag::U256Small => write!(f, "U256({})", self.get_body()),
843            Tag::I256Small => write!(f, "I256({})", { self.get_signed_body() }),
844            Tag::SymbolSmall => {
845                let ss: SymbolStr =
846                    unsafe { <SymbolSmall as ValConvert>::unchecked_from_val(*self) }.into();
847                // Even though this may be called for an arbitrary, not necessarily well-formed
848                // `Val`, this is still safe thanks to `SymbolSmall` iteration implementation that
849                // only returns valid symbol characters or `\0` even for invalid bit
850                // representations.
851                let s: &str = ss.as_ref();
852                write!(f, "Symbol({})", s)
853            }
854
855            Tag::U64Object => fmt_obj("U64", self, f),
856            Tag::I64Object => fmt_obj("I64", self, f),
857            Tag::TimepointObject => fmt_obj("Timepoint", self, f),
858            Tag::DurationObject => fmt_obj("Duration", self, f),
859            Tag::U128Object => fmt_obj("U128", self, f),
860            Tag::I128Object => fmt_obj("I128", self, f),
861            Tag::U256Object => fmt_obj("U256", self, f),
862            Tag::I256Object => fmt_obj("I256", self, f),
863            Tag::BytesObject => fmt_obj("Bytes", self, f),
864            Tag::StringObject => fmt_obj("String", self, f),
865            Tag::SymbolObject => fmt_obj("Symbol", self, f),
866            Tag::VecObject => fmt_obj("Vec", self, f),
867            Tag::MapObject => fmt_obj("Map", self, f),
868            Tag::AddressObject => fmt_obj("Address", self, f),
869
870            Tag::Bad
871            | Tag::SmallCodeUpperBound
872            | Tag::ObjectCodeLowerBound
873            | Tag::ObjectCodeUpperBound => {
874                write!(
875                    f,
876                    "Bad(tag={:x},body={:x})",
877                    self.get_tag_u8(),
878                    self.get_body()
879                )
880            }
881        }
882    }
883}
884
885#[test]
886#[cfg(feature = "std")]
887fn test_debug() {
888    use super::{Error, Object, SymbolSmall};
889    use crate::{
890        xdr::{ScError, ScErrorCode},
891        I64Small, U64Small,
892    };
893    assert_eq!(format!("{:?}", Val::from_void()), "Void");
894    assert_eq!(format!("{:?}", Val::from_bool(true)), "True");
895    assert_eq!(format!("{:?}", Val::from_bool(false)), "False");
896    assert_eq!(format!("{:?}", Val::from_i32(10)), "I32(10)");
897    assert_eq!(format!("{:?}", Val::from_i32(-10)), "I32(-10)");
898    assert_eq!(format!("{:?}", Val::from_u32(10)), "U32(10)");
899    assert_eq!(format!("{:?}", I64Small::try_from(10).unwrap()), "I64(10)");
900    assert_eq!(
901        format!("{:?}", I64Small::try_from(-10).unwrap()),
902        "I64(-10)"
903    );
904    assert_eq!(format!("{:?}", U64Small::try_from(10).unwrap()), "U64(10)");
905    assert_eq!(
906        format!("{:?}", SymbolSmall::try_from_str("hello").unwrap()),
907        "Symbol(hello)"
908    );
909    assert_eq!(
910        format!("{:?}", Object::from_handle_and_tag(7, Tag::VecObject)),
911        "Vec(obj#7)"
912    );
913    assert_eq!(
914        format!(
915            "{:?}",
916            Error::from_scerror(ScError::Value(ScErrorCode::InvalidInput))
917        ),
918        "Error(Value, InvalidInput)"
919    );
920}
921
922// `Tag::from_u8` is implemented by hand unsafely.
923//
924// This test ensures that all cases are correct by comparing to the
925// macro-generated results of the num_enum crate, which is only enabled as a
926// dev-dependency.
927#[test]
928fn test_tag_from_u8() {
929    use num_enum::TryFromPrimitive;
930
931    for i in 0_u8..=255 {
932        let expected_tag = Tag::try_from_primitive(i);
933        let actual_tag = Tag::from_u8(i);
934        match expected_tag {
935            Ok(
936                Tag::SmallCodeUpperBound | Tag::ObjectCodeLowerBound | Tag::ObjectCodeUpperBound,
937            ) => {
938                assert_eq!(actual_tag, Tag::Bad);
939            }
940            Ok(expected_tag) => {
941                assert_eq!(expected_tag, actual_tag);
942                let i_again = actual_tag as u8;
943                assert_eq!(i, i_again);
944            }
945            Err(_) => {
946                assert_eq!(actual_tag, Tag::Bad);
947            }
948        }
949    }
950}