Skip to main content

reifydb_value/value/container/
utf8.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	str,
8};
9
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11
12use crate::{
13	Result, reifydb_assertions,
14	util::bitvec::BitVec,
15	value::{Value, container::varlen::VarlenContainer, value_type::ValueType},
16};
17
18pub struct Utf8Container {
19	inner: VarlenContainer,
20}
21
22impl Clone for Utf8Container {
23	fn clone(&self) -> Self {
24		Self {
25			inner: self.inner.clone(),
26		}
27	}
28}
29
30impl Debug for Utf8Container
31where
32	VarlenContainer: Debug,
33{
34	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35		f.debug_struct("Utf8Container").field("inner", &self.inner).finish()
36	}
37}
38
39impl PartialEq for Utf8Container
40where
41	VarlenContainer: PartialEq,
42{
43	fn eq(&self, other: &Self) -> bool {
44		self.inner == other.inner
45	}
46}
47
48impl Serialize for Utf8Container {
49	fn serialize<Ser: Serializer>(&self, serializer: Ser) -> StdResult<Ser::Ok, Ser::Error> {
50		self.inner.serialize(serializer)
51	}
52}
53
54impl<'de> Deserialize<'de> for Utf8Container {
55	fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
56		let inner = VarlenContainer::deserialize(deserializer)?;
57		Ok(Self {
58			inner,
59		})
60	}
61}
62
63impl Utf8Container {
64	pub fn new(data: Vec<String>) -> Self {
65		Self::from_vec(data)
66	}
67
68	pub fn from_vec(data: Vec<String>) -> Self {
69		let inner = VarlenContainer::from_byte_slices(data.iter().map(|s| s.as_bytes()));
70		Self {
71			inner,
72		}
73	}
74
75	pub fn from_repeated_str(value: &str, count: usize) -> Self {
76		Self {
77			inner: VarlenContainer::from_repeated_bytes(value.as_bytes(), count),
78		}
79	}
80
81	pub fn with_capacity(capacity: usize) -> Self {
82		Self {
83			inner: VarlenContainer::with_capacity(capacity, capacity * 16),
84		}
85	}
86
87	pub fn from_raw_parts(data: Vec<String>) -> Self {
88		Self::from_vec(data)
89	}
90
91	pub fn from_bytes_offsets(data: Vec<u8>, offsets: Vec<u64>) -> Self {
92		reifydb_assertions! {
93			assert!(str::from_utf8(&data).is_ok(), "Utf8Container data must be valid UTF-8");
94		}
95		Self {
96			inner: VarlenContainer::from_raw_parts(data, offsets),
97		}
98	}
99
100	pub fn try_into_raw_parts(self) -> Option<Vec<String>> {
101		Some(self.iter().map(|s| s.unwrap().to_string()).collect())
102	}
103}
104
105impl Utf8Container {
106	pub fn from_inner(inner: VarlenContainer) -> Self {
107		Self {
108			inner,
109		}
110	}
111
112	pub fn from_storage_parts(data: Vec<u8>, offsets: Vec<u64>) -> Self {
113		Self {
114			inner: VarlenContainer::from_storage_parts(data, offsets),
115		}
116	}
117
118	pub fn data_storage(&self) -> &Vec<u8> {
119		self.inner.data()
120	}
121
122	pub fn offsets_storage(&self) -> &Vec<u64> {
123		self.inner.offsets_data()
124	}
125
126	pub fn len(&self) -> usize {
127		self.inner.len()
128	}
129
130	pub fn capacity(&self) -> usize {
131		self.inner.capacity()
132	}
133
134	pub fn heap_size(&self) -> usize {
135		self.inner.heap_size()
136	}
137
138	pub fn is_empty(&self) -> bool {
139		self.inner.is_empty()
140	}
141
142	pub fn clear(&mut self) {
143		self.inner.clear_generic();
144	}
145
146	pub fn get(&self, index: usize) -> Option<&str> {
147		let bytes = self.inner.get_bytes(index)?;
148		// SAFETY: every constructor and push path takes `String`/`&str` and from_bytes_offsets asserts
149		// validity, so the buffer holds UTF-8 and offsets fall on element boundaries.
150		Some(unsafe { str::from_utf8_unchecked(bytes) })
151	}
152
153	pub fn is_defined(&self, idx: usize) -> bool {
154		idx < self.len()
155	}
156
157	pub fn is_fully_defined(&self) -> bool {
158		true
159	}
160
161	pub fn data_bytes(&self) -> &[u8] {
162		self.inner.data_bytes()
163	}
164
165	pub fn offsets(&self) -> &[u64] {
166		self.inner.offsets()
167	}
168
169	pub fn inner(&self) -> &VarlenContainer {
170		&self.inner
171	}
172
173	pub fn as_string(&self, index: usize) -> String {
174		self.get(index).map(str::to_string).unwrap_or_else(|| "none".to_string())
175	}
176
177	pub fn get_value(&self, index: usize) -> Value {
178		match self.get(index) {
179			Some(s) => Value::Utf8(s.to_string()),
180			None => Value::none_of(ValueType::Utf8),
181		}
182	}
183
184	pub fn iter(&self) -> impl Iterator<Item = Option<&str>> + '_ {
185		(0..self.len()).map(|i| self.get(i))
186	}
187
188	pub fn iter_str(&self) -> impl Iterator<Item = &str> + '_ {
189		(0..self.len()).map(|i| self.get(i).unwrap())
190	}
191}
192
193impl Utf8Container {
194	pub fn push(&mut self, value: String) {
195		self.inner.push_bytes(value.as_bytes());
196	}
197
198	pub fn push_str(&mut self, value: &str) {
199		self.inner.push_bytes(value.as_bytes());
200	}
201
202	pub fn push_default(&mut self) {
203		self.inner.push_bytes(&[]);
204	}
205
206	pub fn extend(&mut self, other: &Self) -> Result<()> {
207		self.inner.extend_from(&other.inner);
208		Ok(())
209	}
210
211	pub fn slice(&self, start: usize, end: usize) -> Self {
212		Self {
213			inner: self.inner.slice(start, end),
214		}
215	}
216
217	pub fn filter(&mut self, mask: &BitVec) {
218		let bits: Vec<bool> = mask.iter().collect();
219		self.inner.filter_in_place(|i| bits.get(i).copied().unwrap_or(false));
220	}
221
222	pub fn reorder(&mut self, indices: &[usize]) {
223		self.inner.reorder_in_place(indices);
224	}
225
226	pub fn take(&self, num: usize) -> Self {
227		Self {
228			inner: self.inner.take_n(num),
229		}
230	}
231}
232
233impl Default for Utf8Container {
234	fn default() -> Self {
235		Self::with_capacity(0)
236	}
237}
238
239#[cfg(test)]
240pub mod tests {
241	use postcard::to_allocvec as postcard_to_allocvec;
242
243	use super::*;
244	use crate::util::bitvec::BitVec;
245
246	#[test]
247	fn test_new() {
248		let data = vec!["hello".to_string(), "world".to_string(), "test".to_string()];
249		let container = Utf8Container::new(data.clone());
250
251		assert_eq!(container.len(), 3);
252		assert_eq!(container.get(0), Some("hello"));
253		assert_eq!(container.get(1), Some("world"));
254		assert_eq!(container.get(2), Some("test"));
255	}
256
257	#[test]
258	fn test_from_vec() {
259		let data = vec!["foo".to_string(), "bar".to_string(), "baz".to_string()];
260		let container = Utf8Container::from_vec(data);
261
262		assert_eq!(container.len(), 3);
263		assert_eq!(container.get(0), Some("foo"));
264		assert_eq!(container.get(1), Some("bar"));
265		assert_eq!(container.get(2), Some("baz"));
266
267		for i in 0..3 {
268			assert!(container.is_defined(i));
269		}
270	}
271
272	#[test]
273	fn test_from_repeated_str() {
274		let container = Utf8Container::from_repeated_str("mint", 3);
275		let explicit =
276			Utf8Container::from_vec(vec!["mint".to_string(), "mint".to_string(), "mint".to_string()]);
277		assert_eq!(container, explicit);
278		assert_eq!(container.len(), 3);
279		assert_eq!(container.get(0), Some("mint"));
280		assert_eq!(container.get(2), Some("mint"));
281		for i in 0..3 {
282			assert!(container.is_defined(i));
283		}
284	}
285
286	#[test]
287	fn test_with_capacity() {
288		let container = Utf8Container::with_capacity(10);
289		assert_eq!(container.len(), 0);
290		assert!(container.is_empty());
291		assert!(container.capacity() >= 10);
292	}
293
294	#[test]
295	fn test_push() {
296		let mut container = Utf8Container::with_capacity(3);
297
298		container.push("first".to_string());
299		container.push("second".to_string());
300		container.push_default();
301
302		assert_eq!(container.len(), 3);
303		assert_eq!(container.get(0), Some("first"));
304		assert_eq!(container.get(1), Some("second"));
305		assert_eq!(container.get(2), Some(""));
306
307		assert!(container.is_defined(0));
308		assert!(container.is_defined(1));
309		assert!(container.is_defined(2));
310	}
311
312	#[test]
313	fn test_extend() {
314		let mut container1 = Utf8Container::from_vec(vec!["a".to_string(), "b".to_string()]);
315		let container2 = Utf8Container::from_vec(vec!["c".to_string(), "d".to_string()]);
316
317		container1.extend(&container2).unwrap();
318
319		assert_eq!(container1.len(), 4);
320		assert_eq!(container1.get(0), Some("a"));
321		assert_eq!(container1.get(1), Some("b"));
322		assert_eq!(container1.get(2), Some("c"));
323		assert_eq!(container1.get(3), Some("d"));
324	}
325
326	#[test]
327	fn test_iter() {
328		let data = vec!["x".to_string(), "y".to_string(), "z".to_string()];
329		let container = Utf8Container::new(data);
330
331		let collected: Vec<Option<&str>> = container.iter().collect();
332		assert_eq!(collected, vec![Some("x"), Some("y"), Some("z")]);
333	}
334
335	#[test]
336	fn test_slice() {
337		let container = Utf8Container::from_vec(vec![
338			"one".to_string(),
339			"two".to_string(),
340			"three".to_string(),
341			"four".to_string(),
342		]);
343		let sliced = container.slice(1, 3);
344
345		assert_eq!(sliced.len(), 2);
346		assert_eq!(sliced.get(0), Some("two"));
347		assert_eq!(sliced.get(1), Some("three"));
348	}
349
350	#[test]
351	fn test_filter() {
352		let mut container = Utf8Container::from_vec(vec![
353			"keep".to_string(),
354			"drop".to_string(),
355			"keep".to_string(),
356			"drop".to_string(),
357		]);
358		let mask = BitVec::from_slice(&[true, false, true, false]);
359
360		container.filter(&mask);
361
362		assert_eq!(container.len(), 2);
363		assert_eq!(container.get(0), Some("keep"));
364		assert_eq!(container.get(1), Some("keep"));
365	}
366
367	#[test]
368	fn test_reorder() {
369		let mut container =
370			Utf8Container::from_vec(vec!["first".to_string(), "second".to_string(), "third".to_string()]);
371		let indices = [2, 0, 1];
372
373		container.reorder(&indices);
374
375		assert_eq!(container.len(), 3);
376		assert_eq!(container.get(0), Some("third"));
377		assert_eq!(container.get(1), Some("first"));
378		assert_eq!(container.get(2), Some("second"));
379	}
380
381	#[test]
382	fn test_reorder_with_out_of_bounds() {
383		let mut container = Utf8Container::from_vec(vec!["a".to_string(), "b".to_string()]);
384		let indices = [1, 5, 0];
385
386		container.reorder(&indices);
387
388		assert_eq!(container.len(), 3);
389		assert_eq!(container.get(0), Some("b"));
390		assert_eq!(container.get(1), Some(""));
391		assert_eq!(container.get(2), Some("a"));
392	}
393
394	#[test]
395	fn test_empty_strings() {
396		let mut container = Utf8Container::with_capacity(2);
397		container.push("".to_string());
398		container.push_default();
399
400		assert_eq!(container.len(), 2);
401		assert_eq!(container.get(0), Some(""));
402		assert_eq!(container.get(1), Some(""));
403
404		assert!(container.is_defined(0));
405		assert!(container.is_defined(1));
406	}
407
408	#[test]
409	fn testault() {
410		let container = Utf8Container::default();
411		assert_eq!(container.len(), 0);
412		assert!(container.is_empty());
413	}
414
415	#[test]
416	fn test_data_bytes_and_offsets_match_zero_copy_layout() {
417		let container = Utf8Container::from_vec(vec!["aa".to_string(), "bb".to_string()]);
418		assert_eq!(container.data_bytes(), b"aabb");
419		assert_eq!(container.offsets(), &[0u64, 2, 4]);
420	}
421
422	#[test]
423	fn test_postcard_wire_compat() {
424		// The postcard byte form must match what `Vec<String>` would
425		// produce so on-disk state and CDC streams stay readable.
426		let strings = vec!["hello".to_string(), "world".to_string()];
427		let strings_bytes: Vec<u8> = postcard_to_allocvec(&strings).unwrap();
428
429		let container = Utf8Container::from_vec(strings.clone());
430		let container_bytes: Vec<u8> = postcard_to_allocvec(&container).unwrap();
431
432		assert_eq!(strings_bytes, container_bytes);
433	}
434}