Skip to main content

reifydb_core/encoded/
blob.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use reifydb_type::value::{blob::Blob, r#type::Type};
5
6use crate::encoded::{row::EncodedRow, shape::RowShape};
7
8impl RowShape {
9	pub fn set_blob(&self, row: &mut EncodedRow, index: usize, value: &Blob) {
10		debug_assert_eq!(*self.fields()[index].constraint.get_type().inner_type(), Type::Blob);
11		self.replace_dynamic_data(row, index, value.as_bytes());
12	}
13
14	pub fn get_blob(&self, row: &EncodedRow, index: usize) -> Blob {
15		let field = &self.fields()[index];
16		debug_assert_eq!(*field.constraint.get_type().inner_type(), Type::Blob);
17
18		let ref_slice = &row.as_slice()[field.offset as usize..field.offset as usize + 8];
19		let offset = u32::from_le_bytes([ref_slice[0], ref_slice[1], ref_slice[2], ref_slice[3]]) as usize;
20		let length = u32::from_le_bytes([ref_slice[4], ref_slice[5], ref_slice[6], ref_slice[7]]) as usize;
21
22		let dynamic_start = self.dynamic_section_start();
23		let blob_start = dynamic_start + offset;
24		let blob_slice = &row.as_slice()[blob_start..blob_start + length];
25
26		Blob::from_slice(blob_slice)
27	}
28
29	pub fn try_get_blob(&self, row: &EncodedRow, index: usize) -> Option<Blob> {
30		if row.is_defined(index) && self.fields()[index].constraint.get_type() == Type::Blob {
31			Some(self.get_blob(row, index))
32		} else {
33			None
34		}
35	}
36}
37
38#[cfg(test)]
39pub mod tests {
40	use reifydb_type::value::{blob::Blob, r#type::Type};
41
42	use crate::encoded::shape::RowShape;
43
44	#[test]
45	fn test_set_get_blob() {
46		let shape = RowShape::testing(&[Type::Blob]);
47		let mut row = shape.allocate();
48
49		let blob = Blob::from_slice(&[1, 2, 3, 4, 5]);
50		shape.set_blob(&mut row, 0, &blob);
51		assert_eq!(shape.get_blob(&row, 0), blob);
52	}
53
54	#[test]
55	fn test_try_get_blob() {
56		let shape = RowShape::testing(&[Type::Blob]);
57		let mut row = shape.allocate();
58
59		assert_eq!(shape.try_get_blob(&row, 0), None);
60
61		let blob = Blob::from_slice(&[1, 2, 3, 4, 5]);
62		shape.set_blob(&mut row, 0, &blob);
63		assert_eq!(shape.try_get_blob(&row, 0), Some(blob));
64	}
65
66	#[test]
67	fn test_empty() {
68		let shape = RowShape::testing(&[Type::Blob]);
69		let mut row = shape.allocate();
70
71		let empty_blob = Blob::from_slice(&[]);
72		shape.set_blob(&mut row, 0, &empty_blob);
73		assert_eq!(shape.get_blob(&row, 0), empty_blob);
74		assert_eq!(shape.try_get_blob(&row, 0), Some(empty_blob));
75	}
76
77	#[test]
78	fn test_binary_data() {
79		let shape = RowShape::testing(&[Type::Blob]);
80		let mut row = shape.allocate();
81
82		// Test with various binary data patterns
83		let binary_data = vec![
84			0x00, 0xFF, 0xAA, 0x55, 0xCC, 0x33, 0x00, 0xFF, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
85		];
86		let blob = Blob::from_slice(&binary_data);
87		shape.set_blob(&mut row, 0, &blob);
88		assert_eq!(shape.get_blob(&row, 0), blob);
89	}
90
91	#[test]
92	fn test_large_data() {
93		let shape = RowShape::testing(&[Type::Blob]);
94		let mut row = shape.allocate();
95
96		// Create a large blob (1KB)
97		let large_data: Vec<u8> = (0..1024).map(|i| (i % 256) as u8).collect();
98		let large_blob = Blob::from_slice(&large_data);
99		shape.set_blob(&mut row, 0, &large_blob);
100		assert_eq!(shape.get_blob(&row, 0), large_blob);
101	}
102
103	#[test]
104	fn test_multiple_fields() {
105		let shape = RowShape::testing(&[Type::Blob, Type::Blob, Type::Blob]);
106		let mut row = shape.allocate();
107
108		let blob1 = Blob::from_slice(&[1, 2, 3]);
109		let blob2 = Blob::from_slice(&[4, 5, 6, 7, 8]);
110		let blob3 = Blob::from_slice(&[9]);
111
112		shape.set_blob(&mut row, 0, &blob1);
113		shape.set_blob(&mut row, 1, &blob2);
114		shape.set_blob(&mut row, 2, &blob3);
115
116		assert_eq!(shape.get_blob(&row, 0), blob1);
117		assert_eq!(shape.get_blob(&row, 1), blob2);
118		assert_eq!(shape.get_blob(&row, 2), blob3);
119	}
120
121	#[test]
122	fn test_mixed_with_static_fields() {
123		let shape = RowShape::testing(&[Type::Boolean, Type::Blob, Type::Int4, Type::Blob]);
124		let mut row = shape.allocate();
125
126		let blob1 = Blob::from_slice(&[0xFF, 0x00, 0xAA]);
127		let blob2 = Blob::from_slice(&[0x11, 0x22, 0x33, 0x44]);
128
129		shape.set_bool(&mut row, 0, true);
130		shape.set_blob(&mut row, 1, &blob1);
131		shape.set_i32(&mut row, 2, -12345);
132		shape.set_blob(&mut row, 3, &blob2);
133
134		assert_eq!(shape.get_bool(&row, 0), true);
135		assert_eq!(shape.get_blob(&row, 1), blob1);
136		assert_eq!(shape.get_i32(&row, 2), -12345);
137		assert_eq!(shape.get_blob(&row, 3), blob2);
138	}
139
140	#[test]
141	fn test_different_sizes() {
142		let shape = RowShape::testing(&[Type::Blob, Type::Blob, Type::Blob]);
143		let mut row = shape.allocate();
144
145		let empty_blob = Blob::from_slice(&[]);
146		let medium_blob = Blob::from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
147		let single_byte_blob = Blob::from_slice(&[42]);
148
149		shape.set_blob(&mut row, 0, &empty_blob);
150		shape.set_blob(&mut row, 1, &medium_blob);
151		shape.set_blob(&mut row, 2, &single_byte_blob);
152
153		assert_eq!(shape.get_blob(&row, 0), empty_blob);
154		assert_eq!(shape.get_blob(&row, 1), medium_blob);
155		assert_eq!(shape.get_blob(&row, 2), single_byte_blob);
156	}
157
158	#[test]
159	fn test_arbitrary_setting_order() {
160		let shape = RowShape::testing(&[Type::Blob, Type::Blob, Type::Blob, Type::Blob]);
161		let mut row = shape.allocate();
162
163		let blob0 = Blob::from_slice(&[10, 20]);
164		let blob1 = Blob::from_slice(&[30, 40, 50]);
165		let blob2 = Blob::from_slice(&[60]);
166		let blob3 = Blob::from_slice(&[70, 80, 90, 100]);
167
168		// Set in reverse order
169		shape.set_blob(&mut row, 3, &blob3);
170		shape.set_blob(&mut row, 1, &blob1);
171		shape.set_blob(&mut row, 0, &blob0);
172		shape.set_blob(&mut row, 2, &blob2);
173
174		assert_eq!(shape.get_blob(&row, 0), blob0);
175		assert_eq!(shape.get_blob(&row, 1), blob1);
176		assert_eq!(shape.get_blob(&row, 2), blob2);
177		assert_eq!(shape.get_blob(&row, 3), blob3);
178	}
179
180	#[test]
181	fn test_undefined_handling() {
182		let shape = RowShape::testing(&[Type::Blob, Type::Blob, Type::Blob]);
183		let mut row = shape.allocate();
184
185		let blob = Blob::from_slice(&[1, 2, 3, 4]);
186
187		// Set only some fields
188		shape.set_blob(&mut row, 0, &blob);
189		shape.set_blob(&mut row, 2, &blob);
190
191		assert_eq!(shape.try_get_blob(&row, 0), Some(blob.clone()));
192		assert_eq!(shape.try_get_blob(&row, 1), None);
193		assert_eq!(shape.try_get_blob(&row, 2), Some(blob.clone()));
194
195		// Set field as undefined
196		shape.set_none(&mut row, 0);
197		assert_eq!(shape.try_get_blob(&row, 0), None);
198		assert_eq!(shape.try_get_blob(&row, 2), Some(blob));
199	}
200
201	#[test]
202	fn test_all_byte_values() {
203		let shape = RowShape::testing(&[Type::Blob]);
204		let mut row = shape.allocate();
205
206		// Create blob with all possible byte values (0-255)
207		let all_bytes: Vec<u8> = (0..=255).collect();
208		let full_range_blob = Blob::from_slice(&all_bytes);
209		shape.set_blob(&mut row, 0, &full_range_blob);
210		assert_eq!(shape.get_blob(&row, 0), full_range_blob);
211	}
212
213	#[test]
214	fn test_try_get_blob_wrong_type() {
215		let shape = RowShape::testing(&[Type::Boolean]);
216		let mut row = shape.allocate();
217
218		shape.set_bool(&mut row, 0, true);
219
220		assert_eq!(shape.try_get_blob(&row, 0), None);
221	}
222
223	#[test]
224	fn test_update_blob() {
225		let shape = RowShape::testing(&[Type::Blob]);
226		let mut row = shape.allocate();
227
228		let blob1 = Blob::from_slice(&[1, 2, 3]);
229		shape.set_blob(&mut row, 0, &blob1);
230		assert_eq!(shape.get_blob(&row, 0), blob1);
231
232		// Overwrite with larger blob
233		let blob2 = Blob::from_slice(&[4, 5, 6, 7, 8]);
234		shape.set_blob(&mut row, 0, &blob2);
235		assert_eq!(shape.get_blob(&row, 0), blob2);
236
237		// Overwrite with smaller blob
238		let blob3 = Blob::from_slice(&[9]);
239		shape.set_blob(&mut row, 0, &blob3);
240		assert_eq!(shape.get_blob(&row, 0), blob3);
241		assert_eq!(row.len(), shape.total_static_size() + 1);
242
243		// Overwrite with empty blob
244		let empty = Blob::from_slice(&[]);
245		shape.set_blob(&mut row, 0, &empty);
246		assert_eq!(shape.get_blob(&row, 0), empty);
247		assert_eq!(row.len(), shape.total_static_size());
248	}
249
250	#[test]
251	fn test_update_blob_with_other_dynamic_fields() {
252		let shape = RowShape::testing(&[Type::Blob, Type::Utf8, Type::Blob]);
253		let mut row = shape.allocate();
254
255		shape.set_blob(&mut row, 0, &Blob::from_slice(&[1, 2, 3]));
256		shape.set_utf8(&mut row, 1, "hello");
257		shape.set_blob(&mut row, 2, &Blob::from_slice(&[4, 5]));
258
259		// Update first blob
260		shape.set_blob(&mut row, 0, &Blob::from_slice(&[10, 20, 30, 40, 50]));
261
262		assert_eq!(shape.get_blob(&row, 0), Blob::from_slice(&[10, 20, 30, 40, 50]));
263		assert_eq!(shape.get_utf8(&row, 1), "hello");
264		assert_eq!(shape.get_blob(&row, 2), Blob::from_slice(&[4, 5]));
265	}
266}