soroban_env_common/
compare.rs

1#[cfg(feature = "std")]
2use std::rc::Rc;
3
4use crate::{
5    val::ValConvert,
6    xdr::{ScErrorCode, ScErrorType},
7    Env, Error, Tag, Val,
8};
9use core::cmp::Ordering;
10
11/// General trait representing the ability to compare two values of some type.
12/// Similar to `core::cmp::Cmp` but with two key differences: the comparison is
13/// fallible, and is provided by some external type implementing `Compare`
14/// rather than the compared type itself.
15///
16/// This trait exists to support comparing `Val`s with help from the
17/// environment in which object handles are defined, and allowing for the
18/// possibility of erroneous inputs like invalid object handles, as well as the
19/// possibility of running over budget during the comparison. It is also used
20/// in other places where comparison work has to be budgeted, such as inside
21/// the host's storage maps.
22pub trait Compare<T> {
23    type Error;
24    fn compare(&self, a: &T, b: &T) -> Result<Ordering, Self::Error>;
25}
26
27impl<T, C: Compare<T>> Compare<&T> for C {
28    type Error = C::Error;
29
30    fn compare(&self, a: &&T, b: &&T) -> Result<Ordering, Self::Error> {
31        <C as Compare<T>>::compare(self, *a, *b)
32    }
33}
34
35impl<T, C: Compare<T>> Compare<Option<T>> for C {
36    type Error = C::Error;
37
38    fn compare(&self, a: &Option<T>, b: &Option<T>) -> Result<Ordering, Self::Error> {
39        match (a, b) {
40            (Some(a), Some(b)) => <C as Compare<T>>::compare(self, a, b),
41            (None, None) => Ok(Ordering::Equal),
42            (None, Some(_)) => Ok(Ordering::Less),
43            (Some(_), None) => Ok(Ordering::Greater),
44        }
45    }
46}
47
48macro_rules! impl_compare_for_tuple {
49    ( $($idx:tt $T:ident),+) => {
50        impl<$($T,)+ E, C> Compare<($($T,)+)> for C
51        where
52            $(C: Compare<$T, Error = E>,)+
53        {
54            type Error = E;
55
56            fn compare(&self, a: &($($T,)+), b: &($($T,)+)) -> Result<Ordering, Self::Error> {
57                $(
58                    match <C as Compare<$T>>::compare(self, &a.$idx, &b.$idx)? {
59                        unequal @ (Ordering::Less | Ordering::Greater) => return Ok(unequal),
60                        _ => ()
61                    }
62                )*
63                Ok(Ordering::Equal)
64            }
65        }
66    };
67}
68
69impl_compare_for_tuple!(0 T, 1 U);
70impl_compare_for_tuple!(0 T, 1 U, 2 V);
71impl_compare_for_tuple!(0 T, 1 U, 2 V, 3 W);
72impl_compare_for_tuple!(0 T, 1 U, 2 V, 3 W, 4 X);
73
74#[cfg(feature = "std")]
75impl<T, C: Compare<T>> Compare<Vec<T>> for C {
76    type Error = C::Error;
77
78    fn compare(&self, a: &Vec<T>, b: &Vec<T>) -> Result<Ordering, Self::Error> {
79        let mut i = 0;
80        loop {
81            match (a.get(i), b.get(i)) {
82                (None, None) => return Ok(Ordering::Equal),
83                (None, Some(_)) => return Ok(Ordering::Less),
84                (Some(_), None) => return Ok(Ordering::Greater),
85                (Some(a), Some(b)) => match <C as Compare<T>>::compare(self, a, b)? {
86                    Ordering::Equal => i += 1,
87                    unequal => return Ok(unequal),
88                },
89            }
90        }
91    }
92}
93
94#[cfg(feature = "std")]
95impl<T, C: Compare<T>> Compare<Box<T>> for C {
96    type Error = C::Error;
97
98    fn compare(&self, a: &Box<T>, b: &Box<T>) -> Result<Ordering, Self::Error> {
99        <Self as Compare<T>>::compare(self, a, b)
100    }
101}
102
103#[cfg(feature = "std")]
104impl<T, C: Compare<T>> Compare<Rc<T>> for C {
105    type Error = C::Error;
106
107    fn compare(&self, a: &Rc<T>, b: &Rc<T>) -> Result<Ordering, Self::Error> {
108        <Self as Compare<T>>::compare(self, a, b)
109    }
110}
111
112// Apparently we can't do a blanket T:Ord impl because there are Ord derivations
113// that also go through &T and Option<T> that conflict with our impls above
114// (patches welcome from someone who understands trait-system workarounds
115// better). But we can list out any concrete Ord instances we want to support
116// here.
117
118macro_rules! delegate_compare_to_wrapper {
119    ($T:ident,$A:ident,$B:ident,$SELF:ident) => {{
120        let a = unsafe { crate::$T::unchecked_from_val(*$A) };
121        let b = unsafe { crate::$T::unchecked_from_val(*$B) };
122        $SELF.compare(&a, &b)
123    }};
124}
125
126#[allow(clippy::comparison_chain)]
127impl<E: Env> Compare<Val> for E {
128    type Error = E::Error;
129
130    fn compare(&self, a: &Val, b: &Val) -> Result<Ordering, Self::Error> {
131        if a.get_payload() == b.get_payload() {
132            // Fast-path exactly-equal values.
133            return Ok(Ordering::Equal);
134        }
135        if a.is_object() || b.is_object() {
136            // Delegate any object-comparing to environment.
137            let v = self.obj_cmp(*a, *b)?;
138            return if v == 0 {
139                Ok(Ordering::Equal)
140            } else if v < 0 {
141                Ok(Ordering::Less)
142            } else {
143                Ok(Ordering::Greater)
144            };
145        }
146        let a_tag = a.get_tag();
147        let b_tag = b.get_tag();
148        if a_tag < b_tag {
149            Ok(Ordering::Less)
150        } else if a_tag > b_tag {
151            Ok(Ordering::Greater)
152        } else {
153            // Tags are equal so we only have to switch on one.
154            match a_tag {
155                Tag::False => Ok(Ordering::Equal),
156                Tag::True => Ok(Ordering::Equal),
157                Tag::Void => Ok(Ordering::Equal),
158
159                Tag::Error => delegate_compare_to_wrapper!(Error, a, b, self),
160
161                Tag::U32Val => delegate_compare_to_wrapper!(U32Val, a, b, self),
162                Tag::I32Val => delegate_compare_to_wrapper!(I32Val, a, b, self),
163
164                Tag::U64Small => delegate_compare_to_wrapper!(U64Small, a, b, self),
165                Tag::I64Small => delegate_compare_to_wrapper!(I64Small, a, b, self),
166
167                Tag::TimepointSmall => delegate_compare_to_wrapper!(TimepointSmall, a, b, self),
168                Tag::DurationSmall => delegate_compare_to_wrapper!(DurationSmall, a, b, self),
169
170                Tag::U128Small => delegate_compare_to_wrapper!(U128Small, a, b, self),
171                Tag::I128Small => delegate_compare_to_wrapper!(I128Small, a, b, self),
172
173                Tag::U256Small => delegate_compare_to_wrapper!(U256Small, a, b, self),
174                Tag::I256Small => delegate_compare_to_wrapper!(I256Small, a, b, self),
175
176                Tag::SymbolSmall => delegate_compare_to_wrapper!(SymbolSmall, a, b, self),
177
178                Tag::SmallCodeUpperBound => Ok(Ordering::Equal),
179                Tag::ObjectCodeLowerBound => Ok(Ordering::Equal),
180
181                // None of the object cases should be reachable, they
182                // should all have been handled by the is_object() branch
183                // above.
184                Tag::U64Object
185                | Tag::I64Object
186                | Tag::TimepointObject
187                | Tag::DurationObject
188                | Tag::U128Object
189                | Tag::I128Object
190                | Tag::U256Object
191                | Tag::I256Object
192                | Tag::BytesObject
193                | Tag::StringObject
194                | Tag::SymbolObject
195                | Tag::VecObject
196                | Tag::MapObject
197                | Tag::AddressObject => Err(self.error_from_error_val(Error::from_type_and_code(
198                    ScErrorType::Context,
199                    ScErrorCode::InternalError,
200                ))),
201
202                Tag::ObjectCodeUpperBound => Ok(Ordering::Equal),
203                Tag::Bad => Ok(Ordering::Equal),
204            }
205        }
206    }
207}