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