1use std::str;
5
6use reifydb_value::{reifydb_assertions, value::value_type::ValueType};
7
8use crate::encoded::{row::EncodedRow, shape::RowShape};
9
10impl RowShape {
11 pub fn set_utf8(&self, row: &mut EncodedRow, index: usize, value: impl AsRef<str>) {
12 reifydb_assertions! {
13 assert!(
14 row.len() >= self.total_static_size(),
15 "row/shape size mismatch: row.len()={} < total_static_size()={}",
16 row.len(),
17 self.total_static_size()
18 );
19 assert_eq!(*self.fields()[index].constraint.get_type().inner_type(), ValueType::Utf8);
20 }
21 self.replace_dynamic_data(row, index, value.as_ref().as_bytes());
22 }
23
24 pub fn get_utf8<'a>(&'a self, row: &'a EncodedRow, index: usize) -> &'a str {
25 let field = &self.fields()[index];
26 reifydb_assertions! {
27 assert!(
28 row.len() >= self.total_static_size(),
29 "row/shape size mismatch: row.len()={} < total_static_size()={}",
30 row.len(),
31 self.total_static_size()
32 );
33 assert_eq!(*field.constraint.get_type().inner_type(), ValueType::Utf8);
34 }
35
36 let ref_slice = &row.as_slice()[field.offset as usize..field.offset as usize + 8];
37 let offset = u32::from_le_bytes([ref_slice[0], ref_slice[1], ref_slice[2], ref_slice[3]]) as usize;
38 let length = u32::from_le_bytes([ref_slice[4], ref_slice[5], ref_slice[6], ref_slice[7]]) as usize;
39
40 let dynamic_start = self.dynamic_section_start();
41 let string_start = dynamic_start + offset;
42 let string_slice = &row.as_slice()[string_start..string_start + length];
43
44 unsafe { str::from_utf8_unchecked(string_slice) }
45 }
46
47 pub fn try_get_utf8<'a>(&'a self, row: &'a EncodedRow, index: usize) -> Option<&'a str> {
48 if row.is_defined(index) && self.fields()[index].constraint.get_type() == ValueType::Utf8 {
49 Some(self.get_utf8(row, index))
50 } else {
51 None
52 }
53 }
54}
55
56#[cfg(test)]
57pub mod tests {
58 use reifydb_value::value::value_type::ValueType;
59
60 use crate::encoded::shape::RowShape;
61
62 #[test]
63 fn test_set_get_utf8() {
64 let shape = RowShape::testing(&[ValueType::Utf8]);
65 let mut row = shape.allocate();
66 shape.set_utf8(&mut row, 0, "reifydb");
67 assert_eq!(shape.get_utf8(&row, 0), "reifydb");
68 }
69
70 #[test]
71 fn test_try_get_utf8() {
72 let shape = RowShape::testing(&[ValueType::Utf8]);
73 let mut row = shape.allocate();
74
75 assert_eq!(shape.try_get_utf8(&row, 0), None);
76
77 shape.set_utf8(&mut row, 0, "reifydb");
78 assert_eq!(shape.try_get_utf8(&row, 0), Some("reifydb"));
79 }
80
81 #[test]
82 fn test_empty_string() {
83 let shape = RowShape::testing(&[ValueType::Utf8]);
84 let mut row = shape.allocate();
85 shape.set_utf8(&mut row, 0, "");
86 assert_eq!(shape.get_utf8(&row, 0), "");
87 assert_eq!(shape.try_get_utf8(&row, 0), Some(""));
88 }
89
90 #[test]
91 fn test_unicode() {
92 let shape = RowShape::testing(&[ValueType::Utf8]);
93 let mut row = shape.allocate();
94
95 let unicode_text = "πβ¨π Hello δΈη π";
96 shape.set_utf8(&mut row, 0, unicode_text);
97 assert_eq!(shape.get_utf8(&row, 0), unicode_text);
98 assert_eq!(shape.try_get_utf8(&row, 0), Some(unicode_text));
99 }
100
101 #[test]
102 fn test_large_string() {
103 let shape = RowShape::testing(&[ValueType::Utf8]);
104 let mut row = shape.allocate();
105
106 let large_string = "A".repeat(1000);
107 shape.set_utf8(&mut row, 0, &large_string);
108 assert_eq!(shape.get_utf8(&row, 0), large_string);
109 assert_eq!(shape.try_get_utf8(&row, 0), Some(large_string.as_str()));
110 }
111
112 #[test]
113 fn test_multiple_fields() {
114 let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Utf8, ValueType::Utf8]);
115 let mut row = shape.allocate();
116
117 shape.set_utf8(&mut row, 0, "first");
118 shape.set_utf8(&mut row, 1, "second string");
119 shape.set_utf8(&mut row, 2, "third");
120
121 assert_eq!(shape.get_utf8(&row, 0), "first");
122 assert_eq!(shape.get_utf8(&row, 1), "second string");
123 assert_eq!(shape.get_utf8(&row, 2), "third");
124 }
125
126 #[test]
127 fn test_mixed_with_static_fields() {
128 let shape = RowShape::testing(&[ValueType::Boolean, ValueType::Utf8, ValueType::Int4, ValueType::Utf8]);
129 let mut row = shape.allocate();
130
131 shape.set_bool(&mut row, 0, true);
132 shape.set_utf8(&mut row, 1, "hello world");
133 shape.set_i32(&mut row, 2, 42);
134 shape.set_utf8(&mut row, 3, "goodbye");
135
136 assert_eq!(shape.get_bool(&row, 0), true);
137 assert_eq!(shape.get_utf8(&row, 1), "hello world");
138 assert_eq!(shape.get_i32(&row, 2), 42);
139 assert_eq!(shape.get_utf8(&row, 3), "goodbye");
140 }
141
142 #[test]
143 fn test_different_sizes() {
144 let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Utf8, ValueType::Utf8]);
145 let mut row = shape.allocate();
146
147 shape.set_utf8(&mut row, 0, "");
148 shape.set_utf8(&mut row, 1, "medium length string here");
149 shape.set_utf8(&mut row, 2, "x");
150
151 assert_eq!(shape.get_utf8(&row, 0), "");
152 assert_eq!(shape.get_utf8(&row, 1), "medium length string here");
153 assert_eq!(shape.get_utf8(&row, 2), "x");
154 }
155
156 #[test]
157 fn test_arbitrary_setting_order() {
158 let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Utf8, ValueType::Utf8, ValueType::Utf8]);
159 let mut row = shape.allocate();
160
161 shape.set_utf8(&mut row, 3, "fourth");
163 shape.set_utf8(&mut row, 1, "second");
164 shape.set_utf8(&mut row, 0, "first");
165 shape.set_utf8(&mut row, 2, "third");
166
167 assert_eq!(shape.get_utf8(&row, 0), "first");
168 assert_eq!(shape.get_utf8(&row, 1), "second");
169 assert_eq!(shape.get_utf8(&row, 2), "third");
170 assert_eq!(shape.get_utf8(&row, 3), "fourth");
171 }
172
173 #[test]
174 fn test_special_characters() {
175 let shape = RowShape::testing(&[ValueType::Utf8]);
176
177 let special_strings = [
178 "",
179 " ",
180 "\n",
181 "\t",
182 "\r\n",
183 "\"quoted\"",
184 "'single quotes'",
185 "line1\nline2\nline3",
186 "tabs\there\tand\there",
187 "mixed\twhite\n \r\n\tspace",
188 ];
189
190 for special_str in special_strings {
191 let mut row = shape.allocate();
192 shape.set_utf8(&mut row, 0, special_str);
193 assert_eq!(shape.get_utf8(&row, 0), special_str);
194 }
195 }
196
197 #[test]
198 fn test_undefined_handling() {
199 let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Utf8, ValueType::Utf8]);
200 let mut row = shape.allocate();
201
202 shape.set_utf8(&mut row, 0, "defined");
204 shape.set_utf8(&mut row, 2, "also defined");
205
206 assert_eq!(shape.try_get_utf8(&row, 0), Some("defined"));
207 assert_eq!(shape.try_get_utf8(&row, 1), None);
208 assert_eq!(shape.try_get_utf8(&row, 2), Some("also defined"));
209
210 shape.set_none(&mut row, 0);
212 assert_eq!(shape.try_get_utf8(&row, 0), None);
213 assert_eq!(shape.try_get_utf8(&row, 2), Some("also defined"));
214 }
215
216 #[test]
217 fn test_try_get_utf8_wrong_type() {
218 let shape = RowShape::testing(&[ValueType::Boolean]);
219 let mut row = shape.allocate();
220
221 shape.set_bool(&mut row, 0, true);
222
223 assert_eq!(shape.try_get_utf8(&row, 0), None);
224 }
225
226 #[test]
227 fn test_update_utf8() {
228 let shape = RowShape::testing(&[ValueType::Utf8]);
229 let mut row = shape.allocate();
230
231 shape.set_utf8(&mut row, 0, "hello");
232 assert_eq!(shape.get_utf8(&row, 0), "hello");
233 let size_after_first = row.len();
234
235 shape.set_utf8(&mut row, 0, "hi");
237 assert_eq!(shape.get_utf8(&row, 0), "hi");
238 assert_eq!(row.len(), size_after_first - 3); shape.set_utf8(&mut row, 0, "hello world");
242 assert_eq!(shape.get_utf8(&row, 0), "hello world");
243
244 shape.set_utf8(&mut row, 0, "");
246 assert_eq!(shape.get_utf8(&row, 0), "");
247 assert_eq!(row.len(), shape.total_static_size());
248 }
249
250 #[test]
251 fn test_update_utf8_with_other_dynamic_fields() {
252 let shape = RowShape::testing(&[ValueType::Utf8, ValueType::Utf8, ValueType::Utf8]);
253 let mut row = shape.allocate();
254
255 shape.set_utf8(&mut row, 0, "first");
256 shape.set_utf8(&mut row, 1, "second");
257 shape.set_utf8(&mut row, 2, "third");
258
259 shape.set_utf8(&mut row, 1, "much longer second string");
261
262 assert_eq!(shape.get_utf8(&row, 0), "first");
264 assert_eq!(shape.get_utf8(&row, 1), "much longer second string");
265 assert_eq!(shape.get_utf8(&row, 2), "third");
266
267 shape.set_utf8(&mut row, 0, "f");
269 assert_eq!(shape.get_utf8(&row, 0), "f");
270 assert_eq!(shape.get_utf8(&row, 1), "much longer second string");
271 assert_eq!(shape.get_utf8(&row, 2), "third");
272
273 let expected = shape.total_static_size() + 1 + 25 + 5;
275 assert_eq!(row.len(), expected);
276 }
277}