soroban_env_common/
num.rs

1use core::cmp::Ordering;
2
3use crate::xdr::{Int256Parts, ScVal, UInt256Parts};
4use crate::{
5    declare_tag_based_signed_small_and_object_wrappers,
6    declare_tag_based_small_and_object_wrappers,
7    declare_tag_based_unsigned_small_and_object_wrappers, declare_tag_based_wrapper, val::TAG_BITS,
8    Compare, ConversionError, Env, Tag, Val,
9};
10pub use ethnum::{AsI256, AsU256, I256, U256};
11
12declare_tag_based_wrapper!(U32Val);
13declare_tag_based_wrapper!(I32Val);
14
15impl Val {
16    #[inline(always)]
17    pub const fn from_u32(u: u32) -> U32Val {
18        unsafe { U32Val(Val::from_major_minor_and_tag(u, 0, Tag::U32Val)) }
19    }
20
21    #[inline(always)]
22    pub const fn from_i32(i: i32) -> I32Val {
23        unsafe { I32Val(Val::from_major_minor_and_tag(i as u32, 0, Tag::I32Val)) }
24    }
25}
26
27impl<E: Env> Compare<U32Val> for E {
28    type Error = E::Error;
29    fn compare(&self, a: &U32Val, b: &U32Val) -> Result<Ordering, Self::Error> {
30        Ok(u32::from(*a).cmp(&u32::from(*b)))
31    }
32}
33
34impl<E: Env> Compare<I32Val> for E {
35    type Error = E::Error;
36    fn compare(&self, a: &I32Val, b: &I32Val) -> Result<Ordering, Self::Error> {
37        Ok(i32::from(*a).cmp(&i32::from(*b)))
38    }
39}
40
41declare_tag_based_unsigned_small_and_object_wrappers!(U64Val, U64Small, U64Object);
42declare_tag_based_signed_small_and_object_wrappers!(I64Val, I64Small, I64Object);
43declare_tag_based_unsigned_small_and_object_wrappers!(
44    TimepointVal,
45    TimepointSmall,
46    TimepointObject
47);
48declare_tag_based_unsigned_small_and_object_wrappers!(DurationVal, DurationSmall, DurationObject);
49
50declare_tag_based_unsigned_small_and_object_wrappers!(U128Val, U128Small, U128Object);
51declare_tag_based_signed_small_and_object_wrappers!(I128Val, I128Small, I128Object);
52declare_tag_based_unsigned_small_and_object_wrappers!(U256Val, U256Small, U256Object);
53declare_tag_based_signed_small_and_object_wrappers!(I256Val, I256Small, I256Object);
54
55impl From<u32> for U32Val {
56    fn from(value: u32) -> Self {
57        U32Val(value.into())
58    }
59}
60
61impl From<U32Val> for u32 {
62    fn from(value: U32Val) -> Self {
63        value.0.get_major()
64    }
65}
66
67impl From<i32> for I32Val {
68    fn from(value: i32) -> Self {
69        I32Val(value.into())
70    }
71}
72
73impl From<I32Val> for i32 {
74    fn from(value: I32Val) -> Self {
75        value.0.get_major() as i32
76    }
77}
78
79impl From<U64Small> for u64 {
80    fn from(value: U64Small) -> Self {
81        value.0.get_body()
82    }
83}
84
85impl From<I64Small> for i64 {
86    fn from(value: I64Small) -> Self {
87        value.0.get_signed_body()
88    }
89}
90
91impl From<TimepointSmall> for u64 {
92    fn from(value: TimepointSmall) -> Self {
93        value.0.get_body()
94    }
95}
96
97impl From<DurationSmall> for u64 {
98    fn from(value: DurationSmall) -> Self {
99        value.0.get_body()
100    }
101}
102
103impl From<U128Small> for u128 {
104    fn from(value: U128Small) -> Self {
105        value.0.get_body() as u128
106    }
107}
108
109impl From<I128Small> for i128 {
110    fn from(value: I128Small) -> Self {
111        value.0.get_signed_body() as i128
112    }
113}
114
115impl From<U256Small> for U256 {
116    fn from(value: U256Small) -> Self {
117        U256::from(value.0.get_body())
118    }
119}
120
121impl From<I256Small> for I256 {
122    fn from(value: I256Small) -> Self {
123        I256::from(value.0.get_signed_body())
124    }
125}
126
127impl TryFrom<u64> for U64Small {
128    type Error = ConversionError;
129    fn try_from(value: u64) -> Result<Self, Self::Error> {
130        if is_small_u64(value) {
131            Ok(Self(unsafe {
132                Val::from_body_and_tag(value, Tag::U64Small)
133            }))
134        } else {
135            Err(ConversionError)
136        }
137    }
138}
139
140impl TryFrom<i64> for I64Small {
141    type Error = ConversionError;
142    fn try_from(value: i64) -> Result<Self, Self::Error> {
143        if is_small_i64(value) {
144            Ok(Self(unsafe {
145                Val::from_body_and_tag(value as u64, Tag::I64Small)
146            }))
147        } else {
148            Err(ConversionError)
149        }
150    }
151}
152
153impl TryFrom<u64> for TimepointSmall {
154    type Error = ConversionError;
155    fn try_from(value: u64) -> Result<Self, Self::Error> {
156        if is_small_u64(value) {
157            Ok(Self(unsafe {
158                Val::from_body_and_tag(value, Tag::TimepointSmall)
159            }))
160        } else {
161            Err(ConversionError)
162        }
163    }
164}
165
166impl TryFrom<u64> for DurationSmall {
167    type Error = ConversionError;
168    fn try_from(value: u64) -> Result<Self, Self::Error> {
169        if is_small_u64(value) {
170            Ok(Self(unsafe {
171                Val::from_body_and_tag(value, Tag::DurationSmall)
172            }))
173        } else {
174            Err(ConversionError)
175        }
176    }
177}
178
179impl TryFrom<TimepointSmall> for ScVal {
180    type Error = ConversionError;
181    fn try_from(value: TimepointSmall) -> Result<Self, ConversionError> {
182        Ok(ScVal::Timepoint(value.as_val().get_body().into()))
183    }
184}
185
186impl TryFrom<&TimepointSmall> for ScVal {
187    type Error = ConversionError;
188    fn try_from(value: &TimepointSmall) -> Result<Self, ConversionError> {
189        (*value).try_into()
190    }
191}
192
193impl TryFrom<DurationSmall> for ScVal {
194    type Error = ConversionError;
195    fn try_from(value: DurationSmall) -> Result<Self, ConversionError> {
196        Ok(ScVal::Duration(value.as_val().get_body().into()))
197    }
198}
199
200impl TryFrom<&DurationSmall> for ScVal {
201    type Error = ConversionError;
202    fn try_from(value: &DurationSmall) -> Result<Self, ConversionError> {
203        (*value).try_into()
204    }
205}
206
207impl TryFrom<u128> for U128Small {
208    type Error = ConversionError;
209    fn try_from(value: u128) -> Result<Self, Self::Error> {
210        if is_small_u128(value) {
211            Ok(Self(unsafe {
212                Val::from_body_and_tag(value as u64, Tag::U128Small)
213            }))
214        } else {
215            Err(ConversionError)
216        }
217    }
218}
219
220impl TryFrom<i128> for I128Small {
221    type Error = ConversionError;
222    fn try_from(value: i128) -> Result<Self, Self::Error> {
223        if is_small_i128(value) {
224            Ok(Self(unsafe {
225                Val::from_body_and_tag((value as u128) as u64, Tag::I128Small)
226            }))
227        } else {
228            Err(ConversionError)
229        }
230    }
231}
232
233impl From<U256Small> for u64 {
234    fn from(value: U256Small) -> Self {
235        value.0.get_body()
236    }
237}
238
239impl TryFrom<U256> for U256Small {
240    type Error = ConversionError;
241    fn try_from(value: U256) -> Result<Self, Self::Error> {
242        if is_small_u256(&value) {
243            Ok(Self(unsafe {
244                Val::from_body_and_tag(value.as_u64(), Tag::U256Small)
245            }))
246        } else {
247            Err(ConversionError)
248        }
249    }
250}
251
252impl From<I256Small> for i64 {
253    fn from(value: I256Small) -> Self {
254        value.0.get_signed_body()
255    }
256}
257
258impl TryFrom<I256> for I256Small {
259    type Error = ConversionError;
260    fn try_from(value: I256) -> Result<Self, Self::Error> {
261        if is_small_i256(&value) {
262            Ok(Self(unsafe {
263                Val::from_body_and_tag(value.as_i64() as u64, Tag::I256Small)
264            }))
265        } else {
266            Err(ConversionError)
267        }
268    }
269}
270
271impl TryFrom<U256Small> for ScVal {
272    type Error = ConversionError;
273    fn try_from(value: U256Small) -> Result<Self, ConversionError> {
274        let val = U256::new(value.as_val().get_body() as u128);
275        let (hi_hi, hi_lo, lo_hi, lo_lo) = u256_into_pieces(val);
276        Ok(ScVal::U256(UInt256Parts {
277            hi_hi,
278            hi_lo,
279            lo_hi,
280            lo_lo,
281        }))
282    }
283}
284
285impl TryFrom<&U256Small> for ScVal {
286    type Error = ConversionError;
287    fn try_from(value: &U256Small) -> Result<Self, ConversionError> {
288        (*value).try_into()
289    }
290}
291
292impl TryFrom<I256Small> for ScVal {
293    type Error = ConversionError;
294    fn try_from(value: I256Small) -> Result<Self, ConversionError> {
295        let val = I256::new(value.as_val().get_signed_body() as i128);
296        let (hi_hi, hi_lo, lo_hi, lo_lo) = i256_into_pieces(val);
297        Ok(ScVal::I256(Int256Parts {
298            hi_hi,
299            hi_lo,
300            lo_hi,
301            lo_lo,
302        }))
303    }
304}
305
306impl TryFrom<&I256Small> for ScVal {
307    type Error = ConversionError;
308    fn try_from(value: &I256Small) -> Result<Self, ConversionError> {
309        (*value).try_into()
310    }
311}
312
313// Some explicit convenience "small" constructors that take 32-bit inputs in
314// order to remain infallible and are also `const fn`.
315
316impl I64Small {
317    pub const fn from_i32(small: i32) -> Self {
318        let extended = small as i64;
319        unsafe { I64Small::from_body(extended as u64) }
320    }
321}
322
323impl I64Val {
324    pub const fn from_i32(small: i32) -> Self {
325        Self::from_small(I64Small::from_i32(small))
326    }
327}
328
329impl U64Small {
330    pub const fn from_u32(small: u32) -> Self {
331        let extended = small as u64;
332        unsafe { U64Small::from_body(extended) }
333    }
334}
335
336impl U64Val {
337    pub const fn from_u32(small: u32) -> Self {
338        Self::from_small(U64Small::from_u32(small))
339    }
340}
341
342impl I128Small {
343    pub const fn from_i32(small: i32) -> Self {
344        let extended = small as i64;
345        unsafe { I128Small::from_body(extended as u64) }
346    }
347}
348
349impl I128Val {
350    pub const fn from_i32(small: i32) -> Self {
351        Self::from_small(I128Small::from_i32(small))
352    }
353}
354
355impl U128Small {
356    pub const fn from_u32(small: u32) -> Self {
357        let extended = small as u64;
358        unsafe { U128Small::from_body(extended) }
359    }
360}
361
362impl U128Val {
363    pub const fn from_u32(small: u32) -> Self {
364        Self::from_small(U128Small::from_u32(small))
365    }
366}
367
368impl I256Small {
369    pub const fn from_i32(small: i32) -> Self {
370        let extended = small as i64;
371        unsafe { I256Small::from_body(extended as u64) }
372    }
373}
374
375impl I256Val {
376    pub const fn from_i32(small: i32) -> Self {
377        Self::from_small(I256Small::from_i32(small))
378    }
379}
380
381impl U256Small {
382    pub const fn from_u32(small: u32) -> Self {
383        let extended = small as u64;
384        unsafe { U256Small::from_body(extended) }
385    }
386}
387
388impl U256Val {
389    pub const fn from_u32(small: u32) -> Self {
390        Self::from_small(U256Small::from_u32(small))
391    }
392}
393
394pub const fn is_small_u64(u: u64) -> bool {
395    u == ((u << TAG_BITS) >> TAG_BITS)
396}
397
398pub const fn is_small_i64(i: i64) -> bool {
399    i == ((i << TAG_BITS) >> TAG_BITS)
400}
401
402pub fn is_small_u128(u: u128) -> bool {
403    let word = u as u64;
404    is_small_u64(word) && u == (word as u128)
405}
406
407pub fn is_small_i128(i: i128) -> bool {
408    let word = i as i64;
409    is_small_i64(word) && i == (word as i128)
410}
411
412pub fn is_small_u256(u: &U256) -> bool {
413    let word = u.as_u64();
414    is_small_u64(word) && *u == U256::from(word)
415}
416
417pub fn is_small_i256(i: &I256) -> bool {
418    let word = i.as_i64();
419    is_small_i64(word) && *i == I256::from(word)
420}
421
422pub fn is_small_u256_parts(u: &UInt256Parts) -> bool {
423    u.hi_hi == 0 && u.hi_lo == 0 && u.lo_hi == 0 && is_small_u64(u.lo_lo)
424}
425
426pub fn is_small_i256_parts(i: &Int256Parts) -> bool {
427    let i = i256_from_pieces(i.hi_hi, i.hi_lo, i.lo_hi, i.lo_lo);
428    is_small_i256(&i)
429}
430
431pub fn u256_from_pieces(hi_hi: u64, hi_lo: u64, lo_hi: u64, lo_lo: u64) -> U256 {
432    let high = (u128::from(hi_hi)) << 64 | u128::from(hi_lo);
433    let low = (u128::from(lo_hi)) << 64 | u128::from(lo_lo);
434    U256::from_words(high, low)
435}
436
437pub fn u256_into_pieces(u: U256) -> (u64, u64, u64, u64) {
438    let (high, low) = u.into_words();
439    let (hi_hi, hi_lo) = ((high >> 64) as u64, high as u64);
440    let (lo_hi, lo_lo) = ((low >> 64) as u64, low as u64);
441    (hi_hi, hi_lo, lo_hi, lo_lo)
442}
443
444pub fn i256_from_pieces(hi_hi: i64, hi_lo: u64, lo_hi: u64, lo_lo: u64) -> I256 {
445    let high = ((u128::from(hi_hi as u64) << 64) | u128::from(hi_lo)) as i128;
446    let low = ((u128::from(lo_hi) << 64) | u128::from(lo_lo)) as i128;
447    I256::from_words(high, low)
448}
449
450pub fn i256_into_pieces(i: I256) -> (i64, u64, u64, u64) {
451    let (high, low) = i.into_words();
452    let (hi_hi, hi_lo) = ((high >> 64) as i64, high as u64);
453    let (lo_hi, lo_lo) = ((low >> 64) as u64, low as u64);
454    (hi_hi, hi_lo, lo_hi, lo_lo)
455}
456
457pub const MIN_SMALL_U64: u64 = 0;
458pub const MAX_SMALL_U64: u64 = 0x00ff_ffff_ffff_ffff_u64;
459
460pub const MIN_SMALL_I64: i64 = 0xff80_0000_0000_0000_u64 as i64;
461pub const MAX_SMALL_I64: i64 = 0x007f_ffff_ffff_ffff_u64 as i64;
462
463static_assertions::const_assert!(MIN_SMALL_I64 == -36_028_797_018_963_968_i64);
464static_assertions::const_assert!(MAX_SMALL_I64 == 36_028_797_018_963_967_i64);
465
466pub const MIN_SMALL_U128: u128 = MIN_SMALL_U64 as u128;
467pub const MAX_SMALL_U128: u128 = MAX_SMALL_U64 as u128;
468
469pub const MIN_SMALL_I128: i128 = MIN_SMALL_I64 as i128;
470pub const MAX_SMALL_I128: i128 = MAX_SMALL_I64 as i128;
471
472pub const MIN_SMALL_U256: U256 = U256::new(MIN_SMALL_U128);
473pub const MAX_SMALL_U256: U256 = U256::new(MAX_SMALL_U128);
474
475pub const MIN_SMALL_I256: I256 = I256::new(MIN_SMALL_I128);
476pub const MAX_SMALL_I256: I256 = I256::new(MAX_SMALL_I128);
477
478#[test]
479fn test_small_ints() {
480    assert!(!is_small_i64(MIN_SMALL_I64 - 1));
481    assert!(is_small_i64(MIN_SMALL_I64));
482    assert!(is_small_i64(MAX_SMALL_I64));
483    assert!(!is_small_i64(MAX_SMALL_I64 + 1));
484
485    assert!(is_small_u64(MIN_SMALL_U64));
486    assert!(is_small_u64(MAX_SMALL_U64));
487    assert!(!is_small_u64(MAX_SMALL_U64 + 1));
488
489    assert!(!is_small_i128(MIN_SMALL_I128 - 1));
490    assert!(is_small_i128(MIN_SMALL_I128));
491    assert!(is_small_i128(MAX_SMALL_I128));
492    assert!(!is_small_i128(MAX_SMALL_I128 + 1));
493
494    assert!(is_small_u128(MIN_SMALL_U128));
495    assert!(is_small_u128(MAX_SMALL_U128));
496    assert!(!is_small_u128(MAX_SMALL_U128 + 1));
497
498    assert!(!is_small_i256(&(MIN_SMALL_I256 - 1)));
499    assert!(is_small_i256(&(MIN_SMALL_I256)));
500    assert!(is_small_i256(&(MAX_SMALL_I256)));
501    assert!(!is_small_i256(&(MAX_SMALL_I256 + 1)));
502
503    assert!(is_small_u256(&(MIN_SMALL_U256)));
504    assert!(is_small_u256(&(MAX_SMALL_U256)));
505    assert!(!is_small_u256(&(MAX_SMALL_U256 + 1)));
506
507    assert!(is_small_i64(-1_i64));
508    assert!(is_small_i64(-12345_i64));
509    assert!(is_small_i64(1_i64));
510    assert!(is_small_i64(12345_i64));
511
512    assert!(is_small_i128(-1_i128));
513    assert!(is_small_i128(-12345_i128));
514    assert!(is_small_i128(1_i128));
515    assert!(is_small_i128(12345_i128));
516
517    assert!(is_small_i256(&I256::new(-1_i128)));
518    assert!(is_small_i256(&I256::new(-12345_i128)));
519    assert!(is_small_i256(&I256::new(1_i128)));
520    assert!(is_small_i256(&I256::new(12345_i128)));
521}