reifydb_core/value/encoded/
f32.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;
7
8use crate::value::encoded::{EncodedValues, EncodedValuesLayout};
9
10impl EncodedValuesLayout {
11	pub fn set_f32(&self, row: &mut EncodedValues, index: usize, value: impl Into<f32>) {
12		let field = &self.fields[index];
13		debug_assert!(row.len() >= self.total_static_size());
14		debug_assert_eq!(field.r#type, Type::Float4);
15		row.set_valid(index, true);
16		unsafe { ptr::write_unaligned(row.make_mut().as_mut_ptr().add(field.offset) as *mut f32, value.into()) }
17	}
18
19	pub fn get_f32(&self, row: &EncodedValues, index: usize) -> f32 {
20		let field = &self.fields[index];
21		debug_assert!(row.len() >= self.total_static_size());
22		debug_assert_eq!(field.r#type, Type::Float4);
23		unsafe { (row.as_ptr().add(field.offset) as *const f32).read_unaligned() }
24	}
25
26	pub fn try_get_f32(&self, row: &EncodedValues, index: usize) -> Option<f32> {
27		if row.is_defined(index) {
28			Some(self.get_f32(row, index))
29		} else {
30			None
31		}
32	}
33}
34
35#[cfg(test)]
36#[allow(clippy::approx_constant)]
37mod tests {
38	use reifydb_type::Type;
39
40	use crate::value::encoded::EncodedValuesLayout;
41
42	#[test]
43	fn test_set_get_f32() {
44		let layout = EncodedValuesLayout::new(&[Type::Float4]);
45		let mut row = layout.allocate();
46		layout.set_f32(&mut row, 0, 1.25f32);
47		assert_eq!(layout.get_f32(&row, 0), 1.25f32);
48	}
49
50	#[test]
51	fn test_try_get_f32() {
52		let layout = EncodedValuesLayout::new(&[Type::Float4]);
53		let mut row = layout.allocate();
54
55		assert_eq!(layout.try_get_f32(&row, 0), None);
56
57		layout.set_f32(&mut row, 0, 1.25f32);
58		assert_eq!(layout.try_get_f32(&row, 0), Some(1.25f32));
59	}
60
61	#[test]
62	fn test_special_values() {
63		let layout = EncodedValuesLayout::new(&[Type::Float4]);
64		let mut row = layout.allocate();
65
66		// Test zero
67		layout.set_f32(&mut row, 0, 0.0f32);
68		assert_eq!(layout.get_f32(&row, 0), 0.0f32);
69
70		// Test negative zero
71		let mut row2 = layout.allocate();
72		layout.set_f32(&mut row2, 0, -0.0f32);
73		assert_eq!(layout.get_f32(&row2, 0), -0.0f32);
74
75		// Test infinity
76		let mut row3 = layout.allocate();
77		layout.set_f32(&mut row3, 0, f32::INFINITY);
78		assert_eq!(layout.get_f32(&row3, 0), f32::INFINITY);
79
80		// Test negative infinity
81		let mut row4 = layout.allocate();
82		layout.set_f32(&mut row4, 0, f32::NEG_INFINITY);
83		assert_eq!(layout.get_f32(&row4, 0), f32::NEG_INFINITY);
84
85		// Test NaN
86		let mut row5 = layout.allocate();
87		layout.set_f32(&mut row5, 0, f32::NAN);
88		assert!(layout.get_f32(&row5, 0).is_nan());
89	}
90
91	#[test]
92	fn test_extreme_values() {
93		let layout = EncodedValuesLayout::new(&[Type::Float4]);
94		let mut row = layout.allocate();
95
96		layout.set_f32(&mut row, 0, f32::MAX);
97		assert_eq!(layout.get_f32(&row, 0), f32::MAX);
98
99		let mut row2 = layout.allocate();
100		layout.set_f32(&mut row2, 0, f32::MIN);
101		assert_eq!(layout.get_f32(&row2, 0), f32::MIN);
102
103		let mut row3 = layout.allocate();
104		layout.set_f32(&mut row3, 0, f32::MIN_POSITIVE);
105		assert_eq!(layout.get_f32(&row3, 0), f32::MIN_POSITIVE);
106	}
107
108	#[test]
109	fn test_mixed_with_other_types() {
110		let layout = EncodedValuesLayout::new(&[Type::Float4, Type::Int4, Type::Float4]);
111		let mut row = layout.allocate();
112
113		layout.set_f32(&mut row, 0, 3.14f32);
114		layout.set_i32(&mut row, 1, 42);
115		layout.set_f32(&mut row, 2, -2.718f32);
116
117		assert_eq!(layout.get_f32(&row, 0), 3.14f32);
118		assert_eq!(layout.get_i32(&row, 1), 42);
119		assert_eq!(layout.get_f32(&row, 2), -2.718f32);
120	}
121
122	#[test]
123	fn test_undefined_handling() {
124		let layout = EncodedValuesLayout::new(&[Type::Float4, Type::Float4]);
125		let mut row = layout.allocate();
126
127		layout.set_f32(&mut row, 0, 3.14f32);
128
129		assert_eq!(layout.try_get_f32(&row, 0), Some(3.14f32));
130		assert_eq!(layout.try_get_f32(&row, 1), None);
131
132		layout.set_undefined(&mut row, 0);
133		assert_eq!(layout.try_get_f32(&row, 0), None);
134	}
135
136	#[test]
137	fn test_subnormal_values() {
138		let layout = EncodedValuesLayout::new(&[Type::Float4]);
139		let mut row = layout.allocate();
140
141		// Test smallest positive subnormal
142		let min_subnormal = f32::from_bits(0x00000001);
143		layout.set_f32(&mut row, 0, min_subnormal);
144		assert_eq!(layout.get_f32(&row, 0).to_bits(), min_subnormal.to_bits());
145
146		// Test largest subnormal (just below MIN_POSITIVE)
147		let max_subnormal = f32::from_bits(0x007fffff);
148		layout.set_f32(&mut row, 0, max_subnormal);
149		assert_eq!(layout.get_f32(&row, 0).to_bits(), max_subnormal.to_bits());
150
151		// Test negative subnormals
152		let neg_subnormal = f32::from_bits(0x80000001);
153		layout.set_f32(&mut row, 0, neg_subnormal);
154		assert_eq!(layout.get_f32(&row, 0).to_bits(), neg_subnormal.to_bits());
155	}
156
157	#[test]
158	fn test_nan_payload_preservation() {
159		let layout = EncodedValuesLayout::new(&[Type::Float4]);
160		let mut row = layout.allocate();
161
162		// Test different NaN representations
163		let quiet_nan = f32::NAN;
164		layout.set_f32(&mut row, 0, quiet_nan);
165		assert!(layout.get_f32(&row, 0).is_nan());
166
167		// Test NaN with specific payload
168		let nan_with_payload = f32::from_bits(0x7fc00001);
169		layout.set_f32(&mut row, 0, nan_with_payload);
170		assert_eq!(layout.get_f32(&row, 0).to_bits(), nan_with_payload.to_bits());
171
172		// Test negative NaN
173		let neg_nan = f32::from_bits(0xffc00000);
174		layout.set_f32(&mut row, 0, neg_nan);
175		assert_eq!(layout.get_f32(&row, 0).to_bits(), neg_nan.to_bits());
176	}
177
178	#[test]
179	fn test_repeated_operations() {
180		let layout = EncodedValuesLayout::new(&[Type::Float4]);
181		let mut row = layout.allocate();
182		let initial_len = row.len();
183
184		// Set same field many times with different values
185		for i in 0..1000 {
186			let value = (i as f32) * 0.1;
187			layout.set_f32(&mut row, 0, value);
188			assert_eq!(layout.get_f32(&row, 0), value);
189		}
190
191		// Size shouldn't grow for static type
192		assert_eq!(row.len(), initial_len);
193	}
194
195	#[test]
196	fn test_unaligned_access() {
197		let layout = create_unaligned_layout(Type::Float4);
198		let mut row = layout.allocate();
199
200		// Test at odd offset (index 1)
201		layout.set_f32(&mut row, 1, std::f32::consts::PI);
202		assert_eq!(layout.get_f32(&row, 1), std::f32::consts::PI);
203
204		// Test at another odd offset (index 3)
205		layout.set_f32(&mut row, 3, std::f32::consts::E);
206		assert_eq!(layout.get_f32(&row, 3), std::f32::consts::E);
207
208		// Verify both values are preserved
209		assert_eq!(layout.get_f32(&row, 1), std::f32::consts::PI);
210		assert_eq!(layout.get_f32(&row, 3), std::f32::consts::E);
211	}
212
213	#[test]
214	fn test_denormalized_transitions() {
215		let layout = EncodedValuesLayout::new(&[Type::Float4]);
216		let mut row = layout.allocate();
217
218		// Test transition from normal to subnormal
219		let values = [
220			f32::MIN_POSITIVE,       // Smallest normal
221			f32::MIN_POSITIVE / 2.0, // Becomes subnormal
222			f32::MIN_POSITIVE / 4.0, // Smaller subnormal
223			0.0f32,                  // Underflows to zero
224		];
225
226		for value in values {
227			layout.set_f32(&mut row, 0, value);
228			let retrieved = layout.get_f32(&row, 0);
229			if value == 0.0 {
230				assert_eq!(retrieved, 0.0);
231			} else {
232				// For subnormals, compare bits to ensure exact
233				// preservation
234				assert_eq!(retrieved.to_bits(), value.to_bits());
235			}
236		}
237	}
238
239	/// Creates a layout with odd alignment to test unaligned access
240	pub fn create_unaligned_layout(target_type: Type) -> EncodedValuesLayout {
241		// Use Int1 (1 byte) to create odd alignment
242		EncodedValuesLayout::new(&[
243			Type::Int1,  // 1 byte offset
244			target_type, // Now at odd offset
245			Type::Int1,  // Another odd-sized field
246			target_type, /* Another instance at different odd
247			              * offset */
248		])
249	}
250}