reifydb_core/value/encoded/
duration.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::{Duration, Type};
7
8use crate::value::encoded::{EncodedValues, EncodedValuesLayout};
9
10impl EncodedValuesLayout {
11	pub fn set_duration(&self, row: &mut EncodedValues, index: usize, value: Duration) {
12		let field = &self.fields[index];
13		debug_assert!(row.len() >= self.total_static_size());
14		debug_assert_eq!(field.r#type, Type::Duration);
15		row.set_valid(index, true);
16
17		let months = value.get_months();
18		let days = value.get_days();
19		let nanos = value.get_nanos();
20		unsafe {
21			// Write months (i32) at offset
22			ptr::write_unaligned(row.make_mut().as_mut_ptr().add(field.offset) as *mut i32, months);
23			// Write days (i32) at offset + 4
24			ptr::write_unaligned(row.make_mut().as_mut_ptr().add(field.offset + 4) as *mut i32, days);
25			// Write nanos (i64) at offset + 8
26			ptr::write_unaligned(row.make_mut().as_mut_ptr().add(field.offset + 8) as *mut i64, nanos);
27		}
28	}
29
30	pub fn get_duration(&self, row: &EncodedValues, index: usize) -> Duration {
31		let field = &self.fields[index];
32		debug_assert!(row.len() >= self.total_static_size());
33		debug_assert_eq!(field.r#type, Type::Duration);
34		unsafe {
35			// Read months (i32) from offset
36			let months = (row.as_ptr().add(field.offset) as *const i32).read_unaligned();
37			// Read days (i32) from offset + 4
38			let days = (row.as_ptr().add(field.offset + 4) as *const i32).read_unaligned();
39			// Read nanos (i64) from offset + 8
40			let nanos = (row.as_ptr().add(field.offset + 8) as *const i64).read_unaligned();
41			Duration::new(months, days, nanos)
42		}
43	}
44
45	pub fn try_get_duration(&self, row: &EncodedValues, index: usize) -> Option<Duration> {
46		if row.is_defined(index) {
47			Some(self.get_duration(row, index))
48		} else {
49			None
50		}
51	}
52}
53
54#[cfg(test)]
55mod tests {
56	use reifydb_type::{Duration, Type};
57
58	use crate::value::encoded::EncodedValuesLayout;
59
60	#[test]
61	fn test_set_get_duration() {
62		let layout = EncodedValuesLayout::new(&[Type::Duration]);
63		let mut row = layout.allocate();
64
65		let value = Duration::from_seconds(-7200);
66		layout.set_duration(&mut row, 0, value.clone());
67		assert_eq!(layout.get_duration(&row, 0), value);
68	}
69
70	#[test]
71	fn test_try_get_duration() {
72		let layout = EncodedValuesLayout::new(&[Type::Duration]);
73		let mut row = layout.allocate();
74
75		assert_eq!(layout.try_get_duration(&row, 0), None);
76
77		let test_duration = Duration::from_days(30);
78		layout.set_duration(&mut row, 0, test_duration.clone());
79		assert_eq!(layout.try_get_duration(&row, 0), Some(test_duration));
80	}
81
82	#[test]
83	fn test_zero() {
84		let layout = EncodedValuesLayout::new(&[Type::Duration]);
85		let mut row = layout.allocate();
86
87		let zero = Duration::default(); // Zero duration
88		layout.set_duration(&mut row, 0, zero.clone());
89		assert_eq!(layout.get_duration(&row, 0), zero);
90	}
91
92	#[test]
93	fn test_various_durations() {
94		let layout = EncodedValuesLayout::new(&[Type::Duration]);
95
96		let test_durations = [
97			Duration::from_seconds(0),     // Zero
98			Duration::from_seconds(60),    // 1 minute
99			Duration::from_seconds(3600),  // 1 hour
100			Duration::from_seconds(86400), // 1 day
101			Duration::from_days(7),        // 1 week
102			Duration::from_days(30),       // ~1 month
103			Duration::from_weeks(52),      // ~1 year
104		];
105
106		for duration in test_durations {
107			let mut row = layout.allocate();
108			layout.set_duration(&mut row, 0, duration.clone());
109			assert_eq!(layout.get_duration(&row, 0), duration);
110		}
111	}
112
113	#[test]
114	fn test_negative_durations() {
115		let layout = EncodedValuesLayout::new(&[Type::Duration]);
116
117		let negative_durations = [
118			Duration::from_seconds(-60),    // -1 minute
119			Duration::from_seconds(-3600),  // -1 hour
120			Duration::from_seconds(-86400), // -1 day
121			Duration::from_days(-7),        // -1 week
122			Duration::from_weeks(-4),       // -1 month
123		];
124
125		for duration in negative_durations {
126			let mut row = layout.allocate();
127			layout.set_duration(&mut row, 0, duration.clone());
128			assert_eq!(layout.get_duration(&row, 0), duration);
129		}
130	}
131
132	#[test]
133	fn test_complex_parts() {
134		let layout = EncodedValuesLayout::new(&[Type::Duration]);
135		let mut row = layout.allocate();
136
137		// Create a duration with all components
138		let complex_duration = Duration::new(
139			6,         // 6 months
140			15,        // 15 days
141			123456789, // nanoseconds
142		);
143		layout.set_duration(&mut row, 0, complex_duration.clone());
144		assert_eq!(layout.get_duration(&row, 0), complex_duration);
145	}
146
147	#[test]
148	fn test_mixed_with_other_types() {
149		let layout = EncodedValuesLayout::new(&[Type::Duration, Type::Boolean, Type::Duration, Type::Int8]);
150		let mut row = layout.allocate();
151
152		let duration1 = Duration::from_hours(24);
153		let duration2 = Duration::from_minutes(-30);
154
155		layout.set_duration(&mut row, 0, duration1.clone());
156		layout.set_bool(&mut row, 1, true);
157		layout.set_duration(&mut row, 2, duration2.clone());
158		layout.set_i64(&mut row, 3, 987654321);
159
160		assert_eq!(layout.get_duration(&row, 0), duration1);
161		assert_eq!(layout.get_bool(&row, 1), true);
162		assert_eq!(layout.get_duration(&row, 2), duration2);
163		assert_eq!(layout.get_i64(&row, 3), 987654321);
164	}
165
166	#[test]
167	fn test_undefined_handling() {
168		let layout = EncodedValuesLayout::new(&[Type::Duration, Type::Duration]);
169		let mut row = layout.allocate();
170
171		let duration = Duration::from_days(100);
172		layout.set_duration(&mut row, 0, duration.clone());
173
174		assert_eq!(layout.try_get_duration(&row, 0), Some(duration));
175		assert_eq!(layout.try_get_duration(&row, 1), None);
176
177		layout.set_undefined(&mut row, 0);
178		assert_eq!(layout.try_get_duration(&row, 0), None);
179	}
180
181	#[test]
182	fn test_large_values() {
183		let layout = EncodedValuesLayout::new(&[Type::Duration]);
184		let mut row = layout.allocate();
185
186		// Test with large values
187		let large_duration = Duration::new(
188			120,             // 10 years in months
189			3650,            // ~10 years in days
190			123456789012345, // Large nanosecond value
191		);
192		layout.set_duration(&mut row, 0, large_duration.clone());
193		assert_eq!(layout.get_duration(&row, 0), large_duration);
194	}
195
196	#[test]
197	fn test_precision_preservation() {
198		let layout = EncodedValuesLayout::new(&[Type::Duration]);
199		let mut row = layout.allocate();
200
201		// Test that all components are preserved exactly
202		let precise_duration = Duration::new(
203			-5,        // -5 months
204			20,        // 20 days
205			999999999, // 999,999,999 nanoseconds
206		);
207		layout.set_duration(&mut row, 0, precise_duration.clone());
208
209		let retrieved = layout.get_duration(&row, 0);
210		assert_eq!(retrieved, precise_duration);
211
212		let orig_months = precise_duration.get_months();
213		let orig_days = precise_duration.get_days();
214		let orig_nanos = precise_duration.get_nanos();
215		let ret_months = retrieved.get_months();
216		let ret_days = retrieved.get_days();
217		let ret_nanos = retrieved.get_nanos();
218		assert_eq!(orig_months, ret_months);
219		assert_eq!(orig_days, ret_days);
220		assert_eq!(orig_nanos, ret_nanos);
221	}
222
223	#[test]
224	fn test_common_durations() {
225		let layout = EncodedValuesLayout::new(&[Type::Duration]);
226
227		// Test common durations used in applications
228		let common_durations = [
229			Duration::from_seconds(1),  // 1 second
230			Duration::from_seconds(30), // 30 seconds
231			Duration::from_minutes(5),  // 5 minutes
232			Duration::from_minutes(15), // 15 minutes
233			Duration::from_hours(1),    // 1 hour
234			Duration::from_hours(8),    // Work day
235			Duration::from_days(1),     // 1 day
236			Duration::from_weeks(1),    // 1 week
237			Duration::from_weeks(2),    // 2 weeks
238		];
239
240		for duration in common_durations {
241			let mut row = layout.allocate();
242			layout.set_duration(&mut row, 0, duration.clone());
243			assert_eq!(layout.get_duration(&row, 0), duration);
244		}
245	}
246
247	#[test]
248	fn test_boundary_values() {
249		let layout = EncodedValuesLayout::new(&[Type::Duration]);
250
251		// Test boundary values for each component
252		let boundary_durations = [
253			Duration::new(i32::MAX, 0, 0), // Max months
254			Duration::new(i32::MIN, 0, 0), // Min months
255			Duration::new(0, i32::MAX, 0), // Max days
256			Duration::new(0, i32::MIN, 0), // Min days
257			Duration::new(0, 0, i64::MAX), // Max nanoseconds
258			Duration::new(0, 0, i64::MIN), // Min nanoseconds
259		];
260
261		for duration in boundary_durations {
262			let mut row = layout.allocate();
263			layout.set_duration(&mut row, 0, duration.clone());
264			assert_eq!(layout.get_duration(&row, 0), duration);
265		}
266	}
267}