Skip to main content

reifydb_value/value/container/
varlen.rs

1// SPDX-License-Identifier: Apache-2.0
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, ser::SerializeSeq};
10use serde_bytes::{ByteBuf, Bytes};
11
12use crate::reifydb_assertions;
13
14pub struct VarlenContainer {
15	data: Vec<u8>,
16	offsets: Vec<u64>,
17}
18
19impl Clone for VarlenContainer {
20	fn clone(&self) -> Self {
21		Self {
22			data: self.data.clone(),
23			offsets: self.offsets.clone(),
24		}
25	}
26}
27
28impl Debug for VarlenContainer {
29	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30		f.debug_struct("VarlenContainer")
31			.field("len", &self.len())
32			.field("data_bytes", &self.data.len())
33			.finish()
34	}
35}
36
37impl PartialEq for VarlenContainer {
38	fn eq(&self, other: &Self) -> bool {
39		if self.len() != other.len() {
40			return false;
41		}
42		for i in 0..self.len() {
43			if self.get_bytes(i) != other.get_bytes(i) {
44				return false;
45			}
46		}
47		true
48	}
49}
50
51impl VarlenContainer {
52	pub fn from_byte_slices<'a, I>(items: I) -> Self
53	where
54		I: IntoIterator<Item = &'a [u8]>,
55	{
56		let mut offsets: Vec<u64> = vec![0];
57		let mut data: Vec<u8> = Vec::new();
58		for item in items {
59			data.extend_from_slice(item);
60			offsets.push(data.len() as u64);
61		}
62		Self {
63			data,
64			offsets,
65		}
66	}
67
68	pub fn from_repeated_bytes(item: &[u8], count: usize) -> Self {
69		let mut data: Vec<u8> = Vec::with_capacity(item.len() * count);
70		let mut offsets: Vec<u64> = Vec::with_capacity(count + 1);
71		offsets.push(0);
72		for _ in 0..count {
73			data.extend_from_slice(item);
74			offsets.push(data.len() as u64);
75		}
76		Self {
77			data,
78			offsets,
79		}
80	}
81
82	pub fn with_capacity(item_capacity: usize, data_capacity: usize) -> Self {
83		let mut offsets = Vec::with_capacity(item_capacity + 1);
84		offsets.push(0);
85		Self {
86			data: Vec::with_capacity(data_capacity),
87			offsets,
88		}
89	}
90
91	pub fn empty() -> Self {
92		Self {
93			data: Vec::new(),
94			offsets: vec![0],
95		}
96	}
97
98	pub fn from_raw_parts(data: Vec<u8>, offsets: Vec<u64>) -> Self {
99		reifydb_assertions! {
100			assert!(!offsets.is_empty(), "offsets must always have offsets[0] = 0");
101			assert_eq!(offsets[0], 0, "offsets[0] must be 0");
102			assert_eq!(*offsets.last().unwrap() as usize, data.len(), "offsets[len] must equal data.len()");
103		}
104		Self {
105			data,
106			offsets,
107		}
108	}
109}
110
111impl VarlenContainer {
112	pub fn from_storage_parts(data: Vec<u8>, offsets: Vec<u64>) -> Self {
113		reifydb_assertions! {
114			assert!(
115				!offsets.is_empty(),
116				"offsets must always include the leading 0; got empty offsets"
117			);
118		}
119		Self {
120			data,
121			offsets,
122		}
123	}
124
125	pub fn len(&self) -> usize {
126		self.offsets.len().saturating_sub(1)
127	}
128
129	pub fn is_empty(&self) -> bool {
130		self.len() == 0
131	}
132
133	pub fn data_byte_len(&self) -> usize {
134		self.data.len()
135	}
136
137	pub fn data_bytes(&self) -> &[u8] {
138		self.data.as_slice()
139	}
140
141	pub fn offsets(&self) -> &[u64] {
142		self.offsets.as_slice()
143	}
144
145	pub fn get_bytes(&self, idx: usize) -> Option<&[u8]> {
146		if idx >= self.len() {
147			return None;
148		}
149		let start = self.offsets.as_slice()[idx] as usize;
150		let end = self.offsets.as_slice()[idx + 1] as usize;
151		self.data.as_slice().get(start..end)
152	}
153
154	pub fn capacity(&self) -> usize {
155		self.offsets.capacity().saturating_sub(1)
156	}
157
158	pub fn heap_size(&self) -> usize {
159		self.data.capacity() + self.offsets.capacity() * size_of::<u64>()
160	}
161
162	pub fn data(&self) -> &Vec<u8> {
163		&self.data
164	}
165
166	pub fn offsets_data(&self) -> &Vec<u64> {
167		&self.offsets
168	}
169
170	pub fn clear_generic(&mut self) {
171		self.data.clear();
172		self.offsets.clear();
173		self.offsets.push(0u64);
174	}
175}
176
177impl VarlenContainer {
178	pub fn clear(&mut self) {
179		let data_mut = &mut self.data;
180		data_mut.clear();
181		let offsets_mut = &mut self.offsets;
182		offsets_mut.clear();
183		offsets_mut.push(0);
184	}
185
186	pub fn push_bytes(&mut self, bytes: &[u8]) {
187		let data_mut = &mut self.data;
188		data_mut.extend_from_slice(bytes);
189		let new_end = data_mut.len() as u64;
190		self.offsets.push(new_end);
191	}
192
193	pub fn extend_from(&mut self, other: &Self) {
194		let base = self.data.len() as u64;
195		let other_offsets = other.offsets.as_slice();
196		let other_data = other.data.as_slice();
197
198		let data_mut = &mut self.data;
199		data_mut.extend_from_slice(other_data);
200
201		let offsets_mut = &mut self.offsets;
202
203		offsets_mut.extend(other_offsets.iter().skip(1).map(|&o| base + o));
204	}
205
206	pub fn filter_in_place<F: FnMut(usize) -> bool>(&mut self, mut keep: F) {
207		let len = self.len();
208		let mut new_data = Vec::with_capacity(self.data_byte_len());
209		let mut new_offsets = Vec::with_capacity(len + 1);
210		new_offsets.push(0);
211		for i in 0..len {
212			if keep(i) {
213				let bytes = self.get_bytes(i).unwrap_or(&[]);
214				new_data.extend_from_slice(bytes);
215				new_offsets.push(new_data.len() as u64);
216			}
217		}
218		self.data = new_data;
219		self.offsets = new_offsets;
220	}
221
222	pub fn reorder_in_place(&mut self, indices: &[usize]) {
223		let mut new_data = Vec::with_capacity(self.data_byte_len());
224		let mut new_offsets = Vec::with_capacity(indices.len() + 1);
225		new_offsets.push(0);
226		for &idx in indices {
227			let bytes = self.get_bytes(idx).unwrap_or(&[]);
228			new_data.extend_from_slice(bytes);
229			new_offsets.push(new_data.len() as u64);
230		}
231		self.data = new_data;
232		self.offsets = new_offsets;
233	}
234
235	pub fn take_n(&self, n: usize) -> Self {
236		let n = n.min(self.len());
237		let end_byte = self.offsets.as_slice()[n] as usize;
238		let new_data: Vec<u8> = self.data.as_slice()[..end_byte].to_vec();
239		let new_offsets: Vec<u64> = self.offsets.as_slice()[..=n].to_vec();
240		Self::from_raw_parts(new_data, new_offsets)
241	}
242
243	pub fn slice(&self, start: usize, end: usize) -> Self {
244		let len = self.len();
245		let start = start.min(len);
246		let end = end.min(len);
247		if start >= end {
248			return Self::empty();
249		}
250		let start_byte = self.offsets.as_slice()[start] as usize;
251		let end_byte = self.offsets.as_slice()[end] as usize;
252		let new_data: Vec<u8> = self.data.as_slice()[start_byte..end_byte].to_vec();
253
254		let new_offsets: Vec<u64> =
255			self.offsets.as_slice()[start..=end].iter().map(|o| *o - start_byte as u64).collect();
256		Self::from_raw_parts(new_data, new_offsets)
257	}
258}
259
260impl Serialize for VarlenContainer {
261	fn serialize<Ser: Serializer>(&self, serializer: Ser) -> StdResult<Ser::Ok, Ser::Error> {
262		let mut seq = serializer.serialize_seq(Some(self.len()))?;
263		for i in 0..self.len() {
264			let bytes = self.get_bytes(i).unwrap_or(&[]);
265			seq.serialize_element(Bytes::new(bytes))?;
266		}
267		seq.end()
268	}
269}
270
271impl<'de> Deserialize<'de> for VarlenContainer {
272	fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
273		let items: Vec<ByteBuf> = Vec::deserialize(deserializer)?;
274		let total: usize = items.iter().map(|b| b.len()).sum();
275		let mut data = Vec::with_capacity(total);
276		let mut offsets = Vec::with_capacity(items.len() + 1);
277		offsets.push(0);
278		for item in items {
279			data.extend_from_slice(item.as_slice());
280			offsets.push(data.len() as u64);
281		}
282		Ok(Self::from_raw_parts(data, offsets))
283	}
284}
285
286impl Default for VarlenContainer {
287	fn default() -> Self {
288		Self::empty()
289	}
290}
291
292#[cfg(test)]
293mod tests {
294	use postcard::{from_bytes as postcard_from_bytes, to_allocvec as postcard_to_allocvec};
295
296	use super::*;
297
298	#[test]
299	fn empty_has_zero_len_and_offsets_with_one_zero() {
300		let c = VarlenContainer::empty();
301		assert_eq!(c.len(), 0);
302		assert_eq!(c.offsets(), &[0u64]);
303		assert!(c.data_bytes().is_empty());
304	}
305
306	#[test]
307	fn push_bytes_appends_and_updates_offsets() {
308		let mut c = VarlenContainer::empty();
309		c.push_bytes(b"hello");
310		c.push_bytes(b"");
311		c.push_bytes(b"world");
312		assert_eq!(c.len(), 3);
313		assert_eq!(c.offsets(), &[0u64, 5, 5, 10]);
314		assert_eq!(c.data_bytes(), b"helloworld");
315		assert_eq!(c.get_bytes(0), Some(b"hello".as_slice()));
316		assert_eq!(c.get_bytes(1), Some(b"".as_slice()));
317		assert_eq!(c.get_bytes(2), Some(b"world".as_slice()));
318		assert_eq!(c.get_bytes(3), None);
319	}
320
321	#[test]
322	fn from_byte_slices_round_trip() {
323		let c = VarlenContainer::from_byte_slices([b"a".as_slice(), b"bc", b"def"]);
324		assert_eq!(c.len(), 3);
325		assert_eq!(c.data_bytes(), b"abcdef");
326		assert_eq!(c.offsets(), &[0u64, 1, 3, 6]);
327	}
328
329	#[test]
330	fn from_repeated_bytes_matches_explicit_copies() {
331		let repeated = VarlenContainer::from_repeated_bytes(b"abc", 3);
332		let explicit = VarlenContainer::from_byte_slices([b"abc".as_slice(), b"abc", b"abc"]);
333		assert_eq!(repeated, explicit);
334		assert_eq!(repeated.len(), 3);
335		assert_eq!(repeated.data_bytes(), b"abcabcabc");
336		assert_eq!(repeated.offsets(), &[0u64, 3, 6, 9]);
337	}
338
339	#[test]
340	fn from_repeated_bytes_zero_count_is_empty() {
341		let c = VarlenContainer::from_repeated_bytes(b"abc", 0);
342		assert_eq!(c.len(), 0);
343		assert_eq!(c.offsets(), &[0u64]);
344		assert!(c.data_bytes().is_empty());
345	}
346
347	#[test]
348	fn from_repeated_bytes_empty_item_keeps_count() {
349		let c = VarlenContainer::from_repeated_bytes(b"", 4);
350		assert_eq!(c.len(), 4);
351		assert_eq!(c.get_bytes(0), Some(b"".as_slice()));
352		assert_eq!(c.get_bytes(3), Some(b"".as_slice()));
353		assert!(c.data_bytes().is_empty());
354	}
355
356	#[test]
357	fn clear_resets_to_empty_state() {
358		let mut c = VarlenContainer::from_byte_slices([b"x".as_slice(), b"y"]);
359		c.clear();
360		assert_eq!(c.len(), 0);
361		assert_eq!(c.offsets(), &[0u64]);
362		assert!(c.data_bytes().is_empty());
363	}
364
365	#[test]
366	fn extend_from_concatenates_and_rebases_offsets() {
367		let mut a = VarlenContainer::from_byte_slices([b"foo".as_slice()]);
368		let b = VarlenContainer::from_byte_slices([b"bar".as_slice(), b"baz"]);
369		a.extend_from(&b);
370		assert_eq!(a.len(), 3);
371		assert_eq!(a.data_bytes(), b"foobarbaz");
372		assert_eq!(a.offsets(), &[0u64, 3, 6, 9]);
373	}
374
375	#[test]
376	fn filter_in_place_keeps_matching_elements() {
377		let mut c = VarlenContainer::from_byte_slices([b"yes".as_slice(), b"no", b"yes", b"no"]);
378		c.filter_in_place(|i| i % 2 == 0);
379		assert_eq!(c.len(), 2);
380		assert_eq!(c.get_bytes(0), Some(b"yes".as_slice()));
381		assert_eq!(c.get_bytes(1), Some(b"yes".as_slice()));
382	}
383
384	#[test]
385	fn reorder_in_place_handles_oob_as_empty() {
386		let mut c = VarlenContainer::from_byte_slices([b"a".as_slice(), b"b"]);
387		c.reorder_in_place(&[1, 100, 0]);
388		assert_eq!(c.len(), 3);
389		assert_eq!(c.get_bytes(0), Some(b"b".as_slice()));
390		assert_eq!(c.get_bytes(1), Some(b"".as_slice()));
391		assert_eq!(c.get_bytes(2), Some(b"a".as_slice()));
392	}
393
394	#[test]
395	fn take_n_truncates() {
396		let c = VarlenContainer::from_byte_slices([b"a".as_slice(), b"b", b"c"]);
397		let t = c.take_n(2);
398		assert_eq!(t.len(), 2);
399		assert_eq!(t.get_bytes(0), Some(b"a".as_slice()));
400		assert_eq!(t.get_bytes(1), Some(b"b".as_slice()));
401	}
402
403	#[test]
404	fn slice_extracts_subrange_with_rebased_offsets() {
405		let c = VarlenContainer::from_byte_slices([b"aa".as_slice(), b"bb", b"cc", b"dd"]);
406		let s = c.slice(1, 3);
407		assert_eq!(s.len(), 2);
408		assert_eq!(s.get_bytes(0), Some(b"bb".as_slice()));
409		assert_eq!(s.get_bytes(1), Some(b"cc".as_slice()));
410		assert_eq!(s.offsets(), &[0u64, 2, 4]);
411	}
412
413	#[test]
414	fn serde_round_trip_preserves_content() {
415		let original = VarlenContainer::from_byte_slices([b"hello".as_slice(), b"", b"world"]);
416		let encoded: Vec<u8> = postcard_to_allocvec(&original).unwrap();
417		let decoded: VarlenContainer = postcard_from_bytes(&encoded).unwrap();
418		assert_eq!(decoded.len(), 3);
419		assert_eq!(decoded.get_bytes(0), Some(b"hello".as_slice()));
420		assert_eq!(decoded.get_bytes(1), Some(b"".as_slice()));
421		assert_eq!(decoded.get_bytes(2), Some(b"world".as_slice()));
422	}
423
424	#[test]
425	fn serde_wire_compat_with_vec_of_strings() {
426		// Postcard encodes `Vec<String>` and `Vec<&[u8]>` identically (both are length-prefixed
427		// sequences of length-prefixed bytes), so the two are wire-compatible.
428		let strings = vec!["a".to_string(), "bc".to_string(), "def".to_string()];
429		let encoded: Vec<u8> = postcard_to_allocvec(&strings).unwrap();
430		let decoded: VarlenContainer = postcard_from_bytes(&encoded).unwrap();
431		assert_eq!(decoded.len(), 3);
432		assert_eq!(decoded.get_bytes(0), Some(b"a".as_slice()));
433		assert_eq!(decoded.get_bytes(1), Some(b"bc".as_slice()));
434		assert_eq!(decoded.get_bytes(2), Some(b"def".as_slice()));
435	}
436
437	#[test]
438	fn equality_compares_logical_content() {
439		let a = VarlenContainer::from_byte_slices([b"x".as_slice(), b"y"]);
440		let b = VarlenContainer::from_byte_slices([b"x".as_slice(), b"y"]);
441		assert_eq!(a, b);
442	}
443}