Skip to main content

reifydb_codec/encoded/
uint.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::ptr;
5
6use num_bigint::{BigInt, BigUint};
7use num_traits::ToPrimitive;
8use reifydb_value::{
9	reifydb_assertions,
10	value::{uint::Uint, value_type::ValueType},
11};
12
13use crate::encoded::{row::EncodedRow, shape::RowShape};
14
15const MODE_INLINE: u128 = 0x00000000000000000000000000000000;
16const MODE_MASK: u128 = 0x80000000000000000000000000000000;
17
18const INLINE_VALUE_MASK: u128 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
19
20const DYNAMIC_OFFSET_MASK: u128 = 0x0000000000000000FFFFFFFFFFFFFFFF;
21const DYNAMIC_LENGTH_MASK: u128 = 0x7FFFFFFFFFFFFFFF0000000000000000;
22
23impl RowShape {
24	pub fn set_uint(&self, row: &mut EncodedRow, index: usize, value: &Uint) {
25		let field = &self.fields()[index];
26		reifydb_assertions! {
27			assert!(
28				row.len() >= self.total_static_size(),
29				"row/shape size mismatch: row.len()={} < total_static_size()={}",
30				row.len(),
31				self.total_static_size()
32			);
33			assert_eq!(*field.constraint.get_type().inner_type(), ValueType::Uint);
34		}
35
36		let unsigned_value = value.0.to_biguint().unwrap_or(BigUint::from(0u32));
37
38		if let Some(u128_val) = unsigned_value.to_u128()
39			&& u128_val < (1u128 << 127)
40		{
41			self.remove_dynamic_data(row, index);
42
43			let packed = MODE_INLINE | (u128_val & INLINE_VALUE_MASK);
44			unsafe {
45				ptr::write_unaligned(
46					row.make_mut().as_mut_ptr().add(field.offset as usize) as *mut u128,
47					packed.to_le(),
48				);
49			}
50			row.set_valid(index, true);
51			return;
52		}
53
54		let bytes = unsigned_value.to_bytes_le();
55		self.replace_dynamic_data(row, index, &bytes);
56	}
57
58	pub fn get_uint(&self, row: &EncodedRow, index: usize) -> Uint {
59		let field = &self.fields()[index];
60		reifydb_assertions! {
61			assert!(
62				row.len() >= self.total_static_size(),
63				"row/shape size mismatch: row.len()={} < total_static_size()={}",
64				row.len(),
65				self.total_static_size()
66			);
67			assert_eq!(*field.constraint.get_type().inner_type(), ValueType::Uint);
68		}
69
70		let packed = unsafe { (row.as_ptr().add(field.offset as usize) as *const u128).read_unaligned() };
71		let packed = u128::from_le(packed);
72
73		let mode = packed & MODE_MASK;
74
75		if mode == MODE_INLINE {
76			let value = packed & INLINE_VALUE_MASK;
77
78			let unsigned = BigUint::from(value);
79			Uint::from(BigInt::from(unsigned))
80		} else {
81			let offset = (packed & DYNAMIC_OFFSET_MASK) as usize;
82			let length = ((packed & DYNAMIC_LENGTH_MASK) >> 64) as usize;
83
84			let dynamic_start = self.dynamic_section_start();
85			let data_bytes = &row.as_slice()[dynamic_start + offset..dynamic_start + offset + length];
86
87			let unsigned = BigUint::from_bytes_le(data_bytes);
88			Uint::from(BigInt::from(unsigned))
89		}
90	}
91
92	pub fn try_get_uint(&self, row: &EncodedRow, index: usize) -> Option<Uint> {
93		if row.is_defined(index) && self.fields()[index].constraint.get_type() == ValueType::Uint {
94			Some(self.get_uint(row, index))
95		} else {
96			None
97		}
98	}
99}
100
101#[cfg(test)]
102pub mod tests {
103	use num_bigint::BigInt;
104	use num_traits::Zero;
105	use reifydb_value::value::{uint::Uint, value_type::ValueType};
106
107	use crate::encoded::shape::RowShape;
108
109	#[test]
110	fn test_u64_inline() {
111		let shape = RowShape::testing(&[ValueType::Uint]);
112		let mut row = shape.allocate();
113
114		// Test simple unsigned value
115		let small = Uint::from(42u64);
116		shape.set_uint(&mut row, 0, &small);
117		assert!(row.is_defined(0));
118
119		let retrieved = shape.get_uint(&row, 0);
120		assert_eq!(retrieved, small);
121
122		// Test larger unsigned value
123		let mut row2 = shape.allocate();
124		let large = Uint::from(999999999999u64);
125		shape.set_uint(&mut row2, 0, &large);
126		assert_eq!(shape.get_uint(&row2, 0), large);
127	}
128
129	#[test]
130	fn test_u128_boundary() {
131		let shape = RowShape::testing(&[ValueType::Uint]);
132		let mut row = shape.allocate();
133
134		// Value that needs u128 storage
135		let large = Uint::from(u64::MAX);
136		shape.set_uint(&mut row, 0, &large);
137		assert!(row.is_defined(0));
138
139		let retrieved = shape.get_uint(&row, 0);
140		assert_eq!(retrieved, large);
141
142		// Test max u128 that fits in 127 bits
143		let mut row2 = shape.allocate();
144		let max_u127 = Uint::from(u128::MAX >> 1); // 127 bits
145		shape.set_uint(&mut row2, 0, &max_u127);
146		assert_eq!(shape.get_uint(&row2, 0), max_u127);
147	}
148
149	#[test]
150	fn test_dynamic_storage() {
151		let shape = RowShape::testing(&[ValueType::Uint]);
152		let mut row = shape.allocate();
153
154		// Create a value that requires dynamic storage (>127 bits)
155		// Using string representation for very large numbers
156		let huge = Uint::from(
157			BigInt::parse_bytes(b"123456789012345678901234567890123456789012345678901234567890", 10)
158				.unwrap(),
159		);
160
161		shape.set_uint(&mut row, 0, &huge);
162		assert!(row.is_defined(0));
163
164		let retrieved = shape.get_uint(&row, 0);
165		assert_eq!(retrieved, huge);
166	}
167
168	#[test]
169	fn test_zero() {
170		let shape = RowShape::testing(&[ValueType::Uint]);
171		let mut row = shape.allocate();
172
173		let zero = Uint::from(0);
174		shape.set_uint(&mut row, 0, &zero);
175		assert!(row.is_defined(0));
176
177		let retrieved = shape.get_uint(&row, 0);
178		assert!(retrieved.is_zero());
179	}
180
181	#[test]
182	fn test_try_get() {
183		let shape = RowShape::testing(&[ValueType::Uint]);
184		let mut row = shape.allocate();
185
186		// Undefined initially
187		assert_eq!(shape.try_get_uint(&row, 0), None);
188
189		// Set value
190		let value = Uint::from(12345u64);
191		shape.set_uint(&mut row, 0, &value);
192		assert_eq!(shape.try_get_uint(&row, 0), Some(value));
193	}
194
195	#[test]
196	fn test_clone_on_write() {
197		let shape = RowShape::testing(&[ValueType::Uint]);
198		let row1 = shape.allocate();
199		let mut row2 = row1.clone();
200
201		let value = Uint::from(999999999999999u64);
202		shape.set_uint(&mut row2, 0, &value);
203
204		assert!(!row1.is_defined(0));
205		assert!(row2.is_defined(0));
206		assert_ne!(row1.as_ptr(), row2.as_ptr());
207		assert_eq!(shape.get_uint(&row2, 0), value);
208	}
209
210	#[test]
211	fn test_multiple_fields() {
212		let shape = RowShape::testing(&[
213			ValueType::Boolean,
214			ValueType::Uint,
215			ValueType::Utf8,
216			ValueType::Uint,
217			ValueType::Int4,
218		]);
219		let mut row = shape.allocate();
220
221		shape.set_bool(&mut row, 0, true);
222
223		let small = Uint::from(100u64);
224		shape.set_uint(&mut row, 1, &small);
225
226		shape.set_utf8(&mut row, 2, "test");
227
228		let large = Uint::from(u128::MAX >> 1);
229		shape.set_uint(&mut row, 3, &large);
230
231		shape.set_i32(&mut row, 4, 42);
232
233		assert_eq!(shape.get_bool(&row, 0), true);
234		assert_eq!(shape.get_uint(&row, 1), small);
235		assert_eq!(shape.get_utf8(&row, 2), "test");
236		assert_eq!(shape.get_uint(&row, 3), large);
237		assert_eq!(shape.get_i32(&row, 4), 42);
238	}
239
240	#[test]
241	fn test_negative_input_handling() {
242		let shape = RowShape::testing(&[ValueType::Uint]);
243
244		// Test how negative values are handled (should be converted to
245		// 0 or error)
246		let mut row1 = shape.allocate();
247		let negative = Uint::from(-42); // This creates a negative BigInt
248		shape.set_uint(&mut row1, 0, &negative);
249
250		// Should store as 0 since Uint can't handle negative values
251		let retrieved = shape.get_uint(&row1, 0);
252		assert_eq!(retrieved, Uint::from(0));
253	}
254
255	#[test]
256	fn test_try_get_uint_wrong_type() {
257		let shape = RowShape::testing(&[ValueType::Boolean]);
258		let mut row = shape.allocate();
259
260		shape.set_bool(&mut row, 0, true);
261
262		assert_eq!(shape.try_get_uint(&row, 0), None);
263	}
264
265	#[test]
266	fn test_update_uint_inline_to_inline() {
267		let shape = RowShape::testing(&[ValueType::Uint]);
268		let mut row = shape.allocate();
269
270		shape.set_uint(&mut row, 0, &Uint::from(42u64));
271		assert_eq!(shape.get_uint(&row, 0), Uint::from(42u64));
272
273		shape.set_uint(&mut row, 0, &Uint::from(999u64));
274		assert_eq!(shape.get_uint(&row, 0), Uint::from(999u64));
275	}
276
277	#[test]
278	fn test_update_uint_inline_to_dynamic() {
279		let shape = RowShape::testing(&[ValueType::Uint]);
280		let mut row = shape.allocate();
281
282		shape.set_uint(&mut row, 0, &Uint::from(42u64));
283
284		let huge = Uint::from(
285			BigInt::parse_bytes(b"999999999999999999999999999999999999999999999999", 10).unwrap(),
286		);
287		shape.set_uint(&mut row, 0, &huge);
288		assert_eq!(shape.get_uint(&row, 0), huge);
289	}
290
291	#[test]
292	fn test_update_uint_dynamic_to_inline() {
293		let shape = RowShape::testing(&[ValueType::Uint]);
294		let mut row = shape.allocate();
295
296		let huge = Uint::from(
297			BigInt::parse_bytes(b"999999999999999999999999999999999999999999999999", 10).unwrap(),
298		);
299		shape.set_uint(&mut row, 0, &huge);
300
301		shape.set_uint(&mut row, 0, &Uint::from(42u64));
302		assert_eq!(shape.get_uint(&row, 0), Uint::from(42u64));
303		assert_eq!(row.len(), shape.total_static_size());
304	}
305
306	#[test]
307	fn test_update_uint_with_other_dynamic_fields() {
308		let shape = RowShape::testing(&[ValueType::Uint, ValueType::Utf8]);
309		let mut row = shape.allocate();
310
311		let huge = Uint::from(
312			BigInt::parse_bytes(b"999999999999999999999999999999999999999999999999", 10).unwrap(),
313		);
314		shape.set_uint(&mut row, 0, &huge);
315		shape.set_utf8(&mut row, 1, "hello");
316
317		// Update uint to inline, verify utf8 still works
318		shape.set_uint(&mut row, 0, &Uint::from(1u64));
319		assert_eq!(shape.get_uint(&row, 0), Uint::from(1u64));
320		assert_eq!(shape.get_utf8(&row, 1), "hello");
321	}
322}