Skip to main content

reifydb_value/value/container/
blob.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 ReifyDB
3
4use std::{
5	fmt::{self, Debug},
6	result::Result as StdResult,
7};
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11use crate::{
12	Result,
13	storage::{Cow, Storage},
14	value::{Value, blob::Blob, container::varlen::VarlenContainer, value_type::ValueType},
15};
16
17pub struct BlobContainer<S: Storage = Cow> {
18	inner: VarlenContainer<S>,
19}
20
21impl<S: Storage> Clone for BlobContainer<S> {
22	fn clone(&self) -> Self {
23		Self {
24			inner: self.inner.clone(),
25		}
26	}
27}
28
29impl<S: Storage> Debug for BlobContainer<S>
30where
31	VarlenContainer<S>: Debug,
32{
33	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34		f.debug_struct("BlobContainer").field("inner", &self.inner).finish()
35	}
36}
37
38impl<S: Storage> PartialEq for BlobContainer<S>
39where
40	VarlenContainer<S>: PartialEq,
41{
42	fn eq(&self, other: &Self) -> bool {
43		self.inner == other.inner
44	}
45}
46
47impl Serialize for BlobContainer<Cow> {
48	fn serialize<Ser: Serializer>(&self, serializer: Ser) -> StdResult<Ser::Ok, Ser::Error> {
49		self.inner.serialize(serializer)
50	}
51}
52
53impl<'de> Deserialize<'de> for BlobContainer<Cow> {
54	fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
55		let inner = VarlenContainer::deserialize(deserializer)?;
56		Ok(Self {
57			inner,
58		})
59	}
60}
61
62impl BlobContainer<Cow> {
63	pub fn new(data: Vec<Blob>) -> Self {
64		Self::from_vec(data)
65	}
66
67	pub fn from_vec(data: Vec<Blob>) -> Self {
68		let inner = VarlenContainer::from_byte_slices(data.iter().map(|b| b.as_bytes()));
69		Self {
70			inner,
71		}
72	}
73
74	pub fn with_capacity(capacity: usize) -> Self {
75		Self {
76			inner: VarlenContainer::with_capacity(capacity, capacity * 32),
77		}
78	}
79
80	pub fn from_bytes_offsets(data: Vec<u8>, offsets: Vec<u64>) -> Self {
81		Self {
82			inner: VarlenContainer::from_raw_parts(data, offsets),
83		}
84	}
85}
86
87impl<S: Storage> BlobContainer<S> {
88	pub fn from_inner(inner: VarlenContainer<S>) -> Self {
89		Self {
90			inner,
91		}
92	}
93
94	pub fn from_storage_parts(data: S::Vec<u8>, offsets: S::Vec<u64>) -> Self {
95		Self {
96			inner: VarlenContainer::from_storage_parts(data, offsets),
97		}
98	}
99
100	pub fn data_storage(&self) -> &S::Vec<u8> {
101		self.inner.data()
102	}
103
104	pub fn offsets_storage(&self) -> &S::Vec<u64> {
105		self.inner.offsets_data()
106	}
107
108	pub fn len(&self) -> usize {
109		self.inner.len()
110	}
111
112	pub fn capacity(&self) -> usize {
113		self.inner.capacity()
114	}
115
116	pub fn is_empty(&self) -> bool {
117		self.inner.is_empty()
118	}
119
120	pub fn clear(&mut self) {
121		self.inner.clear_generic();
122	}
123
124	pub fn get(&self, index: usize) -> Option<&[u8]> {
125		self.inner.get_bytes(index)
126	}
127
128	pub fn is_defined(&self, idx: usize) -> bool {
129		idx < self.len()
130	}
131
132	pub fn data_bytes(&self) -> &[u8] {
133		self.inner.data_bytes()
134	}
135
136	pub fn offsets(&self) -> &[u64] {
137		self.inner.offsets()
138	}
139
140	pub fn inner(&self) -> &VarlenContainer<S> {
141		&self.inner
142	}
143
144	pub fn as_string(&self, index: usize) -> String {
145		match self.get(index) {
146			Some(bytes) => Blob::new(bytes.to_vec()).to_string(),
147			None => "none".to_string(),
148		}
149	}
150
151	pub fn get_value(&self, index: usize) -> Value {
152		match self.get(index) {
153			Some(bytes) => Value::Blob(Blob::new(bytes.to_vec())),
154			None => Value::none_of(ValueType::Blob),
155		}
156	}
157
158	pub fn iter(&self) -> impl Iterator<Item = Option<&[u8]>> + '_ {
159		(0..self.len()).map(|i| self.get(i))
160	}
161
162	pub fn iter_bytes(&self) -> impl Iterator<Item = &[u8]> + '_ {
163		(0..self.len()).map(|i| self.get(i).unwrap_or(&[]))
164	}
165}
166
167impl BlobContainer<Cow> {
168	pub fn push(&mut self, value: Blob) {
169		self.inner.push_bytes(value.as_bytes());
170	}
171
172	pub fn push_bytes(&mut self, value: &[u8]) {
173		self.inner.push_bytes(value);
174	}
175
176	pub fn push_default(&mut self) {
177		self.inner.push_bytes(&[]);
178	}
179
180	pub fn extend(&mut self, other: &Self) -> Result<()> {
181		self.inner.extend_from(&other.inner);
182		Ok(())
183	}
184
185	pub fn slice(&self, start: usize, end: usize) -> Self {
186		Self {
187			inner: self.inner.slice(start, end),
188		}
189	}
190
191	pub fn filter(&mut self, mask: &<Cow as Storage>::BitVec) {
192		let bits: Vec<bool> = mask.iter().collect();
193		self.inner.filter_in_place(|i| bits.get(i).copied().unwrap_or(false));
194	}
195
196	pub fn reorder(&mut self, indices: &[usize]) {
197		self.inner.reorder_in_place(indices);
198	}
199
200	pub fn take(&self, num: usize) -> Self {
201		Self {
202			inner: self.inner.take_n(num),
203		}
204	}
205}
206
207impl Default for BlobContainer<Cow> {
208	fn default() -> Self {
209		Self::with_capacity(0)
210	}
211}
212
213#[cfg(test)]
214pub mod tests {
215	use postcard::to_allocvec as postcard_to_allocvec;
216
217	use super::*;
218
219	#[test]
220	fn test_new() {
221		let blob1 = Blob::new(vec![1, 2, 3]);
222		let blob2 = Blob::new(vec![4, 5, 6]);
223		let blobs = vec![blob1.clone(), blob2.clone()];
224		let container = BlobContainer::new(blobs);
225
226		assert_eq!(container.len(), 2);
227		assert_eq!(container.get(0), Some(blob1.as_bytes()));
228		assert_eq!(container.get(1), Some(blob2.as_bytes()));
229	}
230
231	#[test]
232	fn test_from_vec() {
233		let blob1 = Blob::new(vec![10, 20, 30]);
234		let blob2 = Blob::new(vec![40, 50]);
235		let blobs = vec![blob1.clone(), blob2.clone()];
236		let container = BlobContainer::from_vec(blobs);
237
238		assert_eq!(container.len(), 2);
239		assert_eq!(container.get(0), Some(blob1.as_bytes()));
240		assert_eq!(container.get(1), Some(blob2.as_bytes()));
241
242		for i in 0..2 {
243			assert!(container.is_defined(i));
244		}
245	}
246
247	#[test]
248	fn test_with_capacity() {
249		let container = BlobContainer::with_capacity(10);
250		assert_eq!(container.len(), 0);
251		assert!(container.is_empty());
252		assert!(container.capacity() >= 10);
253	}
254
255	#[test]
256	fn test_push_with_default() {
257		let mut container = BlobContainer::with_capacity(3);
258		let blob1 = Blob::new(vec![1, 2, 3]);
259		let blob2 = Blob::new(vec![7, 8, 9]);
260
261		container.push(blob1.clone());
262		container.push_default();
263		container.push(blob2.clone());
264
265		assert_eq!(container.len(), 3);
266		assert_eq!(container.get(0), Some(blob1.as_bytes()));
267		assert_eq!(container.get(1), Some(b"".as_slice()));
268		assert_eq!(container.get(2), Some(blob2.as_bytes()));
269
270		assert!(container.is_defined(0));
271		assert!(container.is_defined(1));
272		assert!(container.is_defined(2));
273	}
274
275	#[test]
276	fn testault() {
277		let container = BlobContainer::default();
278		assert_eq!(container.len(), 0);
279		assert!(container.is_empty());
280	}
281
282	#[test]
283	fn test_data_bytes_and_offsets_match_zero_copy_layout() {
284		let container = BlobContainer::from_vec(vec![Blob::new(vec![0xAA, 0xBB]), Blob::new(vec![0xCC])]);
285		assert_eq!(container.data_bytes(), &[0xAAu8, 0xBB, 0xCC]);
286		assert_eq!(container.offsets(), &[0u64, 2, 3]);
287	}
288
289	#[test]
290	fn test_postcard_wire_compat() {
291		// The inner VarlenContainer is byte-compatible with `Vec<Vec<u8>>`
292		// via postcard. `Blob` derefs to `Vec<u8>`, so a `Vec<Blob>` is
293		// also byte-compatible.
294		let blobs = vec![Blob::new(vec![1, 2, 3]), Blob::new(vec![4, 5])];
295		let blobs_bytes: Vec<u8> = postcard_to_allocvec(&blobs).unwrap();
296
297		let container = BlobContainer::from_vec(blobs.clone());
298		let container_bytes: Vec<u8> = postcard_to_allocvec(&container).unwrap();
299
300		assert_eq!(blobs_bytes, container_bytes);
301	}
302}