1use std::time::Instant;
65
66use rudb_common::{Error, Result};
67
68use crate::chooser::{Chooser, EXHAUSTIVE, Settled};
69use crate::fsst::{MAX_SYMBOL_LEN, SymbolTable};
70use crate::integer;
71use crate::lz;
72use crate::reader::Reader;
73use crate::sequence::Sequence;
74use crate::tally::{self, Family};
75
76const MAX_DEPTH: u8 = 2;
79
80const SHARE_DIVISOR: usize = 20;
84
85const LZ_FLOOR: usize = 4096;
91
92pub(crate) const SAMPLE_BYTES: usize = 64 * 1024;
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub enum Kind {
102 Constant = 0,
104 Plain = 1,
106 Fsst = 2,
108 Dict = 3,
110 Front = 4,
112 Lz = 5,
115}
116
117impl Kind {
118 pub const ALL: [Self; 6] =
120 [Self::Constant, Self::Plain, Self::Fsst, Self::Dict, Self::Front, Self::Lz];
121
122 fn tag(self) -> u8 {
123 self as u8
124 }
125
126 fn from_tag(tag: u8) -> Result<Self> {
127 match tag {
128 0 => Ok(Self::Constant),
129 1 => Ok(Self::Plain),
130 2 => Ok(Self::Fsst),
131 3 => Ok(Self::Dict),
132 4 => Ok(Self::Front),
133 5 => Ok(Self::Lz),
134 other => Err(Error::internal(format!("unknown string encoding tag {other}"))),
135 }
136 }
137
138 #[must_use]
140 pub fn name(self) -> &'static str {
141 match self {
142 Self::Constant => "CONSTANT",
143 Self::Plain => "PLAIN",
144 Self::Fsst => "FSST",
145 Self::Dict => "DICT",
146 Self::Front => "FRONT",
147 Self::Lz => "LZ",
148 }
149 }
150}
151
152pub fn encode(values: &[&[u8]]) -> Result<Vec<u8>> {
163 encode_with(values, &EXHAUSTIVE)
164}
165
166pub fn encode_with(values: &[&[u8]], chooser: &dyn Chooser) -> Result<Vec<u8>> {
176 encode_at(values, 0, chooser)
177}
178
179#[derive(Debug, Clone, Default, PartialEq, Eq)]
192pub struct Flat {
193 bytes: Vec<u8>,
194 ends: Vec<usize>,
198}
199
200impl Flat {
201 fn with_capacity(count: usize, bytes: usize) -> Self {
202 Self { bytes: Vec::with_capacity(bytes), ends: Vec::with_capacity(count) }
203 }
204
205 fn push(&mut self, value: &[u8]) {
206 self.bytes.extend_from_slice(value);
207 self.ends.push(self.bytes.len());
208 }
209
210 fn start(&self, index: usize) -> usize {
212 if index == 0 { 0 } else { self.ends[index - 1] }
213 }
214
215 #[must_use]
217 pub fn len(&self) -> usize {
218 self.ends.len()
219 }
220
221 #[must_use]
223 pub fn is_empty(&self) -> bool {
224 self.ends.is_empty()
225 }
226
227 #[must_use]
230 pub fn bytes(&self) -> &[u8] {
231 &self.bytes
232 }
233
234 #[must_use]
236 pub fn get(&self, index: usize) -> Option<&[u8]> {
237 let end = *self.ends.get(index)?;
238 self.bytes.get(self.start(index)..end)
239 }
240
241 pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
243 let mut at = 0;
244 self.ends.iter().map(move |end| {
245 let value = self.bytes.get(at..*end).unwrap_or_default();
246 at = *end;
247 value
248 })
249 }
250
251 #[must_use]
253 pub fn into_bytes(self) -> Vec<u8> {
254 self.bytes
255 }
256
257 #[must_use]
264 pub fn into_parts(self) -> (Vec<u8>, Vec<usize>) {
265 (self.bytes, self.ends)
266 }
267
268 fn into_values(self) -> Vec<Vec<u8>> {
269 let mut values = Vec::with_capacity(self.len());
270 let mut at = 0;
271 for end in &self.ends {
272 values.push(self.bytes[at..*end].to_vec());
273 at = *end;
274 }
275 values
276 }
277}
278
279pub fn decode_flat(bytes: &[u8]) -> Result<Flat> {
285 let mut reader = Reader::new(bytes);
286 let flat = decode_chunk(&mut reader)?;
287 if reader.remaining() != 0 {
288 return Err(Error::internal(format!(
289 "{} bytes left over after decoding a string chunk",
290 reader.remaining()
291 )));
292 }
293 Ok(flat)
294}
295
296pub fn holds_in(bytes: &[u8], sequence: &Sequence) -> Result<Option<Vec<bool>>> {
307 if bytes.first() != Some(&Kind::Fsst.tag()) {
308 return Ok(None);
309 }
310 let mut reader = Reader::new(bytes);
311 reader.u8()?;
312 let count = reader.u32()? as usize;
313 let runs = read_compressed(&mut reader, count)?;
314 let mut coded = sequence.over(&runs.table);
315 let mut held = Vec::with_capacity(count);
316 let mut payload = runs.payload;
317 for &run in &runs.lengths {
318 let Some((codes, rest)) = payload.split_at_checked(run) else {
319 return Err(Error::internal("a compressed run is past the end of its chunk"));
320 };
321 payload = rest;
322 held.push(coded.holds(codes)?);
323 }
324 if reader.remaining() != 0 {
325 return Err(Error::internal(format!(
326 "{} bytes left over after decoding a string chunk",
327 reader.remaining()
328 )));
329 }
330 Ok(Some(held))
331}
332
333pub fn decode_flat_at(bytes: &[u8], positions: &[u32]) -> Result<Flat> {
345 if positions.windows(2).any(|pair| pair[0] >= pair[1]) {
346 return Err(Error::internal("the positions to decode do not rise"));
347 }
348 let mut reader = Reader::new(bytes);
349 let flat = if bytes.first() == Some(&Kind::Fsst.tag()) {
350 reader.u8()?;
351 let count = reader.u32()? as usize;
352 let runs = read_compressed(&mut reader, count)?;
353 let mut flat = Flat::with_capacity(positions.len(), runs.payload.len());
354 let mut at = 0;
355 let mut next = 0;
356 for &position in positions {
357 let position = position as usize;
358 if position >= count {
359 return Err(Error::internal(format!("value {position} is not in the chunk")));
360 }
361 at += runs.lengths[next..position].iter().sum::<usize>();
362 runs.run_into(position, &mut at, &mut flat.bytes)?;
363 flat.ends.push(flat.bytes.len());
364 next = position + 1;
365 }
366 flat
367 } else {
368 let whole = decode_chunk(&mut reader)?;
369 let mut flat = Flat::with_capacity(positions.len(), 0);
370 for &position in positions {
371 let value = whole
372 .get(position as usize)
373 .ok_or_else(|| Error::internal(format!("value {position} is not in the chunk")))?;
374 flat.push(value);
375 }
376 flat
377 };
378 if reader.remaining() != 0 {
379 return Err(Error::internal(format!(
380 "{} bytes left over after decoding a string chunk",
381 reader.remaining()
382 )));
383 }
384 Ok(flat)
385}
386
387pub fn decode_prefix(bytes: &[u8]) -> Result<(Vec<Vec<u8>>, usize)> {
396 let mut reader = Reader::new(bytes);
397 let values = decode_chunk(&mut reader)?;
398 Ok((values.into_values(), reader.used()))
399}
400
401pub fn describe_prefix(bytes: &[u8]) -> Result<(String, usize)> {
407 let mut reader = Reader::new(bytes);
408 let text = describe_chunk(&mut reader)?;
409 Ok((text, reader.used()))
410}
411
412pub fn decode(bytes: &[u8]) -> Result<Vec<Vec<u8>>> {
418 Ok(decode_flat(bytes)?.into_values())
419}
420
421pub fn candidate_sizes(values: &[&[u8]]) -> Result<Vec<(Kind, usize)>> {
428 let mut sizes = Vec::new();
429 for kind in candidates(values, 0) {
430 if let Some(bytes) = encode_as(kind, values, 0, &EXHAUSTIVE)? {
431 sizes.push((kind, bytes.len()));
432 }
433 }
434 Ok(sizes)
435}
436
437#[must_use]
444pub fn offered(values: &[&[u8]]) -> Vec<Kind> {
445 candidates(values, 0)
446}
447
448pub fn encode_only(kind: Kind, values: &[&[u8]]) -> Result<Option<Vec<u8>>> {
460 encode_as(kind, values, 0, &EXHAUSTIVE)
461}
462
463#[must_use]
473pub fn fsst_shape(values: &[&[u8]]) -> Settled {
474 let started = Instant::now();
475 let table = SymbolTable::train(&sample_of(values));
476 tally::chose(Family::String, started);
477 Settled::new(vec![Kind::Fsst], Vec::new()).with_symbols(0, table)
478}
479
480pub fn encode_fsst(values: &[&[u8]], shape: &Settled) -> Result<Option<Vec<u8>>> {
490 let out =
491 tally::offer(Family::String, Kind::Fsst.tag(), || encode_as(Kind::Fsst, values, 0, shape))?;
492 if out.is_some() {
493 tally::kept(Family::String, Kind::Fsst.tag());
494 }
495 Ok(out)
496}
497
498pub(crate) fn size_as(kind: Kind, values: &[&[u8]], depth: u8) -> Result<Option<usize>> {
503 Ok(encode_as(kind, values, depth, &EXHAUSTIVE)?.map(|bytes| bytes.len()))
504}
505
506pub fn describe(bytes: &[u8]) -> Result<String> {
512 let mut reader = Reader::new(bytes);
513 describe_chunk(&mut reader)
514}
515
516#[must_use]
528pub fn with_symbols(shape: Settled, blocks: &[Vec<&[u8]>]) -> Settled {
529 let kinds = shape.strings();
530 let Some(depth) =
531 (0..=kinds.len()).find(|&at| matches!(kinds.get(at), Some(Kind::Fsst) | None))
532 else {
533 return shape;
534 };
535 let leads =
536 kinds[..depth].iter().all(|kind| matches!(kind, Kind::Front | Kind::Lz | Kind::Dict));
537 if !leads || depth > usize::from(MAX_DEPTH) {
538 return shape;
539 }
540 let mut reached: Vec<Vec<u8>> = Vec::new();
541 for block in blocks {
542 let mut values: Vec<Vec<u8>> = block.iter().map(|value| value.to_vec()).collect();
543 for kind in &kinds[..depth] {
544 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
545 values = match kind {
546 Kind::Front => front_code(&refs).1.into_iter().map(<[u8]>::to_vec).collect(),
547 Kind::Dict => dictionary_of(&refs).0.into_iter().map(<[u8]>::to_vec).collect(),
548 _ => {
549 let joined = refs.concat();
550 lz::tokens_of(&joined).literals.into_iter().map(<[u8]>::to_vec).collect()
551 }
552 };
553 }
554 reached.extend(values);
555 }
556 let refs: Vec<&[u8]> = reached.iter().map(Vec::as_slice).collect();
557 let table = SymbolTable::train(&sample_of(&refs));
558 if table.is_empty() {
559 return shape;
560 }
561 shape.with_symbols(depth as u8, table)
562}
563
564fn encode_at(values: &[&[u8]], depth: u8, chooser: &dyn Chooser) -> Result<Vec<u8>> {
565 let started = Instant::now();
566 let offered = candidates(values, depth);
567 let narrowed = chooser.narrow_strings(values, &offered, depth);
568 let counted = depth == 0;
570 if counted {
571 tally::chose(Family::String, started);
572 }
573 let mut best: Option<(Kind, Vec<u8>)> = None;
574 for kind in narrowed {
575 let encoded = if counted {
576 tally::offer(Family::String, kind.tag(), || encode_as(kind, values, depth, chooser))?
577 } else {
578 encode_as(kind, values, depth, chooser)?
579 };
580 let Some(bytes) = encoded else {
581 continue;
582 };
583 if best.as_ref().is_none_or(|(_, current)| bytes.len() < current.len()) {
584 best = Some((kind, bytes));
585 }
586 }
587 let (kind, bytes) =
588 best.ok_or_else(|| Error::internal("no string encoding applied to the chunk"))?;
589 if counted {
590 tally::kept(Family::String, kind.tag());
591 }
592 Ok(bytes)
593}
594
595fn candidates(values: &[&[u8]], depth: u8) -> Vec<Kind> {
596 let mut kinds = vec![Kind::Plain];
597 if values.is_empty() {
598 return kinds;
599 }
600 if values.iter().all(|value| *value == values[0]) {
601 return vec![Kind::Constant];
602 }
603 kinds.push(Kind::Fsst);
604 if depth < MAX_DEPTH && has_duplicates(values) {
605 kinds.push(Kind::Dict);
606 }
607 if depth < MAX_DEPTH && sharing_of(values) >= total_len(values) / SHARE_DIVISOR {
608 kinds.push(Kind::Front);
609 }
610 if depth < MAX_DEPTH && total_len(values) >= LZ_FLOOR {
611 kinds.push(Kind::Lz);
612 }
613 kinds
614}
615
616fn sharing_of(values: &[&[u8]]) -> usize {
623 let mut shared = 0;
624 for pair in values.windows(2) {
625 shared += shared_prefix(pair[0], pair[1]);
626 }
627 shared
628}
629
630pub(crate) fn front_code<'a>(values: &[&'a [u8]]) -> (Vec<i64>, Vec<&'a [u8]>) {
636 let mut prefixes = Vec::with_capacity(values.len());
637 let mut suffixes: Vec<&'a [u8]> = Vec::with_capacity(values.len());
638 let mut previous: &[u8] = b"";
639 for value in values {
640 let value: &'a [u8] = value;
641 let shared = shared_prefix(previous, value);
642 prefixes.push(shared as i64);
643 suffixes.push(&value[shared..]);
644 previous = value;
645 }
646 (prefixes, suffixes)
647}
648
649pub(crate) fn front_decode(prefixes: &[i64], suffixes: Vec<Vec<u8>>) -> Result<Vec<Vec<u8>>> {
656 let mut values: Vec<Vec<u8>> = Vec::with_capacity(suffixes.len());
657 for (index, suffix) in suffixes.into_iter().enumerate() {
658 let shared = usize::try_from(prefixes[index])
659 .map_err(|_| Error::internal("a negative shared prefix length"))?;
660 let previous: &[u8] = if index == 0 { b"" } else { &values[index - 1] };
661 if shared > previous.len() {
662 return Err(Error::internal(format!(
663 "a value shares {shared} bytes with a value {} bytes long",
664 previous.len()
665 )));
666 }
667 let mut value = Vec::with_capacity(shared + suffix.len());
668 value.extend_from_slice(&previous[..shared]);
669 value.extend_from_slice(&suffix);
670 values.push(value);
671 }
672 Ok(values)
673}
674
675fn shared_prefix(previous: &[u8], value: &[u8]) -> usize {
676 let limit = previous.len().min(value.len());
677 let mut shared = 0;
678 while shared < limit && previous[shared] == value[shared] {
679 shared += 1;
680 }
681 shared
682}
683
684fn total_len(values: &[&[u8]]) -> usize {
685 values.iter().map(|value| value.len()).sum()
686}
687
688fn encode_as(
689 kind: Kind,
690 values: &[&[u8]],
691 depth: u8,
692 chooser: &dyn Chooser,
693) -> Result<Option<Vec<u8>>> {
694 let mut out = vec![kind.tag()];
695 put_u32(&mut out, u32::try_from(values.len()).map_err(|_| too_long(values.len()))?);
696 match kind {
697 Kind::Constant => {
698 let Some(first) = values.first() else {
699 return Ok(None);
700 };
701 if values.iter().any(|value| value != first) {
702 return Ok(None);
703 }
704 put_u32(&mut out, u32::try_from(first.len()).map_err(|_| too_long(first.len()))?);
705 out.extend_from_slice(first);
706 }
707 Kind::Plain => {
708 out.extend_from_slice(&encode_lengths(values, chooser)?);
709 for value in values {
710 out.extend_from_slice(value);
711 }
712 }
713 Kind::Fsst => {
714 let trained;
715 let table = match chooser.symbols(depth) {
716 Some(table) => table,
717 None => {
718 trained = SymbolTable::train(&sample_of(values));
719 &trained
720 }
721 };
722 if table.is_empty() {
723 return Ok(None);
724 }
725 let mut compressed = Vec::new();
726 let mut lengths = Vec::with_capacity(values.len());
727 for value in values {
728 let before = compressed.len();
729 table.compress(value, &mut compressed);
730 lengths.push((compressed.len() - before) as i64);
731 }
732 table.serialize(&mut out);
733 out.extend_from_slice(&integer::encode_with(&lengths, chooser)?);
734 out.extend_from_slice(&compressed);
735 }
736 Kind::Dict => {
737 let (entries, codes) = dictionary_of(values);
738 if entries.is_empty() {
739 return Ok(None);
740 }
741 out.extend_from_slice(&encode_at(&entries, depth + 1, chooser)?);
742 out.extend_from_slice(&integer::encode_with(&codes, chooser)?);
743 }
744 Kind::Front => {
745 let (prefixes, suffixes) = front_code(values);
746 out.extend_from_slice(&integer::encode_with(&prefixes, chooser)?);
747 out.extend_from_slice(&encode_at(&suffixes, depth + 1, chooser)?);
748 }
749 Kind::Lz => {
750 let mut joined = Vec::with_capacity(total_len(values));
751 let mut sizes = Vec::with_capacity(values.len());
752 for value in values {
753 joined.extend_from_slice(value);
754 sizes.push(value.len() as i64);
755 }
756 let tokens = lz::tokens_of(&joined);
757 out.extend_from_slice(&integer::encode_with(&sizes, chooser)?);
758 out.extend_from_slice(&integer::encode_with(&tokens.lengths, chooser)?);
759 out.extend_from_slice(&integer::encode_with(&tokens.offsets, chooser)?);
760 out.extend_from_slice(&encode_at(&tokens.literals, depth + 1, chooser)?);
761 }
762 }
763 Ok(Some(out))
764}
765
766fn decode_chunk(reader: &mut Reader<'_>) -> Result<Flat> {
767 let kind = Kind::from_tag(reader.u8()?)?;
768 let count = reader.u32()? as usize;
769 match kind {
770 Kind::Constant => {
771 let len = reader.u32()? as usize;
772 let value = reader.bytes(len)?;
773 let mut flat = Flat::with_capacity(count, len.saturating_mul(count));
774 for _ in 0..count {
775 flat.push(value);
776 }
777 Ok(flat)
778 }
779 Kind::Plain => {
780 let lengths = decode_lengths(reader, count)?;
781 let total = sum_of(&lengths)?;
784 let payload = reader.bytes(total)?;
785 let mut flat = Flat::with_capacity(count, total);
786 flat.bytes.extend_from_slice(payload);
787 let mut at = 0;
788 for length in lengths {
789 at += length;
790 flat.ends.push(at);
791 }
792 Ok(flat)
793 }
794 Kind::Fsst => {
795 let runs = read_compressed(reader, count)?;
796 let mut flat = Flat::with_capacity(count, 0);
797 runs.all_into(&mut flat)?;
798 Ok(flat)
799 }
800 Kind::Dict => {
801 let dictionary = decode_chunk(reader)?;
802 let codes = decode_integers(reader)?;
803 if codes.len() != count {
804 return Err(Error::internal(format!(
805 "a dictionary chunk says it holds {count} values and has {} codes",
806 codes.len()
807 )));
808 }
809 let mut flat = Flat::with_capacity(count, dictionary.bytes.len());
810 for code in codes {
811 let entry =
812 usize::try_from(code).ok().and_then(|index| dictionary.get(index)).ok_or_else(
813 || Error::internal(format!("code {code} is not in the dictionary")),
814 )?;
815 flat.push(entry);
816 }
817 Ok(flat)
818 }
819 Kind::Front => {
820 let prefixes = decode_integers(reader)?;
821 let suffixes = decode_chunk(reader)?;
822 if prefixes.len() != count || suffixes.len() != count {
823 return Err(Error::internal(format!(
824 "a front coded chunk says it holds {count} values and has {} prefixes and {} suffixes",
825 prefixes.len(),
826 suffixes.len()
827 )));
828 }
829 let mut room = 0usize;
836 let mut previous = 0usize;
837 for (index, prefix) in prefixes.iter().enumerate() {
838 let shared = usize::try_from(*prefix)
839 .map_err(|_| Error::internal("a negative shared prefix length"))?;
840 if shared > previous {
841 return Err(Error::internal(format!(
842 "a value shares {shared} bytes with a value {previous} bytes long"
843 )));
844 }
845 previous = shared + suffixes.get(index).map_or(0, <[u8]>::len);
846 room = room
847 .checked_add(previous)
848 .ok_or_else(|| Error::internal("a string chunk longer than memory"))?;
849 }
850 let mut flat = Flat::with_capacity(count, room);
851 for (index, &prefix) in prefixes.iter().enumerate() {
852 let shared = prefix as usize;
853 let from = if index == 0 { 0 } else { flat.start(index - 1) };
854 flat.bytes.extend_from_within(from..from + shared);
855 flat.bytes.extend_from_slice(suffixes.get(index).expect("in range"));
856 flat.ends.push(flat.bytes.len());
857 }
858 Ok(flat)
859 }
860 Kind::Lz => {
861 let sizes = decode_integers(reader)?;
862 let lengths = decode_integers(reader)?;
863 let offsets = decode_integers(reader)?;
864 if sizes.len() != count {
865 return Err(Error::internal(format!(
866 "a matched chunk says it holds {count} values and has {} lengths",
867 sizes.len()
868 )));
869 }
870 let mut total = 0usize;
871 let mut widths = Vec::with_capacity(count);
872 for size in sizes {
873 let width = usize::try_from(size)
874 .map_err(|_| Error::internal("a negative string length"))?;
875 total = total
876 .checked_add(width)
877 .ok_or_else(|| Error::internal("a string chunk longer than memory"))?;
878 widths.push(width);
879 }
880 let mut flat = Flat::with_capacity(count, total.saturating_add(REPLAY_SLACK));
885 replay_literals(reader, &lengths, &offsets, total, &mut flat.bytes)?;
886 if flat.bytes.len() != total {
887 return Err(Error::internal(format!(
888 "a matched chunk rebuilt {} bytes where its lengths add up to {total}",
889 flat.bytes.len()
890 )));
891 }
892 let mut at = 0;
893 for width in widths {
894 at += width;
895 flat.ends.push(at);
896 }
897 Ok(flat)
898 }
899 }
900}
901
902struct Compressed<'a> {
908 table: SymbolTable,
910 lengths: Vec<usize>,
912 payload: &'a [u8],
914}
915
916impl Compressed<'_> {
917 fn run_into(&self, index: usize, at: &mut usize, out: &mut Vec<u8>) -> Result<()> {
926 self.table.decompress(self.run(index, at)?, out)
927 }
928
929 fn all_into(&self, flat: &mut Flat) -> Result<()> {
942 let out = &mut flat.bytes;
943 let mut at = out.len();
944 let mut payload = self.payload;
945 for &run in &self.lengths {
946 let Some((codes, rest)) = payload.split_at_checked(run) else {
947 return Err(Error::internal("a compressed run is past the end of its chunk"));
948 };
949 payload = rest;
950 let need = run
951 .checked_mul(MAX_SYMBOL_LEN)
952 .and_then(|room| room.checked_add(at))
953 .ok_or_else(|| Error::internal("a compressed chunk longer than memory"))?;
954 if out.len() < need {
955 out.resize(need.max(out.len() * 2), 0);
956 }
957 at = self.table.decompress_at(codes, out, at)?;
958 flat.ends.push(at);
959 }
960 out.truncate(at);
961 Ok(())
962 }
963
964 fn run(&self, index: usize, at: &mut usize) -> Result<&[u8]> {
967 let length = *self
968 .lengths
969 .get(index)
970 .ok_or_else(|| Error::internal(format!("run {index} is not in the chunk")))?;
971 let end = at
972 .checked_add(length)
973 .ok_or_else(|| Error::internal("a compressed chunk longer than memory"))?;
974 let run = self
975 .payload
976 .get(*at..end)
977 .ok_or_else(|| Error::internal("a compressed run is past the end of its chunk"))?;
978 *at = end;
979 Ok(run)
980 }
981}
982
983fn read_compressed<'a>(reader: &mut Reader<'a>, count: usize) -> Result<Compressed<'a>> {
992 let (table, used) = SymbolTable::deserialize(reader.rest())?;
993 reader.skip(used)?;
994 let lengths = decode_lengths(reader, count)?;
995 let compressed_len = sum_of(&lengths)?;
998 if compressed_len > reader.remaining() {
999 return Err(Error::internal(format!(
1000 "a compressed chunk says it holds {compressed_len} bytes and has {}",
1001 reader.remaining()
1002 )));
1003 }
1004 let payload = reader.bytes(compressed_len)?;
1005 Ok(Compressed { table, lengths, payload })
1006}
1007
1008fn replay_literals(
1021 reader: &mut Reader<'_>,
1022 lengths: &[i64],
1023 offsets: &[i64],
1024 total: usize,
1025 out: &mut Vec<u8>,
1026) -> Result<()> {
1027 if reader.rest().first() == Some(&Kind::Fsst.tag()) {
1028 reader.u8()?;
1029 let runs = reader.u32()? as usize;
1030 let compressed = read_compressed(reader, runs)?;
1031 return replay_in_place(&compressed, lengths, offsets, total, out);
1032 }
1033 let literals = decode_chunk(reader)?;
1034 lz::rebuild_into(&literals, lengths, offsets, out)
1035}
1036
1037const REPLAY_SLACK: usize = 16;
1043
1044fn replay_in_place(
1056 compressed: &Compressed<'_>,
1057 lengths: &[i64],
1058 offsets: &[i64],
1059 total: usize,
1060 out: &mut Vec<u8>,
1061) -> Result<()> {
1062 let runs = compressed.lengths.len();
1063 if runs != lengths.len() || lengths.len() != offsets.len() {
1064 return Err(Error::internal(format!(
1065 "a matched chunk has {runs} literal runs, {} lengths and {} offsets",
1066 lengths.len(),
1067 offsets.len()
1068 )));
1069 }
1070 let base = out.len();
1071 let room = total
1072 .checked_add(REPLAY_SLACK)
1073 .ok_or_else(|| Error::internal("a string chunk longer than memory"))?;
1074 out.resize(base + room, 0);
1075 let mut payload = compressed.payload;
1076 let mut at = base;
1077 for ((&run, &length), &offset) in compressed.lengths.iter().zip(lengths).zip(offsets) {
1078 let Some((codes, rest)) = payload.split_at_checked(run) else {
1079 return Err(Error::internal("a compressed run is past the end of its chunk"));
1080 };
1081 payload = rest;
1082 at = compressed.table.decompress_at(codes, out, at)?;
1083 let length =
1084 usize::try_from(length).map_err(|_| Error::internal("a negative copy length"))?;
1085 if length == 0 {
1086 continue;
1087 }
1088 let offset =
1089 usize::try_from(offset).map_err(|_| Error::internal("a negative copy offset"))?;
1090 at = copy_back(out, base, at, offset, length)?;
1091 }
1092 if at > base + total {
1093 return Err(Error::internal(format!(
1094 "a matched chunk rebuilt {} bytes where its lengths add up to {total}",
1095 at - base
1096 )));
1097 }
1098 out.truncate(at);
1099 Ok(())
1100}
1101
1102fn copy_back(
1110 out: &mut [u8],
1111 base: usize,
1112 at: usize,
1113 offset: usize,
1114 length: usize,
1115) -> Result<usize> {
1116 if offset == 0 || offset > at - base {
1117 return Err(Error::internal(format!(
1118 "a copy reaches {offset} bytes back into {} bytes of output",
1119 at - base
1120 )));
1121 }
1122 let end = at
1123 .checked_add(length)
1124 .filter(|&end| end <= out.len())
1125 .ok_or_else(|| Error::internal("a matched chunk rebuilds more than its lengths say"))?;
1126 let from = at - offset;
1127 let wide = end + REPLAY_SLACK <= out.len();
1128 if wide && offset >= 16 {
1129 let mut step = 0;
1130 while step < length {
1131 out.copy_within(from + step..from + step + 16, at + step);
1132 step += 16;
1133 }
1134 } else if wide && offset >= 8 {
1135 let mut step = 0;
1136 while step < length {
1137 out.copy_within(from + step..from + step + 8, at + step);
1138 step += 8;
1139 }
1140 } else {
1141 for step in 0..length {
1142 out[at + step] = out[from + step];
1143 }
1144 }
1145 Ok(end)
1146}
1147
1148fn describe_chunk(reader: &mut Reader<'_>) -> Result<String> {
1149 let kind = Kind::from_tag(reader.u8()?)?;
1150 let count = reader.u32()? as usize;
1151 Ok(match kind {
1152 Kind::Constant => {
1153 let len = reader.u32()? as usize;
1154 reader.bytes(len)?;
1155 "CONSTANT".to_string()
1156 }
1157 Kind::Plain => {
1158 let (shape, lengths) = describe_lengths(reader, count)?;
1159 reader.skip(lengths.iter().sum())?;
1160 format!("PLAIN({shape})")
1161 }
1162 Kind::Fsst => {
1163 let (table, used) = SymbolTable::deserialize(reader.rest())?;
1164 reader.skip(used)?;
1165 let (shape, lengths) = describe_lengths(reader, count)?;
1166 reader.skip(lengths.iter().sum())?;
1167 format!("FSST[{}]({shape})", table.len())
1168 }
1169 Kind::Dict => {
1170 let entries = describe_chunk(reader)?;
1171 let codes = describe_integers(reader)?;
1172 format!("DICT({entries}, {codes})")
1173 }
1174 Kind::Front => {
1175 let prefixes = describe_integers(reader)?;
1176 let suffixes = describe_chunk(reader)?;
1177 format!("FRONT({prefixes}, {suffixes})")
1178 }
1179 Kind::Lz => {
1180 let sizes = describe_integers(reader)?;
1181 let lengths = describe_integers(reader)?;
1182 let offsets = describe_integers(reader)?;
1183 let literals = describe_chunk(reader)?;
1184 format!("LZ({sizes}, {lengths}, {offsets}, {literals})")
1185 }
1186 })
1187}
1188
1189fn describe_lengths(reader: &mut Reader<'_>, count: usize) -> Result<(String, Vec<usize>)> {
1193 let (shape, _) = integer::describe_prefix(reader.rest())?;
1194 let lengths = decode_lengths(reader, count)?;
1195 Ok((shape, lengths))
1196}
1197
1198fn encode_lengths(values: &[&[u8]], chooser: &dyn Chooser) -> Result<Vec<u8>> {
1199 let lengths: Vec<i64> = values.iter().map(|value| value.len() as i64).collect();
1200 integer::encode_with(&lengths, chooser)
1201}
1202
1203fn decode_lengths(reader: &mut Reader<'_>, count: usize) -> Result<Vec<usize>> {
1204 let lengths = decode_integers(reader)?;
1205 if lengths.len() != count {
1206 return Err(Error::internal(format!(
1207 "a string chunk says it holds {count} values and has {} lengths",
1208 lengths.len()
1209 )));
1210 }
1211 lengths
1212 .into_iter()
1213 .map(|length| {
1214 usize::try_from(length).map_err(|_| Error::internal("a negative string length"))
1215 })
1216 .collect()
1217}
1218
1219fn sum_of(lengths: &[usize]) -> Result<usize> {
1225 lengths
1226 .iter()
1227 .try_fold(0usize, |total, length| total.checked_add(*length))
1228 .ok_or_else(|| Error::internal("a string chunk longer than memory"))
1229}
1230
1231fn decode_integers(reader: &mut Reader<'_>) -> Result<Vec<i64>> {
1235 let (values, used) = integer::decode_prefix(reader.rest())?;
1236 reader.skip(used)?;
1237 Ok(values)
1238}
1239
1240fn describe_integers(reader: &mut Reader<'_>) -> Result<String> {
1241 let (text, used) = integer::describe_prefix(reader.rest())?;
1242 reader.skip(used)?;
1243 Ok(text)
1244}
1245
1246pub(crate) fn sample_of<'a>(values: &[&'a [u8]]) -> Vec<&'a [u8]> {
1265 sample_bytes_of(values, SAMPLE_BYTES)
1266}
1267
1268pub(crate) fn sample_bytes_of<'a>(values: &[&'a [u8]], budget: usize) -> Vec<&'a [u8]> {
1271 let budget = budget.max(1);
1272 let total: usize = values.iter().map(|value| value.len()).sum();
1273 if total <= budget {
1274 return values.to_vec();
1275 }
1276 let stride = total.div_ceil(budget).max(1);
1277 let span = (stride * 2 - 1).max(1) as u64;
1278 let mut state = 0x2545_f491_4f6c_dd1du64;
1279 let mut sample = Vec::with_capacity(values.len() / stride + 1);
1280 let mut at = 0usize;
1281 while at < values.len() {
1282 sample.push(values[at]);
1283 state ^= state << 13;
1284 state ^= state >> 7;
1285 state ^= state << 17;
1286 at += 1 + (state % span) as usize;
1287 }
1288 sample
1289}
1290
1291fn dictionary_of<'a>(values: &[&'a [u8]]) -> (Vec<&'a [u8]>, Vec<i64>) {
1303 let mut order: Vec<u32> = (0..values.len() as u32).collect();
1304 order.sort_unstable_by(|left, right| values[*left as usize].cmp(values[*right as usize]));
1305 let mut entries: Vec<&'a [u8]> = Vec::new();
1306 let mut codes = vec![0i64; values.len()];
1307 for &index in &order {
1308 let value = values[index as usize];
1309 if entries.last() != Some(&value) {
1310 entries.push(value);
1311 }
1312 codes[index as usize] = (entries.len() - 1) as i64;
1313 }
1314 (entries, codes)
1315}
1316
1317fn has_duplicates(values: &[&[u8]]) -> bool {
1327 let Some(slots) = values.len().checked_mul(2).map(usize::next_power_of_two) else {
1328 return false;
1329 };
1330 let mask = slots - 1;
1331 let mut table = vec![u32::MAX; slots];
1332 for (index, value) in values.iter().enumerate() {
1333 let mut at = hash_of(value) as usize & mask;
1334 loop {
1335 let held = table[at];
1336 if held == u32::MAX {
1337 table[at] = index as u32;
1338 break;
1339 }
1340 if values[held as usize] == *value {
1341 return true;
1342 }
1343 at = (at + 1) & mask;
1344 }
1345 }
1346 false
1347}
1348
1349fn hash_of(value: &[u8]) -> u64 {
1356 let mut hash = 0xcbf2_9ce4_8422_2325_u64;
1357 let mut chunks = value.chunks_exact(8);
1358 for chunk in &mut chunks {
1359 let word = u64::from_le_bytes(chunk.try_into().expect("chunks_exact(8) gives eight bytes"));
1360 hash = (hash ^ word).wrapping_mul(0x1_0000_01b3);
1361 }
1362 for byte in chunks.remainder() {
1363 hash = (hash ^ u64::from(*byte)).wrapping_mul(0x1_0000_01b3);
1364 }
1365 (hash ^ (value.len() as u64)).wrapping_mul(0x1_0000_01b3)
1366}
1367
1368fn too_long(len: usize) -> Error {
1369 Error::internal(format!("a string chunk of {len} is longer than the format allows"))
1370}
1371
1372fn put_u32(out: &mut Vec<u8>, value: u32) {
1373 out.extend_from_slice(&value.to_le_bytes());
1374}
1375
1376#[cfg(test)]
1377mod tests {
1378 use super::*;
1379
1380 fn urls(count: usize) -> Vec<Vec<u8>> {
1381 let hosts = ["www.example.com", "shop.example.com", "news.other.example.org"];
1382 let paths = ["/index.html", "/catalog/item", "/search", "/user/profile/settings"];
1383 (0..count)
1384 .map(|index| {
1385 let host = hosts[index % hosts.len()];
1386 let path = paths[(index / 3) % paths.len()];
1387 format!("http://{host}{path}?session={}&ref=google", index * 7).into_bytes()
1388 })
1389 .collect()
1390 }
1391
1392 #[test]
1395 fn the_values_at_some_positions_are_the_ones_a_whole_decode_has_there() {
1396 let values = urls(1000);
1397 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1398 let positions = [0_u32, 3, 4, 500, 998, 999];
1399 let wanted: Vec<Vec<u8>> =
1400 positions.iter().map(|&at| values[at as usize].clone()).collect();
1401 for kind in offered(&refs) {
1402 let Some(encoded) = encode_only(kind, &refs).expect("encoded") else { continue };
1403 let flat = decode_flat_at(&encoded, &positions).expect("decoded");
1404 assert_eq!(flat.into_values(), wanted, "{kind:?}");
1405 let none = decode_flat_at(&encoded, &[]).expect("decoded");
1406 assert!(none.is_empty(), "{kind:?}");
1407 assert!(decode_flat_at(&encoded, &[4, 3]).is_err(), "{kind:?}");
1408 assert!(decode_flat_at(&encoded, &[1000]).is_err(), "{kind:?}");
1409 }
1410 let fsst = encode_only(Kind::Fsst, &refs).expect("encoded").expect("compressible");
1411 assert_eq!(decode_flat_at(&fsst, &positions).expect("decoded").into_values(), wanted);
1412 }
1413
1414 fn front_lz() -> Settled {
1415 Settled::new(vec![Kind::Front, Kind::Lz], vec![integer::Kind::Packed])
1416 }
1417
1418 #[test]
1422 fn a_block_compressed_against_the_column_table_reads_back() {
1423 let values = urls(4096);
1424 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1425 let blocks: Vec<Vec<&[u8]>> = refs.chunks(1024).take(2).map(<[&[u8]]>::to_vec).collect();
1426 let shape = with_symbols(front_lz(), &blocks);
1427 assert!(shape.symbols(2).is_some(), "FRONT then LZ leaves FSST the third level");
1428 assert!(shape.symbols(1).is_none(), "and only that one");
1429 for block in refs.chunks(1024) {
1430 let encoded = encode_with(block, &shape).expect("encoded");
1431 assert_eq!(decode(&encoded).expect("decoded"), block.to_vec());
1432 }
1433 }
1434
1435 #[test]
1437 fn a_shape_ending_in_plain_gets_no_table() {
1438 let values = urls(1024);
1439 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1440 let plain = Settled::new(vec![Kind::Lz, Kind::Plain], vec![integer::Kind::Packed]);
1441 let shape = with_symbols(plain, std::slice::from_ref(&refs));
1442 assert!((0..=MAX_DEPTH).all(|depth| shape.symbols(depth).is_none()));
1443 let fsst = Settled::new(vec![Kind::Fsst], vec![integer::Kind::Packed]);
1444 assert!(with_symbols(fsst, &[refs]).symbols(0).is_some());
1445 }
1446
1447 fn keyed(values: Vec<Vec<u8>>) -> Vec<Vec<u8>> {
1451 values
1452 .into_iter()
1453 .enumerate()
1454 .map(|(index, value)| {
1455 let key = (index as u64).wrapping_mul(0x9e37_79b9_7f4a_7c15) % 1_000_000_007;
1456 let mut out = format!("{key:010}/").into_bytes();
1457 out.extend_from_slice(&value);
1458 out
1459 })
1460 .collect()
1461 }
1462
1463 fn borrow(values: &[Vec<u8>]) -> Vec<&[u8]> {
1464 values.iter().map(Vec::as_slice).collect()
1465 }
1466
1467 fn round_trip(values: &[Vec<u8>]) -> Vec<u8> {
1468 let borrowed = borrow(values);
1469 let bytes = encode(&borrowed).unwrap();
1470 let back = decode(&bytes).unwrap();
1471 assert_eq!(back, values, "{}", describe(&bytes).unwrap());
1472 check_flat(&bytes, values);
1473 bytes
1474 }
1475
1476 fn check_flat(bytes: &[u8], values: &[Vec<u8>]) {
1479 let flat = decode_flat(bytes).unwrap();
1480 let shape = describe(bytes).unwrap();
1481 assert_eq!(flat.len(), values.len(), "{shape}");
1482 assert_eq!(flat.iter().collect::<Vec<_>>(), borrow(values), "{shape}");
1483 assert_eq!(flat.bytes(), values.concat(), "{shape}");
1484 assert_eq!(flat.get(values.len()), None, "{shape}");
1485 }
1486
1487 fn kind_of(bytes: &[u8]) -> Kind {
1488 Kind::from_tag(bytes[0]).unwrap()
1489 }
1490
1491 #[test]
1492 fn every_shape_decodes_flat_to_what_it_decodes_split() {
1493 let columns =
1497 [urls(600), keyed(urls(600)), vec![b"same".to_vec(); 400], vec![Vec::new(); 7]];
1498 for values in &columns {
1499 let borrowed = borrow(values);
1500 for kind in offered(&borrowed) {
1501 let Some(bytes) = encode_only(kind, &borrowed).unwrap() else {
1502 continue;
1503 };
1504 assert_eq!(decode(&bytes).unwrap(), *values, "{}", kind.name());
1505 let flat = decode_flat(&bytes).unwrap();
1506 assert_eq!(flat.iter().collect::<Vec<_>>(), borrowed, "{}", kind.name());
1507 assert_eq!(flat.bytes(), values.concat(), "{}", kind.name());
1508 }
1509 }
1510 }
1511
1512 #[test]
1513 fn a_front_coded_chunk_that_shares_more_than_it_has_is_an_error() {
1514 let suffixes: [&[u8]; 2] = [b"abc", b"x"];
1518 let mut bytes = vec![Kind::Front.tag()];
1519 put_u32(&mut bytes, 2);
1520 bytes.extend_from_slice(&integer::encode(&[0, 9]).unwrap());
1521 bytes.extend_from_slice(&encode_only(Kind::Plain, &suffixes).unwrap().unwrap());
1522 let error = decode_flat(&bytes).expect_err("a nine byte prefix of a three byte value");
1523 assert_eq!(error.message(), "a value shares 9 bytes with a value 3 bytes long");
1524 assert_eq!(decode(&bytes).unwrap_err().message(), error.message());
1525 }
1526
1527 #[test]
1528 fn the_dictionary_is_sorted_and_the_codes_point_back_at_the_values() {
1529 let values = vec![
1532 b"pear".to_vec(),
1533 b"apple".to_vec(),
1534 b"pear".to_vec(),
1535 b"cherry".to_vec(),
1536 b"apple".to_vec(),
1537 ];
1538 let borrowed = borrow(&values);
1539 let (entries, codes) = dictionary_of(&borrowed);
1540 assert_eq!(entries, vec![b"apple".as_slice(), b"cherry".as_slice(), b"pear".as_slice()]);
1541 assert_eq!(codes, vec![2, 0, 2, 1, 0]);
1542 for (code, value) in codes.iter().zip(&borrowed) {
1543 assert_eq!(entries[*code as usize], *value);
1544 }
1545 }
1546
1547 #[test]
1548 fn a_column_with_nothing_repeated_has_no_duplicates_and_one_with_anything_does() {
1549 let distinct: Vec<Vec<u8>> =
1550 (0..5000).map(|index| format!("value-{index}").into_bytes()).collect();
1551 assert!(!has_duplicates(&borrow(&distinct)));
1552
1553 let mut repeated = distinct.clone();
1555 repeated.push(b"value-0".to_vec());
1556 assert!(has_duplicates(&borrow(&repeated)));
1557
1558 assert!(!has_duplicates(&borrow(&Vec::new())));
1559 assert!(!has_duplicates(&borrow(&[b"one".to_vec()])));
1560 assert!(has_duplicates(&borrow(&vec![b"same".to_vec(); 2])));
1561 }
1562
1563 #[test]
1564 fn long_values_that_differ_only_at_the_end_are_not_confused_for_each_other() {
1565 let stem = "http://www.example.com/a/very/long/path/that/goes/on?session=";
1568 let values: Vec<Vec<u8>> =
1569 (0..2000).map(|index| format!("{stem}{index}").into_bytes()).collect();
1570 assert!(!has_duplicates(&borrow(&values)));
1571 let (entries, codes) = dictionary_of(&borrow(&values));
1572 assert_eq!(entries.len(), values.len());
1573 assert_eq!(codes.len(), values.len());
1574 }
1575
1576 #[test]
1577 fn what_the_chooser_returns_is_the_smallest_of_what_it_was_offered() {
1578 for values in [urls(400), keyed(urls(400)), vec![b"same".to_vec(); 50], Vec::new()] {
1584 let borrowed = borrow(&values);
1585 let chosen = encode(&borrowed).unwrap();
1586 let mut smallest: Option<Vec<u8>> = None;
1587 for kind in offered(&borrowed) {
1588 let Some(bytes) = encode_only(kind, &borrowed).unwrap() else {
1589 continue;
1590 };
1591 if smallest.as_ref().is_none_or(|best| bytes.len() < best.len()) {
1592 smallest = Some(bytes);
1593 }
1594 }
1595 assert_eq!(smallest.as_deref(), Some(chosen.as_slice()), "{}", values.len());
1596 }
1597 }
1598
1599 fn raw_size(values: &[Vec<u8>]) -> usize {
1600 values.iter().map(Vec::len).sum::<usize>() + values.len() * 4
1601 }
1602
1603 #[test]
1604 fn a_matched_chunk_replays_literals_whether_or_not_they_are_compressed() {
1605 let compressed = describe(&round_trip(&keyed(urls(20_000)))).unwrap();
1611 assert!(compressed.starts_with("LZ(") && compressed.contains(", FSST["), "{compressed}");
1612
1613 let buffered = describe(&round_trip(&keyed(urls(300)))).unwrap();
1614 assert!(buffered.starts_with("LZ(") && buffered.contains(", PLAIN("), "{buffered}");
1615 }
1616
1617 #[test]
1618 fn a_copy_back_writes_what_a_byte_at_a_time_copy_writes_at_every_distance() {
1619 let seed: Vec<u8> = (0..40u8).map(|byte| byte.wrapping_mul(37).wrapping_add(11)).collect();
1623 for offset in 1..=seed.len() {
1624 for length in 1..=50 {
1625 let mut wanted = seed.clone();
1626 for _ in 0..length {
1627 wanted.push(wanted[wanted.len() - offset]);
1628 }
1629 let mut out = seed.clone();
1630 out.resize(seed.len() + length + REPLAY_SLACK, 0);
1631 let end = copy_back(&mut out, 0, seed.len(), offset, length).unwrap();
1632 assert_eq!(&out[..end], wanted.as_slice(), "offset {offset} length {length}");
1633 }
1634 }
1635 let mut short = vec![1, 2, 3, 0];
1636 assert!(copy_back(&mut short, 0, 3, 1, 2).is_err(), "past the end of the buffer");
1637 assert!(copy_back(&mut short, 0, 3, 4, 1).is_err(), "further back than the output");
1638 }
1639
1640 #[test]
1641 fn an_empty_chunk_round_trips() {
1642 let bytes = round_trip(&[]);
1643 assert_eq!(kind_of(&bytes), Kind::Plain);
1644 }
1645
1646 #[test]
1647 fn a_constant_column_costs_what_one_value_costs() {
1648 let values = vec![b"https://www.example.com/".to_vec(); 100_000];
1649 let bytes = round_trip(&values);
1650 assert_eq!(kind_of(&bytes), Kind::Constant);
1651 assert_eq!(bytes.len(), 9 + 24);
1652 }
1653
1654 #[test]
1655 fn a_url_column_of_unique_values_is_matched_rather_than_only_compressed() {
1656 let values = keyed(urls(20_000));
1664 let bytes = round_trip(&values);
1665 assert_eq!(kind_of(&bytes), Kind::Lz);
1666
1667 let borrowed: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1670 let fsst = encode_as(Kind::Fsst, &borrowed, 0, &EXHAUSTIVE).unwrap().unwrap();
1671 assert!(bytes.len() < fsst.len(), "{} against FSST {}", bytes.len(), fsst.len());
1672
1673 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1676 assert!(ratio > 4.0, "{ratio:.2}x");
1677 }
1678
1679 #[test]
1680 fn a_sample_of_a_periodic_column_learns_every_phase_of_it() {
1681 let values = urls(20_000);
1686 let borrowed = borrow(&values);
1687 let sample = sample_of(&borrowed);
1688 let mut phases: Vec<&[u8]> = sample
1689 .iter()
1690 .map(|value| {
1691 let query =
1692 value.iter().position(|byte| *byte == b'?').expect("every value has a query");
1693 &value[..query]
1694 })
1695 .collect();
1696 phases.sort_unstable();
1697 phases.dedup();
1698 assert_eq!(phases.len(), 12);
1700 let whole = SymbolTable::train(&borrowed);
1701 let sampled = SymbolTable::train(&sample);
1702 let mut on_whole = Vec::new();
1703 let mut on_sample = Vec::new();
1704 for value in &borrowed {
1705 whole.compress(value, &mut on_whole);
1706 sampled.compress(value, &mut on_sample);
1707 }
1708 assert!(
1711 on_sample.len() < on_whole.len() * 5 / 4,
1712 "{} against {}",
1713 on_sample.len(),
1714 on_whole.len()
1715 );
1716 }
1717
1718 #[test]
1719 fn a_repeating_column_becomes_a_dictionary_of_compressed_entries() {
1720 let distinct = urls(500);
1730 let values: Vec<Vec<u8>> = (0..50_000_u64)
1731 .map(|index| {
1732 let hashed = (index.wrapping_mul(0x9E37_79B9_7F4A_7C15) >> 32) as usize;
1733 distinct[hashed % distinct.len()].clone()
1734 })
1735 .collect();
1736 let bytes = round_trip(&values);
1737 assert_eq!(kind_of(&bytes), Kind::Dict);
1738 let shape = describe(&bytes).unwrap();
1739 assert!(shape.starts_with("DICT(LZ("), "{shape}");
1740 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1741 assert!(ratio > 20.0, "{ratio:.2}x, {shape}");
1742 }
1743
1744 #[test]
1745 fn a_column_of_long_runs_costs_almost_nothing() {
1746 let distinct = urls(50);
1749 let mut values = Vec::new();
1750 for entry in &distinct {
1751 values.extend(std::iter::repeat_n(entry.clone(), 1000));
1752 }
1753 let bytes = round_trip(&values);
1754 let shape = describe(&bytes).unwrap();
1755 assert!(shape.contains("RLE"), "{shape}");
1756 assert!(bytes.len() < 2000, "{} bytes: {shape}", bytes.len());
1757 }
1758
1759 #[test]
1760 fn incompressible_strings_stay_close_to_their_own_size() {
1761 let mut state = 0x2545_f491_4f6c_dd1du64;
1764 let values: Vec<Vec<u8>> = (0..2000)
1765 .map(|_| {
1766 (0..32)
1767 .map(|_| {
1768 state ^= state << 13;
1769 state ^= state >> 7;
1770 state ^= state << 17;
1771 state as u8
1772 })
1773 .collect()
1774 })
1775 .collect();
1776 let bytes = round_trip(&values);
1777 assert!(bytes.len() < 2000 * 32 + 3000, "{} bytes", bytes.len());
1778 }
1779
1780 #[test]
1781 fn lengths_are_stored_rather_than_offsets() {
1782 let values: Vec<Vec<u8>> =
1785 (0..100_000).map(|index| format!("{index:024}").into_bytes()).collect();
1786 let borrowed = borrow(&values);
1787 let bytes = encode_only(Kind::Plain, &borrowed).unwrap().unwrap();
1788 assert_eq!(bytes.len(), 5 + 13 + 100_000 * 24);
1789 }
1790
1791 #[test]
1792 fn empty_strings_are_values_and_not_nulls() {
1793 let values = vec![Vec::new(), b"a".to_vec(), Vec::new(), b"bb".to_vec()];
1794 round_trip(&values);
1795 }
1796
1797 #[test]
1798 fn a_chunk_with_one_value_round_trips() {
1799 round_trip(&[b"only".to_vec()]);
1800 }
1801
1802 #[test]
1803 fn every_candidate_that_applies_decodes_to_the_input() {
1804 let values = urls(3000);
1805 let borrowed = borrow(&values);
1806 let applicable = candidates(&borrowed, 0);
1807 assert!(applicable.len() >= 2, "{applicable:?}");
1808 for kind in applicable {
1809 let bytes = encode_only(kind, &borrowed).unwrap().unwrap();
1810 assert_eq!(decode(&bytes).unwrap(), values, "{}", kind.name());
1811 }
1812 }
1813
1814 #[test]
1815 fn the_chooser_picks_the_smallest_candidate() {
1816 let values = urls(2000);
1817 let borrowed = borrow(&values);
1818 let chosen = encode(&borrowed).unwrap();
1819 for (_, size) in candidate_sizes(&borrowed).unwrap() {
1820 assert!(chosen.len() <= size);
1821 }
1822 }
1823
1824 #[test]
1825 fn a_truncated_chunk_is_an_error_and_not_a_panic() {
1826 let values = urls(40);
1827 let bytes = encode(&borrow(&values)).unwrap();
1828 for len in 0..bytes.len() {
1829 assert!(decode(&bytes[..len]).is_err(), "{len} bytes decoded");
1830 }
1831 }
1832
1833 #[test]
1834 fn trailing_bytes_are_an_error() {
1835 let mut bytes = encode(&borrow(&urls(10))).unwrap();
1836 bytes.push(0);
1837 let error = decode(&bytes).unwrap_err();
1838 assert!(error.message().contains("left over"), "{error}");
1839 }
1840
1841 #[test]
1842 fn an_unknown_tag_is_an_error() {
1843 let error = decode(&[99, 0, 0, 0, 0]).unwrap_err();
1844 assert!(error.message().contains("unknown string encoding tag"), "{error}");
1845 }
1846
1847 #[test]
1848 fn a_dictionary_code_outside_the_dictionary_is_an_error() {
1849 let mut bytes = vec![Kind::Dict.tag()];
1850 put_u32(&mut bytes, 1);
1851 bytes.extend_from_slice(&encode(&[b"one".as_slice()]).unwrap());
1852 bytes.extend_from_slice(&integer::encode(&[9]).unwrap());
1853 let error = decode(&bytes).unwrap_err();
1854 assert!(error.message().contains("not in the dictionary"), "{error}");
1855 }
1856
1857 #[test]
1858 fn a_sorted_column_of_urls_is_front_coded() {
1859 let mut values = urls(20_000);
1863 values.sort();
1864 let bytes = round_trip(&values);
1865 assert_eq!(kind_of(&bytes), Kind::Front);
1866 let shape = describe(&bytes).unwrap();
1867 let mut plain = Vec::new();
1868 let borrowed = borrow(&values);
1869 for (kind, size) in candidate_sizes(&borrowed).unwrap() {
1870 if kind == Kind::Fsst {
1871 plain.push(size);
1872 }
1873 }
1874 let fsst = plain[0];
1875 assert!(bytes.len() * 2 < fsst, "{} against FSST {fsst}: {shape}", bytes.len());
1876 }
1877
1878 #[test]
1879 fn a_column_with_nothing_to_share_is_not_offered_front_coding() {
1880 let mut state = 0x9e37_79b9_7f4a_7c15u64;
1883 let values: Vec<Vec<u8>> = (0..2000)
1884 .map(|_| {
1885 (0..24)
1886 .map(|_| {
1887 state ^= state << 13;
1888 state ^= state >> 7;
1889 state ^= state << 17;
1890 (state % 251) as u8
1891 })
1892 .collect()
1893 })
1894 .collect();
1895 let borrowed = borrow(&values);
1896 assert!(!candidates(&borrowed, 0).contains(&Kind::Front));
1897 }
1898
1899 #[test]
1900 fn a_prefix_longer_than_the_value_before_it_is_an_error() {
1901 let mut bytes = vec![Kind::Front.tag()];
1902 put_u32(&mut bytes, 2);
1903 bytes.extend_from_slice(&integer::encode(&[0, 9]).unwrap());
1904 bytes.extend_from_slice(&encode(&[b"one".as_slice(), b"two".as_slice()]).unwrap());
1905 let error = decode(&bytes).unwrap_err();
1906 assert!(error.message().contains("shares 9 bytes"), "{error}");
1907 }
1908
1909 #[test]
1910 fn a_negative_prefix_is_an_error() {
1911 let mut bytes = vec![Kind::Front.tag()];
1912 put_u32(&mut bytes, 1);
1913 bytes.extend_from_slice(&integer::encode(&[-1]).unwrap());
1914 bytes.extend_from_slice(&encode(&[b"one".as_slice()]).unwrap());
1915 let error = decode(&bytes).unwrap_err();
1916 assert!(error.message().contains("negative shared prefix"), "{error}");
1917 }
1918
1919 #[test]
1920 fn a_negative_length_is_an_error() {
1921 let mut bytes = vec![Kind::Plain.tag()];
1922 put_u32(&mut bytes, 1);
1923 bytes.extend_from_slice(&integer::encode(&[-1]).unwrap());
1924 let error = decode(&bytes).unwrap_err();
1925 assert!(error.message().contains("negative string length"), "{error}");
1926 }
1927
1928 #[test]
1929 fn the_sample_is_spread_across_the_chunk_and_not_taken_from_the_front() {
1930 let mut values: Vec<Vec<u8>> = Vec::new();
1933 for index in 0..20_000 {
1934 let head = if index < 10_000 { "aaaaaaaaaaaaaaaa" } else { "zzzzzzzzzzzzzzzz" };
1935 values.push(format!("{head}/{index:08}").into_bytes());
1936 }
1937 let borrowed = borrow(&values);
1938 let sample = sample_of(&borrowed);
1939 let first_half = sample.iter().filter(|value| value.starts_with(b"aaaa")).count();
1940 let second_half = sample.len() - first_half;
1941 assert!(first_half > 0 && second_half > 0, "{first_half} and {second_half}");
1942 let bytes = round_trip(&values);
1943 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1944 assert!(ratio > 4.0, "{ratio:.2}x");
1945 }
1946}