reifydb_codec/frame/encoding/
dict.rs1use std::{collections::HashMap, str};
5
6use reifydb_value::value::{
7 blob::Blob,
8 container::{blob::BlobContainer, utf8::Utf8Container},
9};
10
11use super::plain::PlainEncoded;
12use crate::{error::DecodeError, frame::format::dict_index_width_to_flags, tag::ValueKind};
13
14pub struct DictEncoded {
15 pub data: Vec<u8>,
16
17 pub extra: Vec<u8>,
18
19 pub type_code: u8,
20
21 pub flags_bits: u8,
22}
23
24pub fn try_dict_encode_utf8(container: &Utf8Container, min_ratio: f64) -> Option<DictEncoded> {
25 let row_count = container.len();
26 if row_count == 0 {
27 return None;
28 }
29
30 let mut dict_map: HashMap<&str, u32> = HashMap::new();
31 let mut dict_entries: Vec<&str> = Vec::new();
32
33 for s in container.iter_str() {
34 if !dict_map.contains_key(s) {
35 let idx = dict_entries.len() as u32;
36 dict_map.insert(s, idx);
37 dict_entries.push(s);
38 }
39 }
40
41 let dict_count = dict_entries.len();
42
43 if dict_count as f64 >= row_count as f64 * min_ratio {
44 return None;
45 }
46
47 let (index_width, flags_bits) = if dict_count <= 255 {
48 (1usize, dict_index_width_to_flags(1))
49 } else if dict_count <= 65535 {
50 (2, dict_index_width_to_flags(2))
51 } else {
52 (4, dict_index_width_to_flags(4))
53 };
54
55 let mut data = Vec::with_capacity(row_count * index_width);
56 for s in container.iter_str() {
57 let idx = dict_map[s];
58 match index_width {
59 1 => data.push(idx as u8),
60 2 => data.extend_from_slice(&(idx as u16).to_le_bytes()),
61 4 => data.extend_from_slice(&idx.to_le_bytes()),
62 _ => unreachable!(),
63 }
64 }
65
66 let mut extra = Vec::new();
67 extra.extend_from_slice(&(dict_count as u32).to_le_bytes());
68
69 let mut offset: u32 = 0;
70 extra.extend_from_slice(&offset.to_le_bytes());
71 for entry in &dict_entries {
72 offset += entry.len() as u32;
73 extra.extend_from_slice(&offset.to_le_bytes());
74 }
75
76 for entry in &dict_entries {
77 extra.extend_from_slice(entry.as_bytes());
78 }
79
80 Some(DictEncoded {
81 data,
82 extra,
83 type_code: ValueKind::Utf8.byte(),
84 flags_bits,
85 })
86}
87
88pub fn try_dict_encode_blob(container: &BlobContainer, min_ratio: f64) -> Option<DictEncoded> {
89 let row_count = container.len();
90 if row_count == 0 {
91 return None;
92 }
93
94 let mut dict_map: HashMap<&[u8], u32> = HashMap::new();
95 let mut dict_entries: Vec<&[u8]> = Vec::new();
96
97 for bytes in container.iter_bytes() {
98 if !dict_map.contains_key(bytes) {
99 let idx = dict_entries.len() as u32;
100 dict_map.insert(bytes, idx);
101 dict_entries.push(bytes);
102 }
103 }
104
105 let dict_count = dict_entries.len();
106
107 if dict_count as f64 >= row_count as f64 * min_ratio {
108 return None;
109 }
110
111 let (index_width, flags_bits) = if dict_count <= 255 {
112 (1usize, dict_index_width_to_flags(1))
113 } else if dict_count <= 65535 {
114 (2, dict_index_width_to_flags(2))
115 } else {
116 (4, dict_index_width_to_flags(4))
117 };
118
119 let mut data = Vec::with_capacity(row_count * index_width);
120 for bytes in container.iter_bytes() {
121 let idx = dict_map[bytes];
122 match index_width {
123 1 => data.push(idx as u8),
124 2 => data.extend_from_slice(&(idx as u16).to_le_bytes()),
125 4 => data.extend_from_slice(&idx.to_le_bytes()),
126 _ => unreachable!(),
127 }
128 }
129
130 let mut extra = Vec::new();
131 extra.extend_from_slice(&(dict_count as u32).to_le_bytes());
132
133 let mut offset: u32 = 0;
134 extra.extend_from_slice(&offset.to_le_bytes());
135 for entry in &dict_entries {
136 offset += entry.len() as u32;
137 extra.extend_from_slice(&offset.to_le_bytes());
138 }
139 for entry in &dict_entries {
140 extra.extend_from_slice(entry);
141 }
142
143 Some(DictEncoded {
144 data,
145 extra,
146 type_code: ValueKind::Blob.byte(),
147 flags_bits,
148 })
149}
150
151pub fn try_dict_encode_bytes(serialized: &[Vec<u8>], type_code: u8, min_ratio: f64) -> Option<DictEncoded> {
152 if serialized.is_empty() {
153 return None;
154 }
155
156 let mut dict_map: HashMap<&[u8], u32> = HashMap::new();
157 let mut dict_entries: Vec<&[u8]> = Vec::new();
158
159 for bytes in serialized {
160 if !dict_map.contains_key(bytes.as_slice()) {
161 let idx = dict_entries.len() as u32;
162 dict_map.insert(bytes.as_slice(), idx);
163 dict_entries.push(bytes.as_slice());
164 }
165 }
166
167 let dict_count = dict_entries.len();
168
169 if dict_count as f64 >= serialized.len() as f64 * min_ratio {
170 return None;
171 }
172
173 let (index_width, flags_bits) = if dict_count <= 255 {
174 (1usize, dict_index_width_to_flags(1))
175 } else if dict_count <= 65535 {
176 (2, dict_index_width_to_flags(2))
177 } else {
178 (4, dict_index_width_to_flags(4))
179 };
180
181 let mut data = Vec::with_capacity(serialized.len() * index_width);
182 for bytes in serialized {
183 let idx = dict_map[bytes.as_slice()];
184 match index_width {
185 1 => data.push(idx as u8),
186 2 => data.extend_from_slice(&(idx as u16).to_le_bytes()),
187 4 => data.extend_from_slice(&idx.to_le_bytes()),
188 _ => unreachable!(),
189 }
190 }
191
192 let mut extra = Vec::new();
193 extra.extend_from_slice(&(dict_count as u32).to_le_bytes());
194 let mut offset: u32 = 0;
195 extra.extend_from_slice(&offset.to_le_bytes());
196 for entry in &dict_entries {
197 offset += entry.len() as u32;
198 extra.extend_from_slice(&offset.to_le_bytes());
199 }
200 for entry in &dict_entries {
201 extra.extend_from_slice(entry);
202 }
203
204 Some(DictEncoded {
205 data,
206 extra,
207 type_code,
208 flags_bits,
209 })
210}
211
212impl DictEncoded {
213 pub fn into_plain_encoded(self) -> PlainEncoded {
214 PlainEncoded {
215 data: self.data,
216 offsets: vec![],
217 nones: vec![],
218 type_code: self.type_code,
219 has_nones: false,
220 }
221 }
222}
223
224pub fn decode_dict_utf8(
225 data: &[u8],
226 extra: &[u8],
227 row_count: usize,
228 index_width: usize,
229) -> Result<Vec<String>, DecodeError> {
230 let dict_entries = decode_dict_table(extra)?;
231
232 let mut values = Vec::with_capacity(row_count);
233 for i in 0..row_count {
234 let idx = read_index(data, i, index_width) as usize;
235 if idx >= dict_entries.len() {
236 return Err(DecodeError::InvalidData(format!(
237 "dict index {} out of range (dict has {} entries)",
238 idx,
239 dict_entries.len()
240 )));
241 }
242 values.push(dict_entries[idx].clone());
243 }
244
245 Ok(values)
246}
247
248pub fn decode_dict_blob(
249 data: &[u8],
250 extra: &[u8],
251 row_count: usize,
252 index_width: usize,
253) -> Result<Vec<Blob>, DecodeError> {
254 let dict_entries = decode_dict_table_bytes(extra)?;
255
256 let mut values = Vec::with_capacity(row_count);
257 for i in 0..row_count {
258 let idx = read_index(data, i, index_width) as usize;
259 if idx >= dict_entries.len() {
260 return Err(DecodeError::InvalidData(format!(
261 "dict index {} out of range (dict has {} entries)",
262 idx,
263 dict_entries.len()
264 )));
265 }
266 values.push(Blob::new(dict_entries[idx].clone()));
267 }
268
269 Ok(values)
270}
271
272fn decode_dict_table(extra: &[u8]) -> Result<Vec<String>, DecodeError> {
273 if extra.len() < 4 {
274 return Err(DecodeError::InvalidData("dict table too short".to_string()));
275 }
276
277 let dict_count = u32::from_le_bytes([extra[0], extra[1], extra[2], extra[3]]) as usize;
278 let offsets_start = 4;
279 let offsets_end = offsets_start + (dict_count + 1) * 4;
280
281 if extra.len() < offsets_end {
282 return Err(DecodeError::InvalidData("dict offsets truncated".to_string()));
283 }
284
285 let mut offsets = Vec::with_capacity(dict_count + 1);
286 for i in 0..=dict_count {
287 let pos = offsets_start + i * 4;
288 offsets.push(u32::from_le_bytes([extra[pos], extra[pos + 1], extra[pos + 2], extra[pos + 3]]) as usize);
289 }
290
291 let data_start = offsets_end;
292 let mut entries = Vec::with_capacity(dict_count);
293 for i in 0..dict_count {
294 let start = data_start + offsets[i];
295 let end = data_start + offsets[i + 1];
296 let s = str::from_utf8(&extra[start..end])
297 .map_err(|e| DecodeError::InvalidData(format!("invalid dict UTF-8: {}", e)))?;
298 entries.push(s.to_string());
299 }
300
301 Ok(entries)
302}
303
304pub fn decode_dict_table_bytes(extra: &[u8]) -> Result<Vec<Vec<u8>>, DecodeError> {
305 if extra.len() < 4 {
306 return Err(DecodeError::InvalidData("dict table too short".to_string()));
307 }
308
309 let dict_count = u32::from_le_bytes([extra[0], extra[1], extra[2], extra[3]]) as usize;
310 let offsets_start = 4;
311 let offsets_end = offsets_start + (dict_count + 1) * 4;
312
313 if extra.len() < offsets_end {
314 return Err(DecodeError::InvalidData("dict offsets truncated".to_string()));
315 }
316
317 let mut offsets = Vec::with_capacity(dict_count + 1);
318 for i in 0..=dict_count {
319 let pos = offsets_start + i * 4;
320 offsets.push(u32::from_le_bytes([extra[pos], extra[pos + 1], extra[pos + 2], extra[pos + 3]]) as usize);
321 }
322
323 let data_start = offsets_end;
324 let mut entries = Vec::with_capacity(dict_count);
325 for i in 0..dict_count {
326 let start = data_start + offsets[i];
327 let end = data_start + offsets[i + 1];
328 entries.push(extra[start..end].to_vec());
329 }
330
331 Ok(entries)
332}
333
334pub fn read_index(data: &[u8], i: usize, width: usize) -> u32 {
335 let off = i * width;
336 match width {
337 1 => data[off] as u32,
338 2 => u16::from_le_bytes([data[off], data[off + 1]]) as u32,
339 4 => u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]]),
340 _ => unreachable!(),
341 }
342}