1use bigdecimal::BigDecimal as StdBigDecimal;
5use num_bigint::BigInt as StdBigInt;
6use reifydb_type::value::{decimal::Decimal, r#type::Type};
7
8use crate::encoded::{row::EncodedRow, shape::RowShape};
9
10const MODE_DYNAMIC: u128 = 0x80000000000000000000000000000000;
11const MODE_MASK: u128 = 0x80000000000000000000000000000000;
12
13const DYNAMIC_OFFSET_MASK: u128 = 0x0000000000000000FFFFFFFFFFFFFFFF;
14const DYNAMIC_LENGTH_MASK: u128 = 0x7FFFFFFFFFFFFFFF0000000000000000;
15
16impl RowShape {
17 pub fn set_decimal(&self, row: &mut EncodedRow, index: usize, value: &Decimal) {
18 debug_assert!(matches!(self.fields()[index].constraint.get_type().inner_type(), Type::Decimal));
19
20 let (mantissa, original_scale) = value.inner().as_bigint_and_exponent();
21 let scale_bytes = original_scale.to_le_bytes();
22 let digits_bytes = mantissa.to_signed_bytes_le();
23
24 let mut serialized = Vec::with_capacity(8 + digits_bytes.len());
25 serialized.extend_from_slice(&scale_bytes);
26 serialized.extend_from_slice(&digits_bytes);
27
28 self.replace_dynamic_data(row, index, &serialized);
29 }
30
31 pub fn get_decimal(&self, row: &EncodedRow, index: usize) -> Decimal {
32 let field = &self.fields()[index];
33 debug_assert!(matches!(field.constraint.get_type().inner_type(), Type::Decimal));
34
35 let packed = unsafe { (row.as_ptr().add(field.offset as usize) as *const u128).read_unaligned() };
36 let packed = u128::from_le(packed);
37
38 debug_assert!(packed & MODE_MASK == MODE_DYNAMIC, "Expected dynamic storage");
39
40 let offset = (packed & DYNAMIC_OFFSET_MASK) as usize;
41 let length = ((packed & DYNAMIC_LENGTH_MASK) >> 64) as usize;
42
43 let dynamic_start = self.dynamic_section_start();
44 let data_bytes = &row.as_slice()[dynamic_start + offset..dynamic_start + offset + length];
45
46 let original_scale = i64::from_le_bytes(data_bytes[0..8].try_into().unwrap());
47 let mantissa = StdBigInt::from_signed_bytes_le(&data_bytes[8..]);
48
49 let big_decimal = StdBigDecimal::new(mantissa, original_scale);
50
51 Decimal::from(big_decimal)
52 }
53
54 pub fn try_get_decimal(&self, row: &EncodedRow, index: usize) -> Option<Decimal> {
55 if row.is_defined(index)
56 && matches!(self.fields()[index].constraint.get_type().inner_type(), Type::Decimal)
57 {
58 Some(self.get_decimal(row, index))
59 } else {
60 None
61 }
62 }
63}
64
65#[cfg(test)]
66pub mod tests {
67 use std::str::FromStr;
68
69 use num_traits::Zero;
70 use reifydb_type::value::{decimal::Decimal, r#type::Type};
71
72 use crate::encoded::shape::RowShape;
73
74 #[test]
75 fn test_compact_inline() {
76 let shape = RowShape::testing(&[Type::Decimal]);
77 let mut row = shape.allocate();
78
79 let decimal = Decimal::from_str("123.45").unwrap();
81 shape.set_decimal(&mut row, 0, &decimal);
82 assert!(row.is_defined(0));
83
84 let retrieved = shape.get_decimal(&row, 0);
85 assert_eq!(retrieved.to_string(), "123.45");
86
87 let mut row2 = shape.allocate();
89 let negative = Decimal::from_str("-999.99").unwrap();
90 shape.set_decimal(&mut row2, 0, &negative);
91 assert_eq!(shape.get_decimal(&row2, 0).to_string(), "-999.99");
92 }
93
94 #[test]
95 fn test_compact_boundaries() {
96 let shape1 = RowShape::testing(&[Type::Decimal]);
98 let mut row1 = shape1.allocate();
99 let high_precision = Decimal::from_str("1.0000000000000000000000000000001").unwrap();
100 shape1.set_decimal(&mut row1, 0, &high_precision);
101 let retrieved = shape1.get_decimal(&row1, 0);
102 assert_eq!(retrieved.to_string(), "1.0000000000000000000000000000001");
103
104 let shape2 = RowShape::testing(&[Type::Decimal]);
106 let mut row2 = shape2.allocate();
107 let large_int = Decimal::from_str("100000000000000000000000000000000").unwrap();
108 shape2.set_decimal(&mut row2, 0, &large_int);
109 assert_eq!(shape2.get_decimal(&row2, 0).to_string(), "100000000000000000000000000000000");
110 }
111
112 #[test]
113 fn test_extended_i128() {
114 let shape = RowShape::testing(&[Type::Decimal]);
115 let mut row = shape.allocate();
116
117 let large = Decimal::from_str("999999999999999999999.123456789").unwrap();
119 shape.set_decimal(&mut row, 0, &large);
120 assert!(row.is_defined(0));
121
122 let retrieved = shape.get_decimal(&row, 0);
123 assert_eq!(retrieved.to_string(), "999999999999999999999.123456789");
124 }
125
126 #[test]
127 fn test_dynamic_storage() {
128 let shape = RowShape::testing(&[Type::Decimal]);
131 let mut row = shape.allocate();
132
133 let huge = Decimal::from_str("99999999999999999999999999999.123456789").unwrap();
136
137 shape.set_decimal(&mut row, 0, &huge);
138 assert!(row.is_defined(0));
139
140 let retrieved = shape.get_decimal(&row, 0);
141 assert_eq!(retrieved.to_string(), "99999999999999999999999999999.123456789");
142 }
143
144 #[test]
145 fn test_zero() {
146 let shape = RowShape::testing(&[Type::Decimal]);
147 let mut row = shape.allocate();
148
149 let zero = Decimal::from_str("0.0").unwrap();
150 shape.set_decimal(&mut row, 0, &zero);
151 assert!(row.is_defined(0));
152
153 let retrieved = shape.get_decimal(&row, 0);
154 assert!(retrieved.inner().is_zero());
155 }
156
157 #[test]
158 fn test_currency_values() {
159 let shape = RowShape::testing(&[Type::Decimal]);
160
161 let mut row1 = shape.allocate();
163 let price = Decimal::from_str("19.99").unwrap();
164 shape.set_decimal(&mut row1, 0, &price);
165 assert_eq!(shape.get_decimal(&row1, 0).to_string(), "19.99");
166
167 let mut row2 = shape.allocate();
169 let large_price = Decimal::from_str("999999999.99").unwrap();
170 shape.set_decimal(&mut row2, 0, &large_price);
171 assert_eq!(shape.get_decimal(&row2, 0).to_string(), "999999999.99");
172
173 let mut row3 = shape.allocate();
175 let fraction = Decimal::from_str("0.00000001").unwrap();
176 shape.set_decimal(&mut row3, 0, &fraction);
177 assert_eq!(shape.get_decimal(&row3, 0), fraction);
178 }
179
180 #[test]
181 fn test_scientific_notation() {
182 let shape = RowShape::testing(&[Type::Decimal]);
183 let mut row = shape.allocate();
184
185 let scientific = Decimal::from_str("1.23456e10").unwrap();
186 shape.set_decimal(&mut row, 0, &scientific);
187
188 let retrieved = shape.get_decimal(&row, 0);
189 assert_eq!(retrieved.to_string(), "12345600000");
190 }
191
192 #[test]
193 fn test_try_get() {
194 let shape = RowShape::testing(&[Type::Decimal]);
195 let mut row = shape.allocate();
196
197 assert_eq!(shape.try_get_decimal(&row, 0), None);
199
200 let value = Decimal::from_str("42.42").unwrap();
202 shape.set_decimal(&mut row, 0, &value);
203
204 let retrieved = shape.try_get_decimal(&row, 0);
205 assert!(retrieved.is_some());
206 assert_eq!(retrieved.unwrap().to_string(), "42.42");
207 }
208
209 #[test]
210 fn test_clone_on_write() {
211 let shape = RowShape::testing(&[Type::Decimal]);
212 let row1 = shape.allocate();
213 let mut row2 = row1.clone();
214
215 let value = Decimal::from_str("3.14159").unwrap();
216 shape.set_decimal(&mut row2, 0, &value);
217
218 assert!(!row1.is_defined(0));
219 assert!(row2.is_defined(0));
220 assert_ne!(row1.as_ptr(), row2.as_ptr());
221 assert_eq!(shape.get_decimal(&row2, 0).to_string(), "3.14159");
222 }
223
224 #[test]
225 fn test_mixed_with_other_types() {
226 let shape = RowShape::testing(&[Type::Boolean, Type::Decimal, Type::Utf8, Type::Decimal, Type::Int4]);
227 let mut row = shape.allocate();
228
229 shape.set_bool(&mut row, 0, true);
230
231 let small_decimal = Decimal::from_str("99.99").unwrap();
232 shape.set_decimal(&mut row, 1, &small_decimal);
233
234 shape.set_utf8(&mut row, 2, "test");
235
236 let large_decimal = Decimal::from_str("123456789.987654321").unwrap();
237 shape.set_decimal(&mut row, 3, &large_decimal);
238
239 shape.set_i32(&mut row, 4, -42);
240
241 assert_eq!(shape.get_bool(&row, 0), true);
242 assert_eq!(shape.get_decimal(&row, 1).to_string(), "99.99");
243 assert_eq!(shape.get_utf8(&row, 2), "test");
244 assert_eq!(shape.get_decimal(&row, 3).to_string(), "123456789.987654321");
245 assert_eq!(shape.get_i32(&row, 4), -42);
246 }
247
248 #[test]
249 fn test_negative_values() {
250 let shape1 = RowShape::testing(&[Type::Decimal]);
252
253 let mut row1 = shape1.allocate();
254 let small_neg = Decimal::from_str("-0.01").unwrap();
255 shape1.set_decimal(&mut row1, 0, &small_neg);
256 assert_eq!(shape1.get_decimal(&row1, 0).to_string(), "-0.01");
257
258 let shape2 = RowShape::testing(&[Type::Decimal]);
260 let mut row2 = shape2.allocate();
261 let large_neg = Decimal::from_str("-999999999999999999.999").unwrap();
262 shape2.set_decimal(&mut row2, 0, &large_neg);
263 assert_eq!(shape2.get_decimal(&row2, 0).to_string(), "-999999999999999999.999");
264
265 let shape3 = RowShape::testing(&[Type::Decimal]);
267 let mut row3 = shape3.allocate();
268 let huge_neg = Decimal::from_str("-99999999999999999999999999999.999999999").unwrap();
269 shape3.set_decimal(&mut row3, 0, &huge_neg);
270 assert_eq!(shape3.get_decimal(&row3, 0).to_string(), "-99999999999999999999999999999.999999999");
271 }
272
273 #[test]
274 fn test_try_get_decimal_wrong_type() {
275 let shape = RowShape::testing(&[Type::Boolean]);
276 let mut row = shape.allocate();
277
278 shape.set_bool(&mut row, 0, true);
279
280 assert_eq!(shape.try_get_decimal(&row, 0), None);
281 }
282
283 #[test]
284 fn test_update_decimal() {
285 let shape = RowShape::testing(&[Type::Decimal]);
286 let mut row = shape.allocate();
287
288 let d1 = Decimal::from_str("123.45").unwrap();
289 shape.set_decimal(&mut row, 0, &d1);
290 assert_eq!(shape.get_decimal(&row, 0).to_string(), "123.45");
291
292 let d2 = Decimal::from_str("999.99").unwrap();
294 shape.set_decimal(&mut row, 0, &d2);
295 assert_eq!(shape.get_decimal(&row, 0).to_string(), "999.99");
296
297 let d3 = Decimal::from_str("99999999999999999999999999999.123456789").unwrap();
299 shape.set_decimal(&mut row, 0, &d3);
300 assert_eq!(shape.get_decimal(&row, 0).to_string(), "99999999999999999999999999999.123456789");
301 }
302
303 #[test]
304 fn test_update_decimal_with_other_dynamic_fields() {
305 let shape = RowShape::testing(&[Type::Decimal, Type::Utf8, Type::Decimal]);
306 let mut row = shape.allocate();
307
308 shape.set_decimal(&mut row, 0, &Decimal::from_str("1.0").unwrap());
309 shape.set_utf8(&mut row, 1, "test");
310 shape.set_decimal(&mut row, 2, &Decimal::from_str("2.0").unwrap());
311
312 shape.set_decimal(&mut row, 0, &Decimal::from_str("99999.12345").unwrap());
314
315 assert_eq!(shape.get_decimal(&row, 0).to_string(), "99999.12345");
316 assert_eq!(shape.get_utf8(&row, 1), "test");
317 assert_eq!(shape.get_decimal(&row, 2).to_string(), "2.0");
318 }
319}