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