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