1use std::borrow::Cow;
2
3use bitvec::order::Lsb0;
4use bitvec::slice::BitSlice;
5use usize_cast::IntoUsize as _;
6
7use crate::MltError::{BufferUnderflow, DictIndexOutOfBounds};
8#[cfg(feature = "unstable-v2")]
9use crate::codecs::front_coding::{FrontLengths, front_decode};
10use crate::codecs::fsst::{decode_fsst, decode_fsst_bytes};
11use crate::decoder::{
12 DictLayout, DictionaryType, LengthType, OffsetType, ParsedSharedDict, ParsedSharedDictItem,
13 ParsedStrings, RawFsstData, RawPlainData, RawPresence, RawSharedDictEncoding, RawStream,
14 RawStrings, RawStringsEncoding, StreamType,
15};
16use crate::errors::AsMltError as _;
17use crate::{Decoder, DictRange, MltError, MltResult, RawSharedDict, RawSharedDictItem};
18
19impl<'a> ParsedStrings<'a> {
20 #[must_use]
21 pub fn new(name: &'a str, lengths: Vec<i32>, data: Cow<'a, str>) -> Self {
22 ParsedStrings {
23 name,
24 lengths,
25 data,
26 }
27 }
28
29 #[must_use]
30 pub fn feature_count(&self) -> usize {
31 self.lengths.len()
32 }
33
34 #[must_use]
35 pub fn presence_bools(&self) -> Vec<bool> {
36 self.lengths.iter().map(|&end| end >= 0).collect()
37 }
38
39 #[must_use]
40 pub fn get(&self, idx: u32) -> Option<&str> {
41 let idx = idx.into_usize();
42
43 let end = *self.lengths.get(idx)?;
44 if end < 0 {
45 return None;
46 }
47 let end = decode_end(end).into_usize();
48
49 let start = idx
50 .checked_sub(1)
51 .and_then(|prev| self.lengths.get(prev).copied())
52 .map_or(0, decode_end)
53 .into_usize();
54
55 self.data.get(start..end)
56 }
57
58 #[must_use]
59 pub fn dense_values(&self) -> Vec<String> {
60 let mut values = Vec::new();
61 let mut start = 0_u32;
62 for &end in &self.lengths {
63 let end_u32 = decode_end(end);
64 let start_idx = start.into_usize();
65 let end_idx = end_u32.into_usize();
66 if end >= 0
67 && let Some(value) = self.data.get(start_idx..end_idx)
68 {
69 values.push(value.to_string());
70 }
71 start = end_u32;
72 }
73 values
74 }
75
76 #[must_use]
77 pub fn materialize(&self) -> Vec<Option<String>> {
78 (0..u32::try_from(self.feature_count()).unwrap_or(u32::MAX))
79 .map(|i| self.get(i).map(str::to_string))
80 .collect()
81 }
82}
83
84pub(crate) fn decode_shared_dict_range(range: DictRange) -> Option<(u32, u32)> {
85 if let (Ok(start), Ok(end)) = (u32::try_from(range.start), u32::try_from(range.end)) {
86 Some((start, end))
87 } else {
88 None
89 }
90}
91
92pub(crate) fn shared_dict_spans(lengths: &[u32], dec: &mut Decoder) -> MltResult<Vec<(u32, u32)>> {
93 let mut spans = dec.alloc(lengths.len())?;
94 let mut offset = 0_u32;
95 for &len in lengths {
96 let start = offset;
97 offset = offset.saturating_add(len);
98 spans.push((start, offset));
99 }
100 Ok(spans)
101}
102
103pub(crate) fn resolve_dict_spans(
104 offsets: &[u32],
105 presence: Option<&BitSlice<u8, Lsb0>>,
106 dict_spans: &[(u32, u32)],
107 dec: &mut Decoder,
108) -> MltResult<Vec<Option<(u32, u32)>>> {
109 let present_count = presence.map_or(offsets.len(), BitSlice::len);
110 let mut resolved = dec.alloc(present_count)?;
111 let mut next = offsets.iter().copied();
112
113 if let Some(presence) = presence {
114 let fail = || MltError::PresenceValueCountMismatch(presence.count_ones(), offsets.len());
115 for present in presence.iter().by_vals() {
116 if !present {
117 resolved.push(None);
118 continue;
119 }
120 let idx = next.next().ok_or_else(fail)?;
121 let span = dict_spans
122 .get(idx.into_usize())
123 .copied()
124 .ok_or(DictIndexOutOfBounds(idx, dict_spans.len()))?;
125 resolved.push(Some(span));
126 }
127
128 if next.next().is_some() {
129 return Err(fail());
130 }
131 } else {
132 for &idx in offsets {
133 let span = dict_spans
134 .get(idx.into_usize())
135 .copied()
136 .ok_or(DictIndexOutOfBounds(idx, dict_spans.len()))?;
137 resolved.push(Some(span));
138 }
139 }
140
141 Ok(resolved)
142}
143
144fn dict_span_str(dict_data: &str, span: (u32, u32)) -> MltResult<&str> {
145 let start = span.0.into_usize();
146 let end = span.1.into_usize();
147 if let Some(value) = dict_data.get(start..end) {
149 return Ok(value);
150 }
151 let bytes = dict_data.as_bytes();
152 let Some(value) = bytes.get(start..end) else {
153 let len = span.1.saturating_sub(span.0);
154 return Err(BufferUnderflow(len, bytes.len().saturating_sub(start)));
155 };
156 Ok(str::from_utf8(value)?)
157}
158
159impl ParsedSharedDict<'_> {
160 #[must_use]
162 pub fn items(&self) -> &[ParsedSharedDictItem<'_>] {
163 &self.items
164 }
165
166 #[must_use]
167 pub fn corpus(&self) -> &str {
168 &self.data
169 }
170
171 #[must_use]
172 pub fn get(&self, span: (u32, u32)) -> Option<&str> {
173 let start = span.0.into_usize();
174 let end = span.1.into_usize();
175 self.corpus().get(start..end)
176 }
177}
178
179impl ParsedSharedDictItem<'_> {
180 #[must_use]
182 pub fn feature_count(&self) -> usize {
183 self.ranges.len()
184 }
185
186 #[must_use]
187 pub fn get<'a>(&self, shared_dict: &'a ParsedSharedDict<'_>, i: usize) -> Option<&'a str> {
188 self.ranges
189 .get(i)
190 .copied()
191 .and_then(decode_shared_dict_range)
192 .and_then(|span| shared_dict.get(span))
193 }
194}
195
196impl<'a> RawPlainData<'a> {
197 pub fn new(lengths: RawStream<'a>, data: RawStream<'a>) -> MltResult<Self> {
198 validate_stream!(
199 lengths,
200 StreamType::Length(LengthType::VarBinary | LengthType::Dictionary)
201 );
202 validate_stream!(
203 data,
204 StreamType::Data(
205 DictionaryType::None | DictionaryType::Single | DictionaryType::Shared
206 )
207 );
208 Ok(Self { lengths, data })
209 }
210
211 pub fn decode(self, dec: &mut Decoder) -> MltResult<(&'a str, Vec<u32>)> {
212 let (data, lengths) = self.decode_bytes(dec)?;
213 Ok((str::from_utf8(data)?, lengths))
214 }
215
216 pub fn decode_bytes(self, dec: &mut Decoder) -> MltResult<(&'a [u8], Vec<u32>)> {
218 Ok((self.data.data, self.lengths.decode_ints(dec)?))
219 }
220}
221
222impl<'a> RawFsstData<'a> {
223 pub fn new(
224 symbol_lengths: RawStream<'a>,
225 symbol_table: RawStream<'a>,
226 lengths: RawStream<'a>,
227 corpus: RawStream<'a>,
228 ) -> MltResult<Self> {
229 validate_stream!(symbol_lengths, StreamType::Length(LengthType::Symbol));
230 validate_stream!(symbol_table, StreamType::Data(DictionaryType::Fsst));
231 validate_stream!(lengths, StreamType::Length(LengthType::Dictionary));
232 validate_stream!(
233 corpus,
234 StreamType::Data(DictionaryType::Single | DictionaryType::Shared)
235 );
236 Ok(Self {
237 symbol_lengths,
238 symbol_table,
239 lengths,
240 corpus,
241 })
242 }
243
244 pub fn decode(self, dec: &mut Decoder) -> MltResult<(String, Vec<u32>)> {
245 decode_fsst(self, dec)
246 }
247
248 pub fn decode_bytes(self, dec: &mut Decoder) -> MltResult<(Vec<u8>, Vec<u32>)> {
250 decode_fsst_bytes(self, dec)
251 }
252}
253
254impl<'a> RawStringsEncoding<'a> {
255 #[must_use]
256 pub fn plain(plain_data: RawPlainData<'a>) -> Self {
257 Self::Plain(plain_data)
258 }
259
260 pub fn dictionary(
261 plain_data: RawPlainData<'a>,
262 offsets: RawStream<'a>,
263 dict: DictLayout,
264 ) -> MltResult<Self> {
265 validate_stream!(offsets, StreamType::Offset(OffsetType::String));
266 Ok(Self::Dictionary {
267 plain_data,
268 offsets,
269 dict,
270 })
271 }
272
273 #[must_use]
274 pub fn fsst_plain(fsst_data: RawFsstData<'a>) -> Self {
275 Self::FsstPlain(fsst_data)
276 }
277
278 pub fn fsst_dictionary(
279 fsst_data: RawFsstData<'a>,
280 offsets: RawStream<'a>,
281 dict: DictLayout,
282 ) -> MltResult<Self> {
283 validate_stream!(offsets, StreamType::Offset(OffsetType::String));
284 Ok(Self::FsstDictionary {
285 fsst_data,
286 offsets,
287 dict,
288 })
289 }
290}
291
292impl<'a> RawSharedDictEncoding<'a> {
293 #[must_use]
295 pub fn plain(plain_data: RawPlainData<'a>) -> Self {
296 Self::Plain(plain_data)
297 }
298
299 #[must_use]
301 pub fn fsst_plain(fsst_data: RawFsstData<'a>) -> Self {
302 Self::FsstPlain(fsst_data)
303 }
304}
305
306impl<'a> RawStrings<'a> {
307 #[must_use]
308 pub fn new(name: &'a str, presence: RawPresence<'a>, encoding: RawStringsEncoding<'a>) -> Self {
309 Self {
310 name,
311 presence,
312 encoding,
313 }
314 }
315
316 #[cfg(feature = "unstable-v2")]
321 #[must_use]
322 pub(crate) fn value_count(&self) -> u32 {
323 match &self.encoding {
324 RawStringsEncoding::Plain(plain) => plain.lengths.meta.num_values,
325 RawStringsEncoding::FsstPlain(fsst) => fsst.lengths.meta.num_values,
326 RawStringsEncoding::Dictionary { offsets, .. }
327 | RawStringsEncoding::FsstDictionary { offsets, .. } => offsets.meta.num_values,
328 }
329 }
330
331 pub fn decode(self, dec: &mut Decoder) -> MltResult<ParsedStrings<'a>> {
333 let name = self.name;
334 let presence = self.presence.decode_bits(dec)?;
335
336 let parsed = match self.encoding {
337 RawStringsEncoding::Plain(plain_data) => {
338 let (data, lengths) = plain_data.decode(dec)?;
339 ParsedStrings {
340 name,
341 lengths: to_absolute_lengths(&lengths, presence.as_deref(), dec)?,
342 data: data.into(),
343 }
344 }
345 RawStringsEncoding::Dictionary {
346 plain_data,
347 offsets,
348 dict,
349 } => {
350 let (data, lengths) = plain_data.decode_bytes(dec)?;
351 let offsets: Vec<u32> = offsets.decode_ints(dec)?;
352 let (dictionary, lengths) = rebuild_dictionary(dict, &lengths, data)?;
353 decode_dictionary_strings(
354 name,
355 &lengths,
356 &offsets,
357 presence.as_deref(),
358 &dictionary,
359 dec,
360 )?
361 }
362 RawStringsEncoding::FsstPlain(fsst_data) => {
363 let (data, dict_lens) = fsst_data.decode(dec)?;
364 ParsedStrings {
365 name,
366 lengths: to_absolute_lengths(&dict_lens, presence.as_deref(), dec)?,
367 data: data.into(),
368 }
369 }
370 RawStringsEncoding::FsstDictionary {
371 fsst_data,
372 offsets,
373 dict,
374 } => {
375 let (data, lengths) = fsst_data.decode_bytes(dec)?;
376 let offsets: Vec<u32> = offsets.decode_ints(dec)?;
377 let (dictionary, lengths) = rebuild_dictionary(dict, &lengths, &data)?;
378 decode_dictionary_strings(
379 name,
380 &lengths,
381 &offsets,
382 presence.as_deref(),
383 &dictionary,
384 dec,
385 )?
386 }
387 };
388 Ok(parsed)
389 }
390}
391
392fn to_absolute_lengths(
393 lengths: &[u32],
394 presence: Option<&BitSlice<u8, Lsb0>>,
395 dec: &mut Decoder,
396) -> MltResult<Vec<i32>> {
397 let capacity = presence.map_or(lengths.len(), BitSlice::len);
398 let mut absolute = dec.alloc(capacity)?;
399 let mut iter = lengths.iter().copied();
400 let mut end = 0_i32;
401 if let Some(presence) = presence {
402 for present in presence.iter().by_vals() {
403 if present {
404 let len = iter.next().ok_or(MltError::PresenceValueCountMismatch(
405 presence.len(),
406 lengths.len(),
407 ))?;
408 end = checked_absolute_end(end, len)?;
409 absolute.push(end);
410 } else {
411 absolute.push(encode_null_end(end));
412 }
413 }
414 if iter.next().is_some() {
415 return Err(MltError::PresenceValueCountMismatch(
416 presence.count_ones(),
417 lengths.len(),
418 ));
419 }
420 } else {
421 for &len in lengths {
422 end = checked_absolute_end(end, len)?;
423 absolute.push(end);
424 }
425 }
426 Ok(absolute)
427}
428
429pub(crate) fn rebuild_dictionary<'d, 'l>(
434 dict: DictLayout,
435 lengths: &'l [u32],
436 data: &'d [u8],
437) -> MltResult<(Cow<'d, str>, Cow<'l, [u32]>)> {
438 match dict {
439 DictLayout::Plain => Ok((Cow::Borrowed(str::from_utf8(data)?), Cow::Borrowed(lengths))),
440 #[cfg(feature = "unstable-v2")]
441 DictLayout::FrontCoded => {
442 let (entries, entry_lengths) = front_decode(FrontLengths::split(lengths)?, data)?;
443 Ok((Cow::Owned(entries), Cow::Owned(entry_lengths)))
444 }
445 }
446}
447
448fn decode_dictionary_strings<'a>(
449 name: &'a str,
450 dict_lengths: &[u32],
451 offsets: &[u32],
452 presence: Option<&BitSlice<u8, Lsb0>>,
453 dict_data: &str,
454 dec: &mut Decoder,
455) -> MltResult<ParsedStrings<'a>> {
456 let dict_spans = shared_dict_spans(dict_lengths, dec)?;
457 let resolved_spans = resolve_dict_spans(offsets, presence, &dict_spans, dec)?;
458 let mut lengths = dec.alloc(resolved_spans.len())?;
459 let dict_len = dict_data.len();
461 let total = resolved_spans
462 .iter()
463 .flatten()
464 .fold(0_usize, |sum, &(start, end)| {
465 sum.saturating_add(
466 end.into_usize()
467 .min(dict_len)
468 .saturating_sub(start.into_usize()),
469 )
470 });
471 dec.consume_items::<u8>(total)?;
472 let mut data = String::with_capacity(total);
473 let mut end = 0_i32;
474 for span in resolved_spans {
475 if let Some(span) = span {
476 let value = dict_span_str(dict_data, span)?;
477 data.push_str(value);
478 end = checked_string_end(end, value.len())?;
479 lengths.push(end);
480 } else {
481 lengths.push(encode_null_end(end));
482 }
483 }
484 Ok(ParsedStrings {
485 name,
486 lengths,
487 data: Cow::Owned(data),
488 })
489}
490
491pub(crate) fn encode_null_end(end: i32) -> i32 {
492 -end - 1
493}
494
495fn decode_end(end: i32) -> u32 {
496 if end >= 0 {
497 u32::try_from(end).expect("non-negative decoded string end must fit in u32")
498 } else {
499 u32::try_from(-i64::from(end) - 1).expect("encoded null boundary must fit in u32")
500 }
501}
502
503pub(crate) fn checked_string_end(current_end: i32, byte_len: usize) -> MltResult<i32> {
504 let byte_len = u32::try_from(byte_len)?;
505 checked_absolute_end(current_end, byte_len)
506}
507
508pub(crate) fn checked_absolute_end(current_end: i32, delta: u32) -> MltResult<i32> {
509 let delta = i32::try_from(delta)?;
510 current_end.checked_add(delta).or_overflow()
511}
512
513impl<'a> RawSharedDict<'a> {
514 #[must_use]
515 pub fn new(
516 name: &'a str,
517 encoding: RawSharedDictEncoding<'a>,
518 dict: DictLayout,
519 children: Vec<RawSharedDictItem<'a>>,
520 ) -> Self {
521 Self {
522 name,
523 encoding,
524 dict,
525 children,
526 }
527 }
528
529 pub fn decode(self, dec: &mut Decoder) -> MltResult<ParsedSharedDict<'a>> {
531 let prefix = self.name;
532 let (data, dict_spans) = match self.encoding {
533 RawSharedDictEncoding::Plain(plain_data) => {
534 let (blob, lengths) = plain_data.decode_bytes(dec)?;
535 let (entries, lengths) = rebuild_dictionary(self.dict, &lengths, blob)?;
536 let dict_spans = shared_dict_spans(&lengths, dec)?;
537 (entries, dict_spans)
538 }
539 RawSharedDictEncoding::FsstPlain(fsst_data) => {
540 let (blob, lengths) = fsst_data.decode_bytes(dec)?;
541 let (entries, lengths) = rebuild_dictionary(self.dict, &lengths, &blob)?;
543 let (entries, lengths) = (entries.into_owned(), lengths.into_owned());
544 let dict_spans = shared_dict_spans(&lengths, dec)?;
545 (Cow::Owned(entries), dict_spans)
546 }
547 };
548 let mut items = Vec::with_capacity(self.children.len());
549 for child in self.children {
550 let offsets: Vec<u32> = child.data.decode_ints(dec)?;
551 let presence = child.presence.decode_bits(dec)?;
552 let ranges = resolve_dict_spans(&offsets, presence.as_deref(), &dict_spans, dec)?
553 .into_iter()
554 .map(|span| match span {
555 Some(span) => encode_shared_dict_range(span.0, span.1),
556 None => Ok(DictRange::NULL),
557 })
558 .collect::<Result<Vec<_>, _>>()?;
559 items.push(ParsedSharedDictItem {
560 suffix: child.name,
561 ranges,
562 });
563 }
564
565 let parsed = ParsedSharedDict {
566 prefix,
567 data,
568 items,
569 };
570 let bytes = parsed.items.iter().try_fold(
572 u32::try_from(parsed.data.len()).or_overflow()?,
573 |acc, item| {
574 let n = u32::try_from(item.ranges.len() * size_of::<DictRange>()).or_overflow()?;
575 acc.checked_add(n).or_overflow()
576 },
577 )?;
578 dec.consume(bytes)?;
579 Ok(parsed)
580 }
581}
582
583pub(crate) fn encode_shared_dict_range(start: u32, end: u32) -> MltResult<DictRange> {
584 Ok(DictRange {
585 start: i32::try_from(start)?,
586 end: i32::try_from(end)?,
587 })
588}
589
590#[cfg(test)]
591mod tests {
592 use rstest::rstest;
593
594 use super::*;
595 use crate::test_helpers::dec;
596
597 #[rstest]
598 #[case::empty(&[], "", &[])]
599 #[case::all_present(&[1, 3, 6], "abbccc", &[Some("a"), Some("bb"), Some("ccc")])]
600 #[case::leading_null(&[-1, 1], "a", &[None, Some("a")])]
601 #[case::trailing_null(&[1, -2], "a", &[Some("a"), None])]
602 #[case::consecutive_nulls(&[1, -2, -2, 2], "ab", &[Some("a"), None, None, Some("b")])]
603 #[case::only_nulls(&[-1, -1], "", &[None, None])]
604 #[case::empty_string_after_a_value(&[1, 1, -2], "a", &[Some("a"), Some(""), None])]
605 #[case::empty_string_after_a_null(&[1, -2, 1], "a", &[Some("a"), None, Some("")])]
606 #[case::multibyte(&[2, 7], "µ€µ", &[Some("µ"), Some("€µ")])]
607 fn rows_read_back_from_the_length_stream(
608 #[case] lengths: &[i32],
609 #[case] data: &str,
610 #[case] expected: &[Option<&str>],
611 ) {
612 let strings = ParsedStrings::new("col", lengths.to_vec(), Cow::Borrowed(data));
613
614 assert_eq!(strings.feature_count(), expected.len());
615 assert_eq!(
616 strings.materialize(),
617 expected
618 .iter()
619 .map(|v| v.map(str::to_string))
620 .collect::<Vec<_>>()
621 );
622 assert_eq!(
623 strings.dense_values(),
624 expected
625 .iter()
626 .flatten()
627 .map(|v| (*v).to_string())
628 .collect::<Vec<_>>()
629 );
630 assert_eq!(
631 strings.presence_bools(),
632 expected.iter().map(Option::is_some).collect::<Vec<_>>()
633 );
634 for (idx, want) in expected.iter().enumerate() {
635 assert_eq!(strings.get(u32::try_from(idx).expect("small")), *want);
636 }
637 assert_eq!(
638 strings.get(u32::try_from(expected.len()).expect("small")),
639 None
640 );
641 }
642
643 #[rstest]
644 #[case::end_past_the_corpus(&[9], "ab")]
645 #[case::end_before_the_start(&[2, 1], "ab")]
646 #[case::split_multibyte(&[1], "µ")]
647 fn a_row_outside_the_corpus_reads_as_absent(#[case] lengths: &[i32], #[case] data: &str) {
648 let strings = ParsedStrings::new("col", lengths.to_vec(), Cow::Borrowed(data));
649 let last = u32::try_from(lengths.len() - 1).expect("small");
650
651 assert_eq!(strings.get(last), None);
652 assert!(strings.presence_bools().iter().all(|&p| p));
653 assert_eq!(strings.materialize().last(), Some(&None));
654 }
655
656 fn dictionary_rows(
657 dict_lengths: &[u32],
658 dict: &str,
659 offsets: &[u32],
660 ) -> MltResult<Vec<Option<String>>> {
661 decode_dictionary_strings("col", dict_lengths, offsets, None, dict, &mut dec())
662 .map(|strings| strings.materialize())
663 }
664
665 #[test]
666 fn dictionary_rows_repeat_multibyte_entries() {
667 let rows = dictionary_rows(&[2, 3], "µ€", &[1, 0, 1]).expect("valid dictionary");
668 assert_eq!(
669 rows,
670 [
671 Some("€".to_string()),
672 Some("µ".to_string()),
673 Some("€".to_string())
674 ]
675 );
676 }
677
678 #[test]
679 fn dictionary_entry_past_the_dictionary() {
680 let err = dictionary_rows(&[5], "ab", &[0]).expect_err("entry runs past the dictionary");
681 assert!(matches!(err, BufferUnderflow(5, 2)), "{err:?}");
682 }
683
684 #[test]
685 fn dictionary_entry_splitting_a_character() {
686 let err = dictionary_rows(&[1, 1], "µ", &[0]).expect_err("entry splits a character");
687 assert!(matches!(err, MltError::Utf8(_)), "{err:?}");
688 }
689
690 #[test]
691 fn dictionary_rows_are_charged_to_the_budget() {
692 let consumed = |dict: &str| {
693 let mut dec = dec();
694 let len = u32::try_from(dict.len()).expect("small");
695 decode_dictionary_strings("col", &[len], &[0; 100], None, dict, &mut dec)
696 .expect("valid dictionary");
697 dec.consumed()
698 };
699 assert_eq!(consumed("abc") - consumed("a"), 200);
700 }
701}