1use crate::fields::decoder::{FieldDecoder, FromCurrentTx, FromLedger, decode_exact};
2use crate::host;
3use crate::host::error_codes::match_result_code_with_expected_bytes;
4use crate::host::{Error, Result, RoundingMode};
5use crate::types::decode_error::DecodeError;
6
7const NUMBER_SIZE: usize = 12;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
26#[repr(C)]
27pub struct Number([u8; NUMBER_SIZE]);
28
29impl Number {
30 pub const ZERO: Number = Number([0u8; NUMBER_SIZE]);
32
33 pub const ONE: Number = Number([
35 0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE,
36 ]);
37
38 pub const NEGATIVE_ONE: Number = Number([
40 0xF2, 0x1F, 0x49, 0x4C, 0x58, 0x9C, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE,
41 ]);
42
43 pub fn from_int(value: i64) -> Result<Number> {
45 let mut out = [0u8; NUMBER_SIZE];
46 let rescode = unsafe {
47 host::float_from_int(
48 value,
49 out.as_mut_ptr(),
50 NUMBER_SIZE,
51 RoundingMode::ToNearest.into(),
52 )
53 };
54 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
55 }
56
57 pub fn from_uint(value: u64) -> Result<Number> {
59 let value_bytes = value.to_le_bytes();
62 let mut out = [0u8; NUMBER_SIZE];
63 let rescode = unsafe {
64 host::float_from_uint(
65 value_bytes.as_ptr(),
66 value_bytes.len(),
67 out.as_mut_ptr(),
68 NUMBER_SIZE,
69 RoundingMode::ToNearest.into(),
70 )
71 };
72 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
73 }
74
75 pub fn from_mant_exp(mantissa: i64, exponent: i32) -> Result<Number> {
77 let mut out = [0u8; NUMBER_SIZE];
78 let rescode = unsafe {
79 host::float_from_mant_exp(
80 mantissa,
81 exponent,
82 out.as_mut_ptr(),
83 NUMBER_SIZE,
84 RoundingMode::ToNearest.into(),
85 )
86 };
87 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
88 }
89
90 pub fn from_stamount(bytes: &[u8]) -> Result<Number> {
92 let mut out = [0u8; NUMBER_SIZE];
93 let rescode = unsafe {
94 host::float_from_stamount(
95 bytes.as_ptr(),
96 bytes.len(),
97 out.as_mut_ptr(),
98 NUMBER_SIZE,
99 RoundingMode::ToNearest.into(),
100 )
101 };
102 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
103 }
104
105 pub fn from_stnumber(bytes: &[u8]) -> Result<Number> {
107 let mut out = [0u8; NUMBER_SIZE];
108 let rescode = unsafe {
109 host::float_from_stnumber(
110 bytes.as_ptr(),
111 bytes.len(),
112 out.as_mut_ptr(),
113 NUMBER_SIZE,
114 RoundingMode::ToNearest.into(),
115 )
116 };
117 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
118 }
119
120 pub fn to_int(&self) -> Result<i64> {
122 let mut int_bytes = [0u8; 8];
123 let rescode = unsafe {
124 host::float_to_int(
125 self.0.as_ptr(),
126 self.0.len(),
127 int_bytes.as_mut_ptr(),
128 int_bytes.len(),
129 RoundingMode::ToNearest.into(),
130 )
131 };
132 match_result_code_with_expected_bytes(rescode, 8, || i64::from_le_bytes(int_bytes))
133 }
134
135 pub fn to_mant_exp(&self) -> Result<(i64, i32)> {
139 let mut mant_bytes = [0u8; 8];
140 let mut exp_bytes = [0u8; 4];
141 let rescode = unsafe {
142 host::float_to_mant_exp(
143 self.0.as_ptr(),
144 self.0.len(),
145 mant_bytes.as_mut_ptr(),
146 mant_bytes.len(),
147 exp_bytes.as_mut_ptr(),
148 exp_bytes.len(),
149 )
150 };
151 match_result_code_with_expected_bytes(rescode, 8, || {
152 (
153 i64::from_le_bytes(mant_bytes),
154 i32::from_le_bytes(exp_bytes),
155 )
156 })
157 }
158
159 fn compare_via_host(&self, other: &Number) -> Result<core::cmp::Ordering> {
161 let rescode = unsafe {
162 host::float_cmp(
163 self.0.as_ptr(),
164 self.0.len(),
165 other.0.as_ptr(),
166 other.0.len(),
167 )
168 };
169 match rescode {
170 0 => Result::Ok(core::cmp::Ordering::Equal),
171 1 => Result::Ok(core::cmp::Ordering::Greater),
172 2 => Result::Ok(core::cmp::Ordering::Less),
173 _ => Result::Err(Error::from_code(rescode)),
174 }
175 }
176
177 pub fn add(&self, other: &Number, rounding: RoundingMode) -> Result<Number> {
179 let mut out = [0u8; NUMBER_SIZE];
180 let rescode = unsafe {
181 host::float_add(
182 self.0.as_ptr(),
183 self.0.len(),
184 other.0.as_ptr(),
185 other.0.len(),
186 out.as_mut_ptr(),
187 NUMBER_SIZE,
188 rounding.into(),
189 )
190 };
191 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
192 }
193
194 pub fn subtract(&self, other: &Number, rounding: RoundingMode) -> Result<Number> {
196 let mut out = [0u8; NUMBER_SIZE];
197 let rescode = unsafe {
198 host::float_sub(
199 self.0.as_ptr(),
200 self.0.len(),
201 other.0.as_ptr(),
202 other.0.len(),
203 out.as_mut_ptr(),
204 NUMBER_SIZE,
205 rounding.into(),
206 )
207 };
208 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
209 }
210
211 pub fn multiply(&self, other: &Number, rounding: RoundingMode) -> Result<Number> {
213 let mut out = [0u8; NUMBER_SIZE];
214 let rescode = unsafe {
215 host::float_mult(
216 self.0.as_ptr(),
217 self.0.len(),
218 other.0.as_ptr(),
219 other.0.len(),
220 out.as_mut_ptr(),
221 NUMBER_SIZE,
222 rounding.into(),
223 )
224 };
225 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
226 }
227
228 pub fn divide(&self, other: &Number, rounding: RoundingMode) -> Result<Number> {
230 let mut out = [0u8; NUMBER_SIZE];
231 let rescode = unsafe {
232 host::float_div(
233 self.0.as_ptr(),
234 self.0.len(),
235 other.0.as_ptr(),
236 other.0.len(),
237 out.as_mut_ptr(),
238 NUMBER_SIZE,
239 rounding.into(),
240 )
241 };
242 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
243 }
244
245 pub fn pow(&self, n: i32, rounding: RoundingMode) -> Result<Number> {
247 let mut out = [0u8; NUMBER_SIZE];
248 let rescode = unsafe {
249 host::float_pow(
250 self.0.as_ptr(),
251 self.0.len(),
252 n,
253 out.as_mut_ptr(),
254 NUMBER_SIZE,
255 rounding.into(),
256 )
257 };
258 match_result_code_with_expected_bytes(rescode, NUMBER_SIZE, || Number(out))
259 }
260}
261
262impl From<[u8; NUMBER_SIZE]> for Number {
263 fn from(value: [u8; NUMBER_SIZE]) -> Self {
264 Number(value)
265 }
266}
267
268impl FieldDecoder for Number {
276 type Buffer = [u8; NUMBER_SIZE];
277
278 #[inline]
279 fn empty_buffer() -> Self::Buffer {
280 [0u8; NUMBER_SIZE]
281 }
282
283 #[inline]
284 fn decode(buf: Self::Buffer, bytes_written: usize) -> core::result::Result<Self, DecodeError> {
285 decode_exact(buf, bytes_written)
286 }
287}
288
289impl FromCurrentTx for Number {}
290impl FromLedger for Number {}
291
292impl PartialOrd for Number {
293 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
294 Some(self.cmp(other))
295 }
296}
297
298impl Ord for Number {
299 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
300 self.compare_via_host(other).unwrap()
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309 use crate::host::host_bindings_trait::MockHostBindings;
310 use crate::host::setup_mock;
311
312 const SAMPLE: [u8; NUMBER_SIZE] = [
314 0xD4, 0x91, 0xC3, 0x79, 0x37, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
315 ];
316
317 #[test]
318 fn test_from_int_success() {
319 let mut mock = MockHostBindings::new();
320 mock.expect_float_from_int()
321 .times(1)
322 .returning(|_, out, out_len, _| {
323 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
324 out_len as i32
325 });
326 let _guard = setup_mock(mock);
327
328 assert_eq!(Number::from_int(42).unwrap(), Number(SAMPLE));
329 }
330
331 #[test]
332 fn test_from_int_host_error() {
333 let mut mock = MockHostBindings::new();
334 mock.expect_float_from_int()
335 .times(1)
336 .returning(|_, _, _, _| -19); let _guard = setup_mock(mock);
338
339 assert!(Number::from_int(0).is_err());
340 }
341
342 #[test]
343 fn test_from_uint_success() {
344 let mut mock = MockHostBindings::new();
345 mock.expect_float_from_uint()
346 .times(1)
347 .returning(|_, _, out, out_len, _| {
348 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
349 out_len as i32
350 });
351 let _guard = setup_mock(mock);
352
353 assert_eq!(Number::from_uint(42).unwrap(), Number(SAMPLE));
354 }
355
356 #[test]
357 fn test_from_mant_exp_success() {
358 let mut mock = MockHostBindings::new();
359 mock.expect_float_from_mant_exp()
360 .times(1)
361 .returning(|_, _, out, out_len, _| {
362 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
363 out_len as i32
364 });
365 let _guard = setup_mock(mock);
366
367 assert_eq!(
368 Number::from_mant_exp(5_000_000_000_000_000, -15).unwrap(),
369 Number(SAMPLE)
370 );
371 }
372
373 #[test]
374 fn test_float_from_stamount_success() {
375 let mut mock = MockHostBindings::new();
376 mock.expect_float_from_stamount()
377 .times(1)
378 .returning(|_, _, out, out_len, _| {
379 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
380 out_len as i32
381 });
382 let _guard = setup_mock(mock);
383
384 assert_eq!(Number::from_stamount(&[0u8; 48]).unwrap(), Number(SAMPLE));
385 }
386
387 #[test]
388 fn test_float_from_stnumber_success() {
389 let mut mock = MockHostBindings::new();
390 mock.expect_float_from_stnumber()
391 .times(1)
392 .returning(|_, _, out, out_len, _| {
393 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
394 out_len as i32
395 });
396 let _guard = setup_mock(mock);
397
398 assert_eq!(
399 Number::from_stnumber(&[0u8; NUMBER_SIZE]).unwrap(),
400 Number(SAMPLE)
401 );
402 }
403
404 #[test]
405 fn test_float_to_int_success() {
406 let mut mock = MockHostBindings::new();
407 mock.expect_float_to_int()
408 .times(1)
409 .returning(|_, _, out, out_len, _| {
410 unsafe { out.copy_from_nonoverlapping(42i64.to_le_bytes().as_ptr(), 8) }
411 out_len as i32
412 });
413 let _guard = setup_mock(mock);
414
415 assert_eq!(Number(SAMPLE).to_int().unwrap(), 42);
416 }
417
418 #[test]
419 fn test_float_to_mant_exp_success() {
420 let mut mock = MockHostBindings::new();
421 mock.expect_float_to_mant_exp()
422 .times(1)
423 .returning(|_, _, mant, mant_len, exp, _| {
424 unsafe {
425 mant.copy_from_nonoverlapping(123i64.to_le_bytes().as_ptr(), 8);
426 exp.copy_from_nonoverlapping(5i32.to_le_bytes().as_ptr(), 4);
427 }
428 mant_len as i32
429 });
430 let _guard = setup_mock(mock);
431
432 assert_eq!(Number(SAMPLE).to_mant_exp().unwrap(), (123, 5));
433 }
434
435 #[test]
436 fn test_compare_orderings() {
437 for (code, expected) in [
438 (0, core::cmp::Ordering::Equal),
439 (1, core::cmp::Ordering::Greater),
440 (2, core::cmp::Ordering::Less),
441 ] {
442 let mut mock = MockHostBindings::new();
443 mock.expect_float_cmp()
444 .times(1)
445 .returning(move |_, _, _, _| code);
446 let _guard = setup_mock(mock);
447
448 assert_eq!(Number(SAMPLE).cmp(&Number(SAMPLE)), expected);
449 }
450 }
451
452 #[test]
453 fn test_compare_via_host_error() {
454 let mut mock = MockHostBindings::new();
455 mock.expect_float_cmp().times(1).returning(|_, _, _, _| -19);
456 let _guard = setup_mock(mock);
457
458 assert!(Number(SAMPLE).compare_via_host(&Number(SAMPLE)).is_err());
459 }
460
461 #[test]
462 fn test_zero_is_all_zeros() {
463 assert_eq!(Number::ZERO, Number([0u8; NUMBER_SIZE]));
464 }
465
466 const OTHER: [u8; NUMBER_SIZE] = [
468 0xD4, 0x83, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
469 ];
470
471 #[test]
472 fn test_float_add_success() {
473 let mut mock = MockHostBindings::new();
474 mock.expect_float_add()
475 .times(1)
476 .returning(|_, _, _, _, out, out_len, _| {
477 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
478 out_len as i32
479 });
480 let _guard = setup_mock(mock);
481
482 assert_eq!(
483 Number(SAMPLE)
484 .add(&Number(OTHER), RoundingMode::ToNearest)
485 .unwrap(),
486 Number(SAMPLE)
487 );
488 }
489
490 #[test]
491 fn test_float_sub_success() {
492 let mut mock = MockHostBindings::new();
493 mock.expect_float_sub()
494 .times(1)
495 .returning(|_, _, _, _, out, out_len, _| {
496 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
497 out_len as i32
498 });
499 let _guard = setup_mock(mock);
500
501 assert_eq!(
502 Number(SAMPLE)
503 .subtract(&Number(OTHER), RoundingMode::ToNearest)
504 .unwrap(),
505 Number(SAMPLE)
506 );
507 }
508
509 #[test]
510 fn test_float_mult_success() {
511 let mut mock = MockHostBindings::new();
512 mock.expect_float_mult()
513 .times(1)
514 .returning(|_, _, _, _, out, out_len, _| {
515 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
516 out_len as i32
517 });
518 let _guard = setup_mock(mock);
519
520 assert_eq!(
521 Number(SAMPLE)
522 .multiply(&Number(OTHER), RoundingMode::ToNearest)
523 .unwrap(),
524 Number(SAMPLE)
525 );
526 }
527
528 #[test]
529 fn test_float_div_success() {
530 let mut mock = MockHostBindings::new();
531 mock.expect_float_div()
532 .times(1)
533 .returning(|_, _, _, _, out, out_len, _| {
534 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
535 out_len as i32
536 });
537 let _guard = setup_mock(mock);
538
539 assert_eq!(
540 Number(SAMPLE)
541 .divide(&Number(OTHER), RoundingMode::ToNearest)
542 .unwrap(),
543 Number(SAMPLE)
544 );
545 }
546
547 #[test]
548 fn test_float_div_host_error() {
549 let mut mock = MockHostBindings::new();
550 mock.expect_float_div()
551 .times(1)
552 .returning(|_, _, _, _, _, _, _| -20); let _guard = setup_mock(mock);
554
555 assert!(
556 Number(SAMPLE)
557 .divide(&Number(OTHER), RoundingMode::ToNearest)
558 .is_err()
559 );
560 }
561
562 #[test]
563 fn test_float_pow_success() {
564 let mut mock = MockHostBindings::new();
565 mock.expect_float_pow()
566 .times(1)
567 .returning(|_, _, _, out, out_len, _| {
568 unsafe { out.copy_from_nonoverlapping(SAMPLE.as_ptr(), NUMBER_SIZE) }
569 out_len as i32
570 });
571 let _guard = setup_mock(mock);
572
573 assert_eq!(
574 Number(SAMPLE).pow(2, RoundingMode::ToNearest).unwrap(),
575 Number(SAMPLE)
576 );
577 }
578 fn wire(mantissa: i64, exponent: i32) -> [u8; NUMBER_SIZE] {
582 let mut bytes = [0u8; NUMBER_SIZE];
583 bytes[0..8].copy_from_slice(&mantissa.to_be_bytes());
584 bytes[8..12].copy_from_slice(&exponent.to_be_bytes());
585 bytes
586 }
587
588 #[test]
589 fn test_decode_matches_declared_constants() {
590 assert_eq!(
591 Number::decode(wire(0, 0), NUMBER_SIZE).unwrap(),
592 Number::ZERO
593 );
594 assert_eq!(
595 Number::decode(wire(1_000_000_000_000_000_000, -18), NUMBER_SIZE).unwrap(),
596 Number::ONE
597 );
598 assert_eq!(
599 Number::decode(wire(-1_000_000_000_000_000_000, -18), NUMBER_SIZE).unwrap(),
600 Number::NEGATIVE_ONE
601 );
602 }
603
604 #[test]
605 fn test_decode_roundtrips_wire_bytes() {
606 for (mantissa, exponent) in [
609 (-7_654_321i64, 12i32),
610 (123i64, -7i32),
611 (i64::MIN, i32::MIN),
612 (i64::MAX, i32::MAX),
613 ] {
614 let bytes = wire(mantissa, exponent);
615 assert_eq!(Number::decode(bytes, NUMBER_SIZE).unwrap(), Number(bytes));
616 }
617 }
618
619 #[test]
620 fn test_decode_rejects_short_write() {
621 assert_eq!(Number::decode(wire(1, 0), 11), Err(DecodeError));
622 assert_eq!(Number::decode(wire(1, 0), 0), Err(DecodeError));
623 }
624
625 #[test]
626 fn test_empty_buffer_is_number_sized() {
627 assert_eq!(<Number as FieldDecoder>::empty_buffer(), [0u8; NUMBER_SIZE]);
628 }
629}