icydb_schema/decimal/
compare.rs1use crate::decimal::{DECIMAL_DIGIT_BUFFER_LEN, Decimal};
2use std::{
3 cmp::Ordering,
4 hash::{Hash, Hasher},
5};
6
7impl Decimal {
8 pub(in crate::decimal) fn cmp_decimal(&self, other: &Self) -> Ordering {
9 let (lhs_m, lhs_s) = self.normalized_parts();
10 let (rhs_m, rhs_s) = other.normalized_parts();
11
12 if lhs_m == rhs_m && lhs_s == rhs_s {
13 return Ordering::Equal;
14 }
15
16 if lhs_m == 0 {
17 return if rhs_m.is_negative() {
18 Ordering::Greater
19 } else {
20 Ordering::Less
21 };
22 }
23
24 if rhs_m == 0 {
25 return if lhs_m.is_negative() {
26 Ordering::Less
27 } else {
28 Ordering::Greater
29 };
30 }
31
32 if lhs_m.is_negative() != rhs_m.is_negative() {
33 return if lhs_m.is_negative() {
34 Ordering::Less
35 } else {
36 Ordering::Greater
37 };
38 }
39
40 let positive = !lhs_m.is_negative();
41 let mut lhs_digits = [0u8; DECIMAL_DIGIT_BUFFER_LEN];
42 let mut rhs_digits = [0u8; DECIMAL_DIGIT_BUFFER_LEN];
43 let lhs_len = write_u128_decimal_digits(lhs_m.unsigned_abs(), &mut lhs_digits);
44 let rhs_len = write_u128_decimal_digits(rhs_m.unsigned_abs(), &mut rhs_digits);
45
46 let lhs_exponent = compare_exponent(lhs_s, lhs_len).unwrap_or(i64::MIN);
47 let rhs_exponent = compare_exponent(rhs_s, rhs_len).unwrap_or(i64::MIN);
48
49 let exponent_cmp = lhs_exponent.cmp(&rhs_exponent);
50 if exponent_cmp != Ordering::Equal {
51 return if positive {
52 exponent_cmp
53 } else {
54 exponent_cmp.reverse()
55 };
56 }
57
58 let significand_cmp =
59 cmp_significand_digits(&lhs_digits[..lhs_len], &rhs_digits[..rhs_len]);
60 if positive {
61 significand_cmp
62 } else {
63 significand_cmp.reverse()
64 }
65 }
66}
67
68impl PartialEq for Decimal {
69 fn eq(&self, other: &Self) -> bool {
70 let (lhs_m, lhs_s) = self.normalized_parts();
71 let (rhs_m, rhs_s) = other.normalized_parts();
72 lhs_m == rhs_m && lhs_s == rhs_s
73 }
74}
75
76impl Eq for Decimal {}
77
78impl PartialOrd for Decimal {
79 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80 Some(Ord::cmp(self, other))
81 }
82}
83
84impl Ord for Decimal {
85 fn cmp(&self, other: &Self) -> Ordering {
86 self.cmp_decimal(other)
87 }
88}
89
90impl Hash for Decimal {
91 fn hash<H: Hasher>(&self, state: &mut H) {
92 let (mantissa, scale) = self.normalized_parts();
93 mantissa.hash(state);
94 scale.hash(state);
95 }
96}
97
98fn write_u128_decimal_digits(mut value: u128, out: &mut [u8; DECIMAL_DIGIT_BUFFER_LEN]) -> usize {
99 let mut write_idx = DECIMAL_DIGIT_BUFFER_LEN;
100
101 loop {
102 write_idx = write_idx.saturating_sub(1);
103 out[write_idx] = match value % 10 {
104 0 => b'0',
105 1 => b'1',
106 2 => b'2',
107 3 => b'3',
108 4 => b'4',
109 5 => b'5',
110 6 => b'6',
111 7 => b'7',
112 8 => b'8',
113 9 => b'9',
114 _ => unreachable!("decimal digit remainder must be in 0..=9"),
115 };
116 value /= 10;
117
118 if value == 0 {
119 break;
120 }
121 }
122
123 let len = DECIMAL_DIGIT_BUFFER_LEN.saturating_sub(write_idx);
124 out.copy_within(write_idx..DECIMAL_DIGIT_BUFFER_LEN, 0);
125 len
126}
127
128fn compare_exponent(scale: u32, digit_len: usize) -> Option<i64> {
129 let digit_count = i64::try_from(digit_len).ok()?;
130 let scale = i64::from(scale);
131 digit_count.checked_sub(1)?.checked_sub(scale)
132}
133
134fn cmp_significand_digits(lhs: &[u8], rhs: &[u8]) -> Ordering {
135 let width = lhs.len().max(rhs.len());
136 for idx in 0..width {
137 let l = lhs.get(idx).copied().unwrap_or(b'0');
138 let r = rhs.get(idx).copied().unwrap_or(b'0');
139 let cmp = l.cmp(&r);
140 if cmp != Ordering::Equal {
141 return cmp;
142 }
143 }
144
145 Ordering::Equal
146}