reifydb_core/value/encoded/
uuid7.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use std::ptr;
5
6use reifydb_type::{Type, Uuid7};
7use uuid::Uuid;
8
9use crate::value::encoded::{EncodedValues, EncodedValuesLayout};
10
11impl EncodedValuesLayout {
12	pub fn set_uuid7(&self, row: &mut EncodedValues, index: usize, value: Uuid7) {
13		let field = &self.fields[index];
14		debug_assert!(row.len() >= self.total_static_size());
15		debug_assert_eq!(field.r#type, Type::Uuid7);
16		row.set_valid(index, true);
17		unsafe {
18			// UUIDs are 16 bytes
19			ptr::write_unaligned(
20				row.make_mut().as_mut_ptr().add(field.offset) as *mut [u8; 16],
21				*value.as_bytes(),
22			);
23		}
24	}
25
26	pub fn get_uuid7(&self, row: &EncodedValues, index: usize) -> Uuid7 {
27		let field = &self.fields[index];
28		debug_assert!(row.len() >= self.total_static_size());
29		debug_assert_eq!(field.r#type, Type::Uuid7);
30		unsafe {
31			// UUIDs are 16 bytes
32			let bytes: [u8; 16] = ptr::read_unaligned(row.as_ptr().add(field.offset) as *const [u8; 16]);
33			Uuid7::from(Uuid::from_bytes(bytes))
34		}
35	}
36
37	pub fn try_get_uuid7(&self, row: &EncodedValues, index: usize) -> Option<Uuid7> {
38		if row.is_defined(index) && self.fields[index].r#type == Type::Uuid7 {
39			Some(self.get_uuid7(row, index))
40		} else {
41			None
42		}
43	}
44}
45
46#[cfg(test)]
47mod tests {
48	use std::time::Duration;
49
50	use reifydb_type::{Type, Uuid7};
51	use tokio::time::sleep;
52
53	use crate::value::encoded::EncodedValuesLayout;
54
55	#[test]
56	fn test_set_get_uuid7() {
57		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
58		let mut row = layout.allocate();
59
60		let uuid = Uuid7::generate();
61		layout.set_uuid7(&mut row, 0, uuid.clone());
62		assert_eq!(layout.get_uuid7(&row, 0), uuid);
63	}
64
65	#[test]
66	fn test_try_get_uuid7() {
67		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
68		let mut row = layout.allocate();
69
70		assert_eq!(layout.try_get_uuid7(&row, 0), None);
71
72		let uuid = Uuid7::generate();
73		layout.set_uuid7(&mut row, 0, uuid.clone());
74		assert_eq!(layout.try_get_uuid7(&row, 0), Some(uuid));
75	}
76
77	#[test]
78	fn test_multiple_generations() {
79		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
80
81		// Generate multiple UUIDs and ensure they're different
82		let mut uuids = Vec::new();
83		for _ in 0..10 {
84			let mut row = layout.allocate();
85			let uuid = Uuid7::generate();
86			layout.set_uuid7(&mut row, 0, uuid.clone());
87			let retrieved = layout.get_uuid7(&row, 0);
88			assert_eq!(retrieved, uuid);
89			uuids.push(uuid);
90		}
91
92		// Ensure all generated UUIDs are unique
93		for i in 0..uuids.len() {
94			for j in (i + 1)..uuids.len() {
95				assert_ne!(uuids[i], uuids[j], "UUIDs should be unique");
96			}
97		}
98	}
99
100	#[test]
101	fn test_version_check() {
102		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
103		let mut row = layout.allocate();
104
105		let uuid = Uuid7::generate();
106		layout.set_uuid7(&mut row, 0, uuid.clone());
107		let retrieved = layout.get_uuid7(&row, 0);
108
109		// Verify it's a version 7 UUID
110		assert_eq!(retrieved.get_version_num(), 7);
111	}
112
113	#[tokio::test]
114	async fn test_timestamp_ordering() {
115		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
116
117		// Generate UUIDs in sequence - they should be ordered by
118		// timestamp
119		let mut uuids = Vec::new();
120		for _ in 0..5 {
121			let mut row = layout.allocate();
122			let uuid = Uuid7::generate();
123			layout.set_uuid7(&mut row, 0, uuid.clone());
124			let retrieved = layout.get_uuid7(&row, 0);
125			assert_eq!(retrieved, uuid);
126			uuids.push(uuid);
127
128			// Small delay to ensure different timestamps
129			sleep(Duration::from_millis(1)).await;
130		}
131
132		// Verify that UUIDs are ordered (timestamp-based)
133		for i in 1..uuids.len() {
134			assert!(uuids[i].as_bytes() >= uuids[i - 1].as_bytes(), "UUID7s should be timestamp-ordered");
135		}
136	}
137
138	#[test]
139	fn test_mixed_with_other_types() {
140		let layout = EncodedValuesLayout::new(&[Type::Uuid7, Type::Boolean, Type::Uuid7, Type::Int4]);
141		let mut row = layout.allocate();
142
143		let uuid1 = Uuid7::generate();
144		let uuid2 = Uuid7::generate();
145
146		layout.set_uuid7(&mut row, 0, uuid1.clone());
147		layout.set_bool(&mut row, 1, true);
148		layout.set_uuid7(&mut row, 2, uuid2.clone());
149		layout.set_i32(&mut row, 3, 42);
150
151		assert_eq!(layout.get_uuid7(&row, 0), uuid1);
152		assert_eq!(layout.get_bool(&row, 1), true);
153		assert_eq!(layout.get_uuid7(&row, 2), uuid2);
154		assert_eq!(layout.get_i32(&row, 3), 42);
155	}
156
157	#[test]
158	fn test_undefined_handling() {
159		let layout = EncodedValuesLayout::new(&[Type::Uuid7, Type::Uuid7]);
160		let mut row = layout.allocate();
161
162		let uuid = Uuid7::generate();
163		layout.set_uuid7(&mut row, 0, uuid.clone());
164
165		assert_eq!(layout.try_get_uuid7(&row, 0), Some(uuid));
166		assert_eq!(layout.try_get_uuid7(&row, 1), None);
167
168		layout.set_undefined(&mut row, 0);
169		assert_eq!(layout.try_get_uuid7(&row, 0), None);
170	}
171
172	#[test]
173	fn test_persistence() {
174		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
175		let mut row = layout.allocate();
176
177		let uuid = Uuid7::generate();
178		let uuid_string = uuid.to_string();
179
180		layout.set_uuid7(&mut row, 0, uuid.clone());
181		let retrieved = layout.get_uuid7(&row, 0);
182
183		assert_eq!(retrieved, uuid);
184		assert_eq!(retrieved.to_string(), uuid_string);
185		assert_eq!(retrieved.as_bytes(), uuid.as_bytes());
186	}
187
188	#[test]
189	fn test_clone_consistency() {
190		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
191		let mut row = layout.allocate();
192
193		let original_uuid = Uuid7::generate();
194		layout.set_uuid7(&mut row, 0, original_uuid.clone());
195
196		let retrieved_uuid = layout.get_uuid7(&row, 0);
197		assert_eq!(retrieved_uuid, original_uuid);
198
199		// Verify that the byte representation is identical
200		assert_eq!(retrieved_uuid.as_bytes(), original_uuid.as_bytes());
201	}
202
203	#[test]
204	fn test_multiple_fields() {
205		let layout = EncodedValuesLayout::new(&[Type::Uuid7, Type::Uuid7, Type::Uuid7]);
206		let mut row = layout.allocate();
207
208		let uuid1 = Uuid7::generate();
209		let uuid2 = Uuid7::generate();
210		let uuid3 = Uuid7::generate();
211
212		layout.set_uuid7(&mut row, 0, uuid1.clone());
213		layout.set_uuid7(&mut row, 1, uuid2.clone());
214		layout.set_uuid7(&mut row, 2, uuid3.clone());
215
216		assert_eq!(layout.get_uuid7(&row, 0), uuid1);
217		assert_eq!(layout.get_uuid7(&row, 1), uuid2);
218		assert_eq!(layout.get_uuid7(&row, 2), uuid3);
219
220		// Ensure all UUIDs are different
221		assert_ne!(uuid1, uuid2);
222		assert_ne!(uuid1, uuid3);
223		assert_ne!(uuid2, uuid3);
224	}
225
226	#[test]
227	fn test_format_consistency() {
228		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
229		let mut row = layout.allocate();
230
231		let uuid = Uuid7::generate();
232		let original_string = uuid.to_string();
233
234		layout.set_uuid7(&mut row, 0, uuid.clone());
235		let retrieved = layout.get_uuid7(&row, 0);
236		let retrieved_string = retrieved.to_string();
237
238		assert_eq!(original_string, retrieved_string);
239
240		// Verify UUID string format (8-4-4-4-12)
241		assert_eq!(original_string.len(), 36);
242		assert_eq!(original_string.matches('-').count(), 4);
243	}
244
245	#[test]
246	fn test_byte_level_storage() {
247		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
248		let mut row = layout.allocate();
249
250		let uuid = Uuid7::generate();
251		let original_bytes = *uuid.as_bytes();
252
253		layout.set_uuid7(&mut row, 0, uuid.clone());
254		let retrieved = layout.get_uuid7(&row, 0);
255		let retrieved_bytes = *retrieved.as_bytes();
256
257		assert_eq!(original_bytes, retrieved_bytes);
258
259		// Verify that it's exactly 16 bytes
260		assert_eq!(original_bytes.len(), 16);
261		assert_eq!(retrieved_bytes.len(), 16);
262	}
263
264	#[tokio::test]
265	async fn test_time_based_properties() {
266		let layout = EncodedValuesLayout::new(&[Type::Uuid7]);
267
268		// Generate UUIDs at different times
269		let uuid1 = Uuid7::generate();
270		sleep(Duration::from_millis(2)).await;
271		let uuid2 = Uuid7::generate();
272
273		let mut row1 = layout.allocate();
274		let mut row2 = layout.allocate();
275
276		layout.set_uuid7(&mut row1, 0, uuid1.clone());
277		layout.set_uuid7(&mut row2, 0, uuid2.clone());
278
279		let retrieved1 = layout.get_uuid7(&row1, 0);
280		let retrieved2 = layout.get_uuid7(&row2, 0);
281
282		// The second UUID should be "greater" due to timestamp ordering
283		assert!(retrieved2.as_bytes() > retrieved1.as_bytes());
284	}
285
286	#[test]
287	fn test_try_get_uuid7_wrong_type() {
288		let layout = EncodedValuesLayout::new(&[Type::Boolean]);
289		let mut row = layout.allocate();
290
291		layout.set_bool(&mut row, 0, true);
292
293		assert_eq!(layout.try_get_uuid7(&row, 0), None);
294	}
295}