reifydb_core/value/encoded/
blob.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 reifydb_type::{Blob, Type};
5
6use crate::value::encoded::{EncodedValues, EncodedValuesLayout};
7
8impl EncodedValuesLayout {
9	pub fn set_blob(&self, row: &mut EncodedValues, index: usize, value: &Blob) {
10		let field = &self.fields[index];
11		debug_assert_eq!(field.r#type, Type::Blob);
12		debug_assert!(!row.is_defined(index), "BLOB field {} already set", index);
13
14		let bytes = value.as_bytes();
15
16		// Calculate offset in dynamic section (relative to start of
17		// dynamic section)
18		let dynamic_offset = self.dynamic_section_size(row);
19
20		// Append blob bytes to dynamic section
21		row.0.extend_from_slice(bytes);
22
23		// Update reference in static section: [offset: u32][length:
24		// u32]
25		let ref_slice = &mut row.0.make_mut()[field.offset..field.offset + 8];
26		ref_slice[0..4].copy_from_slice(&(dynamic_offset as u32).to_le_bytes());
27		ref_slice[4..8].copy_from_slice(&(bytes.len() as u32).to_le_bytes());
28
29		row.set_valid(index, true);
30	}
31
32	pub fn get_blob(&self, row: &EncodedValues, index: usize) -> Blob {
33		let field = &self.fields[index];
34		debug_assert_eq!(field.r#type, Type::Blob);
35
36		// Read offset and length from static section
37		let ref_slice = &row.as_slice()[field.offset..field.offset + 8];
38		let offset = u32::from_le_bytes([ref_slice[0], ref_slice[1], ref_slice[2], ref_slice[3]]) as usize;
39		let length = u32::from_le_bytes([ref_slice[4], ref_slice[5], ref_slice[6], ref_slice[7]]) as usize;
40
41		// Get bytes from dynamic section
42		let dynamic_start = self.dynamic_section_start();
43		let blob_start = dynamic_start + offset;
44		let blob_slice = &row.as_slice()[blob_start..blob_start + length];
45
46		Blob::from_slice(blob_slice)
47	}
48
49	pub fn try_get_blob(&self, row: &EncodedValues, index: usize) -> Option<Blob> {
50		if row.is_defined(index) {
51			Some(self.get_blob(row, index))
52		} else {
53			None
54		}
55	}
56}
57
58#[cfg(test)]
59mod tests {
60	use reifydb_type::{Blob, Type};
61
62	use crate::value::encoded::EncodedValuesLayout;
63
64	#[test]
65	fn test_set_get_blob() {
66		let layout = EncodedValuesLayout::new(&[Type::Blob]);
67		let mut row = layout.allocate();
68
69		let blob = Blob::from_slice(&[1, 2, 3, 4, 5]);
70		layout.set_blob(&mut row, 0, &blob);
71		assert_eq!(layout.get_blob(&row, 0), blob);
72	}
73
74	#[test]
75	fn test_try_get_blob() {
76		let layout = EncodedValuesLayout::new(&[Type::Blob]);
77		let mut row = layout.allocate();
78
79		assert_eq!(layout.try_get_blob(&row, 0), None);
80
81		let blob = Blob::from_slice(&[1, 2, 3, 4, 5]);
82		layout.set_blob(&mut row, 0, &blob);
83		assert_eq!(layout.try_get_blob(&row, 0), Some(blob));
84	}
85
86	#[test]
87	fn test_empty() {
88		let layout = EncodedValuesLayout::new(&[Type::Blob]);
89		let mut row = layout.allocate();
90
91		let empty_blob = Blob::from_slice(&[]);
92		layout.set_blob(&mut row, 0, &empty_blob);
93		assert_eq!(layout.get_blob(&row, 0), empty_blob);
94		assert_eq!(layout.try_get_blob(&row, 0), Some(empty_blob));
95	}
96
97	#[test]
98	fn test_binary_data() {
99		let layout = EncodedValuesLayout::new(&[Type::Blob]);
100		let mut row = layout.allocate();
101
102		// Test with various binary data patterns
103		let binary_data = vec![
104			0x00, 0xFF, 0xAA, 0x55, 0xCC, 0x33, 0x00, 0xFF, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
105		];
106		let blob = Blob::from_slice(&binary_data);
107		layout.set_blob(&mut row, 0, &blob);
108		assert_eq!(layout.get_blob(&row, 0), blob);
109	}
110
111	#[test]
112	fn test_large_data() {
113		let layout = EncodedValuesLayout::new(&[Type::Blob]);
114		let mut row = layout.allocate();
115
116		// Create a large blob (1KB)
117		let large_data: Vec<u8> = (0..1024).map(|i| (i % 256) as u8).collect();
118		let large_blob = Blob::from_slice(&large_data);
119		layout.set_blob(&mut row, 0, &large_blob);
120		assert_eq!(layout.get_blob(&row, 0), large_blob);
121	}
122
123	#[test]
124	fn test_multiple_fields() {
125		let layout = EncodedValuesLayout::new(&[Type::Blob, Type::Blob, Type::Blob]);
126		let mut row = layout.allocate();
127
128		let blob1 = Blob::from_slice(&[1, 2, 3]);
129		let blob2 = Blob::from_slice(&[4, 5, 6, 7, 8]);
130		let blob3 = Blob::from_slice(&[9]);
131
132		layout.set_blob(&mut row, 0, &blob1);
133		layout.set_blob(&mut row, 1, &blob2);
134		layout.set_blob(&mut row, 2, &blob3);
135
136		assert_eq!(layout.get_blob(&row, 0), blob1);
137		assert_eq!(layout.get_blob(&row, 1), blob2);
138		assert_eq!(layout.get_blob(&row, 2), blob3);
139	}
140
141	#[test]
142	fn test_mixed_with_static_fields() {
143		let layout = EncodedValuesLayout::new(&[Type::Boolean, Type::Blob, Type::Int4, Type::Blob]);
144		let mut row = layout.allocate();
145
146		let blob1 = Blob::from_slice(&[0xFF, 0x00, 0xAA]);
147		let blob2 = Blob::from_slice(&[0x11, 0x22, 0x33, 0x44]);
148
149		layout.set_bool(&mut row, 0, true);
150		layout.set_blob(&mut row, 1, &blob1);
151		layout.set_i32(&mut row, 2, -12345);
152		layout.set_blob(&mut row, 3, &blob2);
153
154		assert_eq!(layout.get_bool(&row, 0), true);
155		assert_eq!(layout.get_blob(&row, 1), blob1);
156		assert_eq!(layout.get_i32(&row, 2), -12345);
157		assert_eq!(layout.get_blob(&row, 3), blob2);
158	}
159
160	#[test]
161	fn test_different_sizes() {
162		let layout = EncodedValuesLayout::new(&[Type::Blob, Type::Blob, Type::Blob]);
163		let mut row = layout.allocate();
164
165		let empty_blob = Blob::from_slice(&[]);
166		let medium_blob = Blob::from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
167		let single_byte_blob = Blob::from_slice(&[42]);
168
169		layout.set_blob(&mut row, 0, &empty_blob);
170		layout.set_blob(&mut row, 1, &medium_blob);
171		layout.set_blob(&mut row, 2, &single_byte_blob);
172
173		assert_eq!(layout.get_blob(&row, 0), empty_blob);
174		assert_eq!(layout.get_blob(&row, 1), medium_blob);
175		assert_eq!(layout.get_blob(&row, 2), single_byte_blob);
176	}
177
178	#[test]
179	fn test_arbitrary_setting_order() {
180		let layout = EncodedValuesLayout::new(&[Type::Blob, Type::Blob, Type::Blob, Type::Blob]);
181		let mut row = layout.allocate();
182
183		let blob0 = Blob::from_slice(&[10, 20]);
184		let blob1 = Blob::from_slice(&[30, 40, 50]);
185		let blob2 = Blob::from_slice(&[60]);
186		let blob3 = Blob::from_slice(&[70, 80, 90, 100]);
187
188		// Set in reverse order
189		layout.set_blob(&mut row, 3, &blob3);
190		layout.set_blob(&mut row, 1, &blob1);
191		layout.set_blob(&mut row, 0, &blob0);
192		layout.set_blob(&mut row, 2, &blob2);
193
194		assert_eq!(layout.get_blob(&row, 0), blob0);
195		assert_eq!(layout.get_blob(&row, 1), blob1);
196		assert_eq!(layout.get_blob(&row, 2), blob2);
197		assert_eq!(layout.get_blob(&row, 3), blob3);
198	}
199
200	#[test]
201	fn test_undefined_handling() {
202		let layout = EncodedValuesLayout::new(&[Type::Blob, Type::Blob, Type::Blob]);
203		let mut row = layout.allocate();
204
205		let blob = Blob::from_slice(&[1, 2, 3, 4]);
206
207		// Set only some fields
208		layout.set_blob(&mut row, 0, &blob);
209		layout.set_blob(&mut row, 2, &blob);
210
211		assert_eq!(layout.try_get_blob(&row, 0), Some(blob.clone()));
212		assert_eq!(layout.try_get_blob(&row, 1), None);
213		assert_eq!(layout.try_get_blob(&row, 2), Some(blob.clone()));
214
215		// Set field as undefined
216		layout.set_undefined(&mut row, 0);
217		assert_eq!(layout.try_get_blob(&row, 0), None);
218		assert_eq!(layout.try_get_blob(&row, 2), Some(blob));
219	}
220
221	#[test]
222	fn test_all_byte_values() {
223		let layout = EncodedValuesLayout::new(&[Type::Blob]);
224		let mut row = layout.allocate();
225
226		// Create blob with all possible byte values (0-255)
227		let all_bytes: Vec<u8> = (0..=255).collect();
228		let full_range_blob = Blob::from_slice(&all_bytes);
229		layout.set_blob(&mut row, 0, &full_range_blob);
230		assert_eq!(layout.get_blob(&row, 0), full_range_blob);
231	}
232}