soroban_env_common/
object.rs

1use crate::xdr::{Duration, ScVal, TimePoint};
2use crate::{
3    impl_val_wrapper_base, num, val::ValConvert, Compare, Convert, Env, Tag, TryFromVal, Val,
4};
5use core::{cmp::Ordering, fmt::Debug};
6
7/// Wrapper for a [Val] that is tagged with one of the object types,
8/// interpreting the [Val]'s body as containing a 32-bit handle to a host
9/// object.
10#[repr(transparent)]
11#[derive(Copy, Clone)]
12pub struct Object(pub(crate) Val);
13impl_val_wrapper_base!(Object);
14
15impl ValConvert for Object {
16    fn is_val_type(v: Val) -> bool {
17        v.is_object()
18    }
19
20    unsafe fn unchecked_from_val(v: Val) -> Self {
21        Object(v)
22    }
23}
24
25impl Object {
26    #[inline(always)]
27    pub const fn get_handle(&self) -> u32 {
28        self.as_val().get_major()
29    }
30
31    #[inline(always)]
32    pub const fn from_handle_and_tag(handle: u32, tag: Tag) -> Self {
33        debug_assert!(tag.is_object());
34        unsafe { Object(Val::from_major_minor_and_tag(handle, 0, tag)) }
35    }
36}
37
38impl<E: Env> Compare<Object> for E {
39    type Error = E::Error;
40
41    fn compare(&self, a: &Object, b: &Object) -> Result<Ordering, Self::Error> {
42        self.compare(&a.to_val(), &b.to_val())
43    }
44}
45
46/// `ScValObject` (and its reference-based type `ScValObjRef`) is a small
47/// wrapper type that does _not_ have its own XDR definition, it just denotes
48/// (as a type) the subset of `ScVal` values that need to be represented in
49/// `Val` by one of the cases that can be an `Object`. In other words
50/// `Val::try_from_val(&v, e).is_object()` will be true iff
51/// `ScValObject::classify(v)` is `Ok(ScValObject(v))`.
52
53#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
54pub struct ScValObject(ScVal);
55impl From<ScValObject> for ScVal {
56    fn from(value: ScValObject) -> Self {
57        value.0
58    }
59}
60
61impl<E> TryFromVal<E, Object> for ScValObject
62where
63    E: Env + Convert<Object, ScValObject>,
64{
65    type Error = <E as Convert<Object, ScValObject>>::Error;
66    fn try_from_val(env: &E, val: &Object) -> Result<Self, Self::Error> {
67        env.convert(*val)
68    }
69}
70
71impl<'a, E> TryFromVal<E, ScValObjRef<'a>> for Object
72where
73    E: Env + Convert<ScValObjRef<'a>, Object>,
74{
75    type Error = crate::Error;
76
77    fn try_from_val(env: &E, v: &ScValObjRef<'a>) -> Result<Self, Self::Error> {
78        match env.convert(*v) {
79            Ok(obj) => Ok(obj),
80            Err(e) => Err(e.into()),
81        }
82    }
83}
84
85impl ScValObject {
86    /// Inspect the provided `value` and return `Ok(ScValObject(value))` if it
87    /// is a value that should be represented as an object, else `Err(value)`.
88    pub fn classify(value: ScVal) -> Result<ScValObject, ScVal> {
89        if ScValObjRef::classify(&value).is_some() {
90            Ok(ScValObject(value))
91        } else {
92            Err(value)
93        }
94    }
95
96    /// Assert that `value` is the sort of value that should be
97    /// `ScValObject` without actually inspecting its value.
98    pub unsafe fn unchecked_from_val(value: ScVal) -> ScValObject {
99        ScValObject(value)
100    }
101}
102
103impl AsRef<ScVal> for ScValObject {
104    fn as_ref(&self) -> &ScVal {
105        &self.0
106    }
107}
108
109#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
110pub struct ScValObjRef<'a>(&'a ScVal);
111
112impl<'a> From<ScValObjRef<'a>> for &'a ScVal {
113    fn from(val: ScValObjRef<'a>) -> Self {
114        val.0
115    }
116}
117
118impl AsRef<ScVal> for ScValObjRef<'_> {
119    fn as_ref(&self) -> &ScVal {
120        self.0
121    }
122}
123
124impl<'a> ScValObjRef<'a> {
125    /// Return either `Some(ScValObject(value))` if the provided `value` is a case
126    /// that needs to be stored as a host-side object, or else `None`.
127    pub fn classify(value: &'a ScVal) -> Option<Self> {
128        match value {
129            // Always-small values are never ScValObject, nor are
130            // ScVals that don't actually project into Vals at all.
131            ScVal::Bool(_)
132            | ScVal::Void
133            | ScVal::Error(_)
134            | ScVal::U32(_)
135            | ScVal::I32(_)
136            | ScVal::LedgerKeyContractInstance
137            | ScVal::LedgerKeyNonce(_)
138            | ScVal::ContractInstance(_) => None,
139
140            // Always-large values are always ScValObject
141            ScVal::Bytes(_)
142            | ScVal::String(_)
143            | ScVal::Vec(_)
144            | ScVal::Map(_)
145            | ScVal::Address(_) => Some(ScValObjRef(value)),
146
147            // Other values are small or large depending on
148            // their actual scalar value.
149            ScVal::U64(u) | ScVal::Timepoint(TimePoint(u)) | ScVal::Duration(Duration(u)) => {
150                if num::is_small_u64(*u) {
151                    None
152                } else {
153                    Some(ScValObjRef(value))
154                }
155            }
156            ScVal::I64(i) => {
157                if num::is_small_i64(*i) {
158                    None
159                } else {
160                    Some(ScValObjRef(value))
161                }
162            }
163            ScVal::U128(u) => {
164                if num::is_small_u128(u.into()) {
165                    None
166                } else {
167                    Some(ScValObjRef(value))
168                }
169            }
170            ScVal::I128(i) => {
171                if num::is_small_i128(i.into()) {
172                    None
173                } else {
174                    Some(ScValObjRef(value))
175                }
176            }
177            ScVal::U256(u) => {
178                if num::is_small_u256_parts(u) {
179                    None
180                } else {
181                    Some(ScValObjRef(value))
182                }
183            }
184            ScVal::I256(i) => {
185                if num::is_small_i256_parts(i) {
186                    None
187                } else {
188                    Some(ScValObjRef(value))
189                }
190            }
191            ScVal::Symbol(s) => {
192                if s.len() <= crate::symbol::MAX_SMALL_CHARS {
193                    None
194                } else {
195                    Some(ScValObjRef(value))
196                }
197            }
198        }
199    }
200}