1use rudb_common::{Error, Result};
65
66use crate::chooser::{Chooser, EXHAUSTIVE, Settled};
67use crate::fsst::SymbolTable;
68use crate::integer;
69use crate::lz;
70use crate::reader::Reader;
71
72const MAX_DEPTH: u8 = 2;
75
76const SHARE_DIVISOR: usize = 20;
80
81const LZ_FLOOR: usize = 4096;
87
88pub(crate) const SAMPLE_BYTES: usize = 64 * 1024;
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum Kind {
98 Constant = 0,
100 Plain = 1,
102 Fsst = 2,
104 Dict = 3,
106 Front = 4,
108 Lz = 5,
111}
112
113impl Kind {
114 fn tag(self) -> u8 {
115 self as u8
116 }
117
118 fn from_tag(tag: u8) -> Result<Self> {
119 match tag {
120 0 => Ok(Self::Constant),
121 1 => Ok(Self::Plain),
122 2 => Ok(Self::Fsst),
123 3 => Ok(Self::Dict),
124 4 => Ok(Self::Front),
125 5 => Ok(Self::Lz),
126 other => Err(Error::internal(format!("unknown string encoding tag {other}"))),
127 }
128 }
129
130 #[must_use]
132 pub fn name(self) -> &'static str {
133 match self {
134 Self::Constant => "CONSTANT",
135 Self::Plain => "PLAIN",
136 Self::Fsst => "FSST",
137 Self::Dict => "DICT",
138 Self::Front => "FRONT",
139 Self::Lz => "LZ",
140 }
141 }
142}
143
144pub fn encode(values: &[&[u8]]) -> Result<Vec<u8>> {
155 encode_with(values, &EXHAUSTIVE)
156}
157
158pub fn encode_with(values: &[&[u8]], chooser: &dyn Chooser) -> Result<Vec<u8>> {
168 encode_at(values, 0, chooser)
169}
170
171#[derive(Debug, Clone, Default, PartialEq, Eq)]
184pub struct Flat {
185 bytes: Vec<u8>,
186 ends: Vec<usize>,
190}
191
192impl Flat {
193 fn with_capacity(count: usize, bytes: usize) -> Self {
194 Self { bytes: Vec::with_capacity(bytes), ends: Vec::with_capacity(count) }
195 }
196
197 fn push(&mut self, value: &[u8]) {
198 self.bytes.extend_from_slice(value);
199 self.ends.push(self.bytes.len());
200 }
201
202 fn start(&self, index: usize) -> usize {
204 if index == 0 { 0 } else { self.ends[index - 1] }
205 }
206
207 #[must_use]
209 pub fn len(&self) -> usize {
210 self.ends.len()
211 }
212
213 #[must_use]
215 pub fn is_empty(&self) -> bool {
216 self.ends.is_empty()
217 }
218
219 #[must_use]
222 pub fn bytes(&self) -> &[u8] {
223 &self.bytes
224 }
225
226 #[must_use]
228 pub fn get(&self, index: usize) -> Option<&[u8]> {
229 let end = *self.ends.get(index)?;
230 self.bytes.get(self.start(index)..end)
231 }
232
233 pub fn iter(&self) -> impl Iterator<Item = &[u8]> {
235 let mut at = 0;
236 self.ends.iter().map(move |end| {
237 let value = self.bytes.get(at..*end).unwrap_or_default();
238 at = *end;
239 value
240 })
241 }
242
243 #[must_use]
245 pub fn into_bytes(self) -> Vec<u8> {
246 self.bytes
247 }
248
249 #[must_use]
256 pub fn into_parts(self) -> (Vec<u8>, Vec<usize>) {
257 (self.bytes, self.ends)
258 }
259
260 fn into_values(self) -> Vec<Vec<u8>> {
261 let mut values = Vec::with_capacity(self.len());
262 let mut at = 0;
263 for end in &self.ends {
264 values.push(self.bytes[at..*end].to_vec());
265 at = *end;
266 }
267 values
268 }
269}
270
271pub fn decode_flat(bytes: &[u8]) -> Result<Flat> {
277 let mut reader = Reader::new(bytes);
278 let flat = decode_chunk(&mut reader)?;
279 if reader.remaining() != 0 {
280 return Err(Error::internal(format!(
281 "{} bytes left over after decoding a string chunk",
282 reader.remaining()
283 )));
284 }
285 Ok(flat)
286}
287
288pub fn decode_flat_at(bytes: &[u8], positions: &[u32]) -> Result<Flat> {
300 if positions.windows(2).any(|pair| pair[0] >= pair[1]) {
301 return Err(Error::internal("the positions to decode do not rise"));
302 }
303 let mut reader = Reader::new(bytes);
304 let flat = if bytes.first() == Some(&Kind::Fsst.tag()) {
305 reader.u8()?;
306 let count = reader.u32()? as usize;
307 let runs = read_compressed(&mut reader, count)?;
308 let mut flat = Flat::with_capacity(positions.len(), runs.payload.len());
309 let mut at = 0;
310 let mut next = 0;
311 for &position in positions {
312 let position = position as usize;
313 if position >= count {
314 return Err(Error::internal(format!("value {position} is not in the chunk")));
315 }
316 at += runs.lengths[next..position].iter().sum::<usize>();
317 runs.run_into(position, &mut at, &mut flat.bytes)?;
318 flat.ends.push(flat.bytes.len());
319 next = position + 1;
320 }
321 flat
322 } else {
323 let whole = decode_chunk(&mut reader)?;
324 let mut flat = Flat::with_capacity(positions.len(), 0);
325 for &position in positions {
326 let value = whole
327 .get(position as usize)
328 .ok_or_else(|| Error::internal(format!("value {position} is not in the chunk")))?;
329 flat.push(value);
330 }
331 flat
332 };
333 if reader.remaining() != 0 {
334 return Err(Error::internal(format!(
335 "{} bytes left over after decoding a string chunk",
336 reader.remaining()
337 )));
338 }
339 Ok(flat)
340}
341
342pub fn decode_prefix(bytes: &[u8]) -> Result<(Vec<Vec<u8>>, usize)> {
351 let mut reader = Reader::new(bytes);
352 let values = decode_chunk(&mut reader)?;
353 Ok((values.into_values(), reader.used()))
354}
355
356pub fn describe_prefix(bytes: &[u8]) -> Result<(String, usize)> {
362 let mut reader = Reader::new(bytes);
363 let text = describe_chunk(&mut reader)?;
364 Ok((text, reader.used()))
365}
366
367pub fn decode(bytes: &[u8]) -> Result<Vec<Vec<u8>>> {
373 Ok(decode_flat(bytes)?.into_values())
374}
375
376pub fn candidate_sizes(values: &[&[u8]]) -> Result<Vec<(Kind, usize)>> {
383 let mut sizes = Vec::new();
384 for kind in candidates(values, 0) {
385 if let Some(bytes) = encode_as(kind, values, 0, &EXHAUSTIVE)? {
386 sizes.push((kind, bytes.len()));
387 }
388 }
389 Ok(sizes)
390}
391
392#[must_use]
399pub fn offered(values: &[&[u8]]) -> Vec<Kind> {
400 candidates(values, 0)
401}
402
403pub fn encode_only(kind: Kind, values: &[&[u8]]) -> Result<Option<Vec<u8>>> {
415 encode_as(kind, values, 0, &EXHAUSTIVE)
416}
417
418pub(crate) fn size_as(kind: Kind, values: &[&[u8]], depth: u8) -> Result<Option<usize>> {
423 Ok(encode_as(kind, values, depth, &EXHAUSTIVE)?.map(|bytes| bytes.len()))
424}
425
426pub fn describe(bytes: &[u8]) -> Result<String> {
432 let mut reader = Reader::new(bytes);
433 describe_chunk(&mut reader)
434}
435
436#[must_use]
448pub fn with_symbols(shape: Settled, blocks: &[Vec<&[u8]>]) -> Settled {
449 let kinds = shape.strings();
450 let Some(depth) =
451 (0..=kinds.len()).find(|&at| matches!(kinds.get(at), Some(Kind::Fsst) | None))
452 else {
453 return shape;
454 };
455 let leads =
456 kinds[..depth].iter().all(|kind| matches!(kind, Kind::Front | Kind::Lz | Kind::Dict));
457 if !leads || depth > usize::from(MAX_DEPTH) {
458 return shape;
459 }
460 let mut reached: Vec<Vec<u8>> = Vec::new();
461 for block in blocks {
462 let mut values: Vec<Vec<u8>> = block.iter().map(|value| value.to_vec()).collect();
463 for kind in &kinds[..depth] {
464 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
465 values = match kind {
466 Kind::Front => front_code(&refs).1.into_iter().map(<[u8]>::to_vec).collect(),
467 Kind::Dict => dictionary_of(&refs).0.into_iter().map(<[u8]>::to_vec).collect(),
468 _ => {
469 let joined = refs.concat();
470 lz::tokens_of(&joined).literals.into_iter().map(<[u8]>::to_vec).collect()
471 }
472 };
473 }
474 reached.extend(values);
475 }
476 let refs: Vec<&[u8]> = reached.iter().map(Vec::as_slice).collect();
477 let table = SymbolTable::train(&sample_of(&refs));
478 if table.is_empty() {
479 return shape;
480 }
481 shape.with_symbols(depth as u8, table)
482}
483
484fn encode_at(values: &[&[u8]], depth: u8, chooser: &dyn Chooser) -> Result<Vec<u8>> {
485 let offered = candidates(values, depth);
486 let mut best: Option<Vec<u8>> = None;
487 for kind in chooser.narrow_strings(values, &offered, depth) {
488 let Some(bytes) = encode_as(kind, values, depth, chooser)? else {
489 continue;
490 };
491 if best.as_ref().is_none_or(|current| bytes.len() < current.len()) {
492 best = Some(bytes);
493 }
494 }
495 best.ok_or_else(|| Error::internal("no string encoding applied to the chunk"))
496}
497
498fn candidates(values: &[&[u8]], depth: u8) -> Vec<Kind> {
499 let mut kinds = vec![Kind::Plain];
500 if values.is_empty() {
501 return kinds;
502 }
503 if values.iter().all(|value| *value == values[0]) {
504 return vec![Kind::Constant];
505 }
506 kinds.push(Kind::Fsst);
507 if depth < MAX_DEPTH && has_duplicates(values) {
508 kinds.push(Kind::Dict);
509 }
510 if depth < MAX_DEPTH && sharing_of(values) >= total_len(values) / SHARE_DIVISOR {
511 kinds.push(Kind::Front);
512 }
513 if depth < MAX_DEPTH && total_len(values) >= LZ_FLOOR {
514 kinds.push(Kind::Lz);
515 }
516 kinds
517}
518
519fn sharing_of(values: &[&[u8]]) -> usize {
526 let mut shared = 0;
527 for pair in values.windows(2) {
528 shared += shared_prefix(pair[0], pair[1]);
529 }
530 shared
531}
532
533pub(crate) fn front_code<'a>(values: &[&'a [u8]]) -> (Vec<i64>, Vec<&'a [u8]>) {
539 let mut prefixes = Vec::with_capacity(values.len());
540 let mut suffixes: Vec<&'a [u8]> = Vec::with_capacity(values.len());
541 let mut previous: &[u8] = b"";
542 for value in values {
543 let value: &'a [u8] = value;
544 let shared = shared_prefix(previous, value);
545 prefixes.push(shared as i64);
546 suffixes.push(&value[shared..]);
547 previous = value;
548 }
549 (prefixes, suffixes)
550}
551
552pub(crate) fn front_decode(prefixes: &[i64], suffixes: Vec<Vec<u8>>) -> Result<Vec<Vec<u8>>> {
559 let mut values: Vec<Vec<u8>> = Vec::with_capacity(suffixes.len());
560 for (index, suffix) in suffixes.into_iter().enumerate() {
561 let shared = usize::try_from(prefixes[index])
562 .map_err(|_| Error::internal("a negative shared prefix length"))?;
563 let previous: &[u8] = if index == 0 { b"" } else { &values[index - 1] };
564 if shared > previous.len() {
565 return Err(Error::internal(format!(
566 "a value shares {shared} bytes with a value {} bytes long",
567 previous.len()
568 )));
569 }
570 let mut value = Vec::with_capacity(shared + suffix.len());
571 value.extend_from_slice(&previous[..shared]);
572 value.extend_from_slice(&suffix);
573 values.push(value);
574 }
575 Ok(values)
576}
577
578fn shared_prefix(previous: &[u8], value: &[u8]) -> usize {
579 let limit = previous.len().min(value.len());
580 let mut shared = 0;
581 while shared < limit && previous[shared] == value[shared] {
582 shared += 1;
583 }
584 shared
585}
586
587fn total_len(values: &[&[u8]]) -> usize {
588 values.iter().map(|value| value.len()).sum()
589}
590
591fn encode_as(
592 kind: Kind,
593 values: &[&[u8]],
594 depth: u8,
595 chooser: &dyn Chooser,
596) -> Result<Option<Vec<u8>>> {
597 let mut out = vec![kind.tag()];
598 put_u32(&mut out, u32::try_from(values.len()).map_err(|_| too_long(values.len()))?);
599 match kind {
600 Kind::Constant => {
601 let Some(first) = values.first() else {
602 return Ok(None);
603 };
604 if values.iter().any(|value| value != first) {
605 return Ok(None);
606 }
607 put_u32(&mut out, u32::try_from(first.len()).map_err(|_| too_long(first.len()))?);
608 out.extend_from_slice(first);
609 }
610 Kind::Plain => {
611 out.extend_from_slice(&encode_lengths(values, chooser)?);
612 for value in values {
613 out.extend_from_slice(value);
614 }
615 }
616 Kind::Fsst => {
617 let trained;
618 let table = match chooser.symbols(depth) {
619 Some(table) => table,
620 None => {
621 trained = SymbolTable::train(&sample_of(values));
622 &trained
623 }
624 };
625 if table.is_empty() {
626 return Ok(None);
627 }
628 let mut compressed = Vec::new();
629 let mut lengths = Vec::with_capacity(values.len());
630 for value in values {
631 let before = compressed.len();
632 table.compress(value, &mut compressed);
633 lengths.push((compressed.len() - before) as i64);
634 }
635 table.serialize(&mut out);
636 out.extend_from_slice(&integer::encode_with(&lengths, chooser)?);
637 out.extend_from_slice(&compressed);
638 }
639 Kind::Dict => {
640 let (entries, codes) = dictionary_of(values);
641 if entries.is_empty() {
642 return Ok(None);
643 }
644 out.extend_from_slice(&encode_at(&entries, depth + 1, chooser)?);
645 out.extend_from_slice(&integer::encode_with(&codes, chooser)?);
646 }
647 Kind::Front => {
648 let (prefixes, suffixes) = front_code(values);
649 out.extend_from_slice(&integer::encode_with(&prefixes, chooser)?);
650 out.extend_from_slice(&encode_at(&suffixes, depth + 1, chooser)?);
651 }
652 Kind::Lz => {
653 let mut joined = Vec::with_capacity(total_len(values));
654 let mut sizes = Vec::with_capacity(values.len());
655 for value in values {
656 joined.extend_from_slice(value);
657 sizes.push(value.len() as i64);
658 }
659 let tokens = lz::tokens_of(&joined);
660 out.extend_from_slice(&integer::encode_with(&sizes, chooser)?);
661 out.extend_from_slice(&integer::encode_with(&tokens.lengths, chooser)?);
662 out.extend_from_slice(&integer::encode_with(&tokens.offsets, chooser)?);
663 out.extend_from_slice(&encode_at(&tokens.literals, depth + 1, chooser)?);
664 }
665 }
666 Ok(Some(out))
667}
668
669fn decode_chunk(reader: &mut Reader<'_>) -> Result<Flat> {
670 let kind = Kind::from_tag(reader.u8()?)?;
671 let count = reader.u32()? as usize;
672 match kind {
673 Kind::Constant => {
674 let len = reader.u32()? as usize;
675 let value = reader.bytes(len)?;
676 let mut flat = Flat::with_capacity(count, len.saturating_mul(count));
677 for _ in 0..count {
678 flat.push(value);
679 }
680 Ok(flat)
681 }
682 Kind::Plain => {
683 let lengths = decode_lengths(reader, count)?;
684 let total = sum_of(&lengths)?;
687 let payload = reader.bytes(total)?;
688 let mut flat = Flat::with_capacity(count, total);
689 flat.bytes.extend_from_slice(payload);
690 let mut at = 0;
691 for length in lengths {
692 at += length;
693 flat.ends.push(at);
694 }
695 Ok(flat)
696 }
697 Kind::Fsst => {
698 let runs = read_compressed(reader, count)?;
699 let mut flat = Flat::with_capacity(count, runs.payload.len());
700 let mut at = 0;
701 for index in 0..count {
702 runs.run_into(index, &mut at, &mut flat.bytes)?;
703 flat.ends.push(flat.bytes.len());
704 }
705 Ok(flat)
706 }
707 Kind::Dict => {
708 let dictionary = decode_chunk(reader)?;
709 let codes = decode_integers(reader)?;
710 if codes.len() != count {
711 return Err(Error::internal(format!(
712 "a dictionary chunk says it holds {count} values and has {} codes",
713 codes.len()
714 )));
715 }
716 let mut flat = Flat::with_capacity(count, dictionary.bytes.len());
717 for code in codes {
718 let entry =
719 usize::try_from(code).ok().and_then(|index| dictionary.get(index)).ok_or_else(
720 || Error::internal(format!("code {code} is not in the dictionary")),
721 )?;
722 flat.push(entry);
723 }
724 Ok(flat)
725 }
726 Kind::Front => {
727 let prefixes = decode_integers(reader)?;
728 let suffixes = decode_chunk(reader)?;
729 if prefixes.len() != count || suffixes.len() != count {
730 return Err(Error::internal(format!(
731 "a front coded chunk says it holds {count} values and has {} prefixes and {} suffixes",
732 prefixes.len(),
733 suffixes.len()
734 )));
735 }
736 let mut room = 0usize;
743 let mut previous = 0usize;
744 for (index, prefix) in prefixes.iter().enumerate() {
745 let shared = usize::try_from(*prefix)
746 .map_err(|_| Error::internal("a negative shared prefix length"))?;
747 if shared > previous {
748 return Err(Error::internal(format!(
749 "a value shares {shared} bytes with a value {previous} bytes long"
750 )));
751 }
752 previous = shared + suffixes.get(index).map_or(0, <[u8]>::len);
753 room = room
754 .checked_add(previous)
755 .ok_or_else(|| Error::internal("a string chunk longer than memory"))?;
756 }
757 let mut flat = Flat::with_capacity(count, room);
758 for (index, &prefix) in prefixes.iter().enumerate() {
759 let shared = prefix as usize;
760 let from = if index == 0 { 0 } else { flat.start(index - 1) };
761 flat.bytes.extend_from_within(from..from + shared);
762 flat.bytes.extend_from_slice(suffixes.get(index).expect("in range"));
763 flat.ends.push(flat.bytes.len());
764 }
765 Ok(flat)
766 }
767 Kind::Lz => {
768 let sizes = decode_integers(reader)?;
769 let lengths = decode_integers(reader)?;
770 let offsets = decode_integers(reader)?;
771 if sizes.len() != count {
772 return Err(Error::internal(format!(
773 "a matched chunk says it holds {count} values and has {} lengths",
774 sizes.len()
775 )));
776 }
777 let mut total = 0usize;
778 let mut widths = Vec::with_capacity(count);
779 for size in sizes {
780 let width = usize::try_from(size)
781 .map_err(|_| Error::internal("a negative string length"))?;
782 total = total
783 .checked_add(width)
784 .ok_or_else(|| Error::internal("a string chunk longer than memory"))?;
785 widths.push(width);
786 }
787 let mut flat = Flat::with_capacity(count, total.saturating_add(REPLAY_SLACK));
792 replay_literals(reader, &lengths, &offsets, total, &mut flat.bytes)?;
793 if flat.bytes.len() != total {
794 return Err(Error::internal(format!(
795 "a matched chunk rebuilt {} bytes where its lengths add up to {total}",
796 flat.bytes.len()
797 )));
798 }
799 let mut at = 0;
800 for width in widths {
801 at += width;
802 flat.ends.push(at);
803 }
804 Ok(flat)
805 }
806 }
807}
808
809struct Compressed<'a> {
815 table: SymbolTable,
817 lengths: Vec<usize>,
819 payload: &'a [u8],
821}
822
823impl Compressed<'_> {
824 fn run_into(&self, index: usize, at: &mut usize, out: &mut Vec<u8>) -> Result<()> {
833 self.table.decompress(self.run(index, at)?, out)
834 }
835
836 fn run(&self, index: usize, at: &mut usize) -> Result<&[u8]> {
839 let length = *self
840 .lengths
841 .get(index)
842 .ok_or_else(|| Error::internal(format!("run {index} is not in the chunk")))?;
843 let end = at
844 .checked_add(length)
845 .ok_or_else(|| Error::internal("a compressed chunk longer than memory"))?;
846 let run = self
847 .payload
848 .get(*at..end)
849 .ok_or_else(|| Error::internal("a compressed run is past the end of its chunk"))?;
850 *at = end;
851 Ok(run)
852 }
853}
854
855fn read_compressed<'a>(reader: &mut Reader<'a>, count: usize) -> Result<Compressed<'a>> {
864 let (table, used) = SymbolTable::deserialize(reader.rest())?;
865 reader.skip(used)?;
866 let lengths = decode_lengths(reader, count)?;
867 let compressed_len = sum_of(&lengths)?;
870 if compressed_len > reader.remaining() {
871 return Err(Error::internal(format!(
872 "a compressed chunk says it holds {compressed_len} bytes and has {}",
873 reader.remaining()
874 )));
875 }
876 let payload = reader.bytes(compressed_len)?;
877 Ok(Compressed { table, lengths, payload })
878}
879
880fn replay_literals(
893 reader: &mut Reader<'_>,
894 lengths: &[i64],
895 offsets: &[i64],
896 total: usize,
897 out: &mut Vec<u8>,
898) -> Result<()> {
899 if reader.rest().first() == Some(&Kind::Fsst.tag()) {
900 reader.u8()?;
901 let runs = reader.u32()? as usize;
902 let compressed = read_compressed(reader, runs)?;
903 return replay_in_place(&compressed, lengths, offsets, total, out);
904 }
905 let literals = decode_chunk(reader)?;
906 lz::rebuild_into(&literals, lengths, offsets, out)
907}
908
909const REPLAY_SLACK: usize = 16;
915
916fn replay_in_place(
928 compressed: &Compressed<'_>,
929 lengths: &[i64],
930 offsets: &[i64],
931 total: usize,
932 out: &mut Vec<u8>,
933) -> Result<()> {
934 let runs = compressed.lengths.len();
935 if runs != lengths.len() || lengths.len() != offsets.len() {
936 return Err(Error::internal(format!(
937 "a matched chunk has {runs} literal runs, {} lengths and {} offsets",
938 lengths.len(),
939 offsets.len()
940 )));
941 }
942 let base = out.len();
943 let room = total
944 .checked_add(REPLAY_SLACK)
945 .ok_or_else(|| Error::internal("a string chunk longer than memory"))?;
946 out.resize(base + room, 0);
947 let mut payload = compressed.payload;
948 let mut at = base;
949 for ((&run, &length), &offset) in compressed.lengths.iter().zip(lengths).zip(offsets) {
950 let Some((codes, rest)) = payload.split_at_checked(run) else {
951 return Err(Error::internal("a compressed run is past the end of its chunk"));
952 };
953 payload = rest;
954 at = compressed.table.decompress_at(codes, out, at)?;
955 let length =
956 usize::try_from(length).map_err(|_| Error::internal("a negative copy length"))?;
957 if length == 0 {
958 continue;
959 }
960 let offset =
961 usize::try_from(offset).map_err(|_| Error::internal("a negative copy offset"))?;
962 at = copy_back(out, base, at, offset, length)?;
963 }
964 if at > base + total {
965 return Err(Error::internal(format!(
966 "a matched chunk rebuilt {} bytes where its lengths add up to {total}",
967 at - base
968 )));
969 }
970 out.truncate(at);
971 Ok(())
972}
973
974fn copy_back(
982 out: &mut [u8],
983 base: usize,
984 at: usize,
985 offset: usize,
986 length: usize,
987) -> Result<usize> {
988 if offset == 0 || offset > at - base {
989 return Err(Error::internal(format!(
990 "a copy reaches {offset} bytes back into {} bytes of output",
991 at - base
992 )));
993 }
994 let end = at
995 .checked_add(length)
996 .filter(|&end| end <= out.len())
997 .ok_or_else(|| Error::internal("a matched chunk rebuilds more than its lengths say"))?;
998 let from = at - offset;
999 let wide = end + REPLAY_SLACK <= out.len();
1000 if wide && offset >= 16 {
1001 let mut step = 0;
1002 while step < length {
1003 out.copy_within(from + step..from + step + 16, at + step);
1004 step += 16;
1005 }
1006 } else if wide && offset >= 8 {
1007 let mut step = 0;
1008 while step < length {
1009 out.copy_within(from + step..from + step + 8, at + step);
1010 step += 8;
1011 }
1012 } else {
1013 for step in 0..length {
1014 out[at + step] = out[from + step];
1015 }
1016 }
1017 Ok(end)
1018}
1019
1020fn describe_chunk(reader: &mut Reader<'_>) -> Result<String> {
1021 let kind = Kind::from_tag(reader.u8()?)?;
1022 let count = reader.u32()? as usize;
1023 Ok(match kind {
1024 Kind::Constant => {
1025 let len = reader.u32()? as usize;
1026 reader.bytes(len)?;
1027 "CONSTANT".to_string()
1028 }
1029 Kind::Plain => {
1030 let (shape, lengths) = describe_lengths(reader, count)?;
1031 reader.skip(lengths.iter().sum())?;
1032 format!("PLAIN({shape})")
1033 }
1034 Kind::Fsst => {
1035 let (table, used) = SymbolTable::deserialize(reader.rest())?;
1036 reader.skip(used)?;
1037 let (shape, lengths) = describe_lengths(reader, count)?;
1038 reader.skip(lengths.iter().sum())?;
1039 format!("FSST[{}]({shape})", table.len())
1040 }
1041 Kind::Dict => {
1042 let entries = describe_chunk(reader)?;
1043 let codes = describe_integers(reader)?;
1044 format!("DICT({entries}, {codes})")
1045 }
1046 Kind::Front => {
1047 let prefixes = describe_integers(reader)?;
1048 let suffixes = describe_chunk(reader)?;
1049 format!("FRONT({prefixes}, {suffixes})")
1050 }
1051 Kind::Lz => {
1052 let sizes = describe_integers(reader)?;
1053 let lengths = describe_integers(reader)?;
1054 let offsets = describe_integers(reader)?;
1055 let literals = describe_chunk(reader)?;
1056 format!("LZ({sizes}, {lengths}, {offsets}, {literals})")
1057 }
1058 })
1059}
1060
1061fn describe_lengths(reader: &mut Reader<'_>, count: usize) -> Result<(String, Vec<usize>)> {
1065 let (shape, _) = integer::describe_prefix(reader.rest())?;
1066 let lengths = decode_lengths(reader, count)?;
1067 Ok((shape, lengths))
1068}
1069
1070fn encode_lengths(values: &[&[u8]], chooser: &dyn Chooser) -> Result<Vec<u8>> {
1071 let lengths: Vec<i64> = values.iter().map(|value| value.len() as i64).collect();
1072 integer::encode_with(&lengths, chooser)
1073}
1074
1075fn decode_lengths(reader: &mut Reader<'_>, count: usize) -> Result<Vec<usize>> {
1076 let lengths = decode_integers(reader)?;
1077 if lengths.len() != count {
1078 return Err(Error::internal(format!(
1079 "a string chunk says it holds {count} values and has {} lengths",
1080 lengths.len()
1081 )));
1082 }
1083 lengths
1084 .into_iter()
1085 .map(|length| {
1086 usize::try_from(length).map_err(|_| Error::internal("a negative string length"))
1087 })
1088 .collect()
1089}
1090
1091fn sum_of(lengths: &[usize]) -> Result<usize> {
1097 lengths
1098 .iter()
1099 .try_fold(0usize, |total, length| total.checked_add(*length))
1100 .ok_or_else(|| Error::internal("a string chunk longer than memory"))
1101}
1102
1103fn decode_integers(reader: &mut Reader<'_>) -> Result<Vec<i64>> {
1107 let (values, used) = integer::decode_prefix(reader.rest())?;
1108 reader.skip(used)?;
1109 Ok(values)
1110}
1111
1112fn describe_integers(reader: &mut Reader<'_>) -> Result<String> {
1113 let (text, used) = integer::describe_prefix(reader.rest())?;
1114 reader.skip(used)?;
1115 Ok(text)
1116}
1117
1118pub(crate) fn sample_of<'a>(values: &[&'a [u8]]) -> Vec<&'a [u8]> {
1137 sample_bytes_of(values, SAMPLE_BYTES)
1138}
1139
1140pub(crate) fn sample_bytes_of<'a>(values: &[&'a [u8]], budget: usize) -> Vec<&'a [u8]> {
1143 let budget = budget.max(1);
1144 let total: usize = values.iter().map(|value| value.len()).sum();
1145 if total <= budget {
1146 return values.to_vec();
1147 }
1148 let stride = total.div_ceil(budget).max(1);
1149 let span = (stride * 2 - 1).max(1) as u64;
1150 let mut state = 0x2545_f491_4f6c_dd1du64;
1151 let mut sample = Vec::with_capacity(values.len() / stride + 1);
1152 let mut at = 0usize;
1153 while at < values.len() {
1154 sample.push(values[at]);
1155 state ^= state << 13;
1156 state ^= state >> 7;
1157 state ^= state << 17;
1158 at += 1 + (state % span) as usize;
1159 }
1160 sample
1161}
1162
1163fn dictionary_of<'a>(values: &[&'a [u8]]) -> (Vec<&'a [u8]>, Vec<i64>) {
1175 let mut order: Vec<u32> = (0..values.len() as u32).collect();
1176 order.sort_unstable_by(|left, right| values[*left as usize].cmp(values[*right as usize]));
1177 let mut entries: Vec<&'a [u8]> = Vec::new();
1178 let mut codes = vec![0i64; values.len()];
1179 for &index in &order {
1180 let value = values[index as usize];
1181 if entries.last() != Some(&value) {
1182 entries.push(value);
1183 }
1184 codes[index as usize] = (entries.len() - 1) as i64;
1185 }
1186 (entries, codes)
1187}
1188
1189fn has_duplicates(values: &[&[u8]]) -> bool {
1199 let Some(slots) = values.len().checked_mul(2).map(usize::next_power_of_two) else {
1200 return false;
1201 };
1202 let mask = slots - 1;
1203 let mut table = vec![u32::MAX; slots];
1204 for (index, value) in values.iter().enumerate() {
1205 let mut at = hash_of(value) as usize & mask;
1206 loop {
1207 let held = table[at];
1208 if held == u32::MAX {
1209 table[at] = index as u32;
1210 break;
1211 }
1212 if values[held as usize] == *value {
1213 return true;
1214 }
1215 at = (at + 1) & mask;
1216 }
1217 }
1218 false
1219}
1220
1221fn hash_of(value: &[u8]) -> u64 {
1228 let mut hash = 0xcbf2_9ce4_8422_2325_u64;
1229 let mut chunks = value.chunks_exact(8);
1230 for chunk in &mut chunks {
1231 let word = u64::from_le_bytes(chunk.try_into().expect("chunks_exact(8) gives eight bytes"));
1232 hash = (hash ^ word).wrapping_mul(0x1_0000_01b3);
1233 }
1234 for byte in chunks.remainder() {
1235 hash = (hash ^ u64::from(*byte)).wrapping_mul(0x1_0000_01b3);
1236 }
1237 (hash ^ (value.len() as u64)).wrapping_mul(0x1_0000_01b3)
1238}
1239
1240fn too_long(len: usize) -> Error {
1241 Error::internal(format!("a string chunk of {len} is longer than the format allows"))
1242}
1243
1244fn put_u32(out: &mut Vec<u8>, value: u32) {
1245 out.extend_from_slice(&value.to_le_bytes());
1246}
1247
1248#[cfg(test)]
1249mod tests {
1250 use super::*;
1251
1252 fn urls(count: usize) -> Vec<Vec<u8>> {
1253 let hosts = ["www.example.com", "shop.example.com", "news.other.example.org"];
1254 let paths = ["/index.html", "/catalog/item", "/search", "/user/profile/settings"];
1255 (0..count)
1256 .map(|index| {
1257 let host = hosts[index % hosts.len()];
1258 let path = paths[(index / 3) % paths.len()];
1259 format!("http://{host}{path}?session={}&ref=google", index * 7).into_bytes()
1260 })
1261 .collect()
1262 }
1263
1264 #[test]
1267 fn the_values_at_some_positions_are_the_ones_a_whole_decode_has_there() {
1268 let values = urls(1000);
1269 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1270 let positions = [0_u32, 3, 4, 500, 998, 999];
1271 let wanted: Vec<Vec<u8>> =
1272 positions.iter().map(|&at| values[at as usize].clone()).collect();
1273 for kind in offered(&refs) {
1274 let Some(encoded) = encode_only(kind, &refs).expect("encoded") else { continue };
1275 let flat = decode_flat_at(&encoded, &positions).expect("decoded");
1276 assert_eq!(flat.into_values(), wanted, "{kind:?}");
1277 let none = decode_flat_at(&encoded, &[]).expect("decoded");
1278 assert!(none.is_empty(), "{kind:?}");
1279 assert!(decode_flat_at(&encoded, &[4, 3]).is_err(), "{kind:?}");
1280 assert!(decode_flat_at(&encoded, &[1000]).is_err(), "{kind:?}");
1281 }
1282 let fsst = encode_only(Kind::Fsst, &refs).expect("encoded").expect("compressible");
1283 assert_eq!(decode_flat_at(&fsst, &positions).expect("decoded").into_values(), wanted);
1284 }
1285
1286 fn front_lz() -> Settled {
1287 Settled::new(vec![Kind::Front, Kind::Lz], vec![integer::Kind::Packed])
1288 }
1289
1290 #[test]
1294 fn a_block_compressed_against_the_column_table_reads_back() {
1295 let values = urls(4096);
1296 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1297 let blocks: Vec<Vec<&[u8]>> = refs.chunks(1024).take(2).map(<[&[u8]]>::to_vec).collect();
1298 let shape = with_symbols(front_lz(), &blocks);
1299 assert!(shape.symbols(2).is_some(), "FRONT then LZ leaves FSST the third level");
1300 assert!(shape.symbols(1).is_none(), "and only that one");
1301 for block in refs.chunks(1024) {
1302 let encoded = encode_with(block, &shape).expect("encoded");
1303 assert_eq!(decode(&encoded).expect("decoded"), block.to_vec());
1304 }
1305 }
1306
1307 #[test]
1309 fn a_shape_ending_in_plain_gets_no_table() {
1310 let values = urls(1024);
1311 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1312 let plain = Settled::new(vec![Kind::Lz, Kind::Plain], vec![integer::Kind::Packed]);
1313 let shape = with_symbols(plain, std::slice::from_ref(&refs));
1314 assert!((0..=MAX_DEPTH).all(|depth| shape.symbols(depth).is_none()));
1315 let fsst = Settled::new(vec![Kind::Fsst], vec![integer::Kind::Packed]);
1316 assert!(with_symbols(fsst, &[refs]).symbols(0).is_some());
1317 }
1318
1319 fn keyed(values: Vec<Vec<u8>>) -> Vec<Vec<u8>> {
1323 values
1324 .into_iter()
1325 .enumerate()
1326 .map(|(index, value)| {
1327 let key = (index as u64).wrapping_mul(0x9e37_79b9_7f4a_7c15) % 1_000_000_007;
1328 let mut out = format!("{key:010}/").into_bytes();
1329 out.extend_from_slice(&value);
1330 out
1331 })
1332 .collect()
1333 }
1334
1335 fn borrow(values: &[Vec<u8>]) -> Vec<&[u8]> {
1336 values.iter().map(Vec::as_slice).collect()
1337 }
1338
1339 fn round_trip(values: &[Vec<u8>]) -> Vec<u8> {
1340 let borrowed = borrow(values);
1341 let bytes = encode(&borrowed).unwrap();
1342 let back = decode(&bytes).unwrap();
1343 assert_eq!(back, values, "{}", describe(&bytes).unwrap());
1344 check_flat(&bytes, values);
1345 bytes
1346 }
1347
1348 fn check_flat(bytes: &[u8], values: &[Vec<u8>]) {
1351 let flat = decode_flat(bytes).unwrap();
1352 let shape = describe(bytes).unwrap();
1353 assert_eq!(flat.len(), values.len(), "{shape}");
1354 assert_eq!(flat.iter().collect::<Vec<_>>(), borrow(values), "{shape}");
1355 assert_eq!(flat.bytes(), values.concat(), "{shape}");
1356 assert_eq!(flat.get(values.len()), None, "{shape}");
1357 }
1358
1359 fn kind_of(bytes: &[u8]) -> Kind {
1360 Kind::from_tag(bytes[0]).unwrap()
1361 }
1362
1363 #[test]
1364 fn every_shape_decodes_flat_to_what_it_decodes_split() {
1365 let columns =
1369 [urls(600), keyed(urls(600)), vec![b"same".to_vec(); 400], vec![Vec::new(); 7]];
1370 for values in &columns {
1371 let borrowed = borrow(values);
1372 for kind in offered(&borrowed) {
1373 let Some(bytes) = encode_only(kind, &borrowed).unwrap() else {
1374 continue;
1375 };
1376 assert_eq!(decode(&bytes).unwrap(), *values, "{}", kind.name());
1377 let flat = decode_flat(&bytes).unwrap();
1378 assert_eq!(flat.iter().collect::<Vec<_>>(), borrowed, "{}", kind.name());
1379 assert_eq!(flat.bytes(), values.concat(), "{}", kind.name());
1380 }
1381 }
1382 }
1383
1384 #[test]
1385 fn a_front_coded_chunk_that_shares_more_than_it_has_is_an_error() {
1386 let suffixes: [&[u8]; 2] = [b"abc", b"x"];
1390 let mut bytes = vec![Kind::Front.tag()];
1391 put_u32(&mut bytes, 2);
1392 bytes.extend_from_slice(&integer::encode(&[0, 9]).unwrap());
1393 bytes.extend_from_slice(&encode_only(Kind::Plain, &suffixes).unwrap().unwrap());
1394 let error = decode_flat(&bytes).expect_err("a nine byte prefix of a three byte value");
1395 assert_eq!(error.message(), "a value shares 9 bytes with a value 3 bytes long");
1396 assert_eq!(decode(&bytes).unwrap_err().message(), error.message());
1397 }
1398
1399 #[test]
1400 fn the_dictionary_is_sorted_and_the_codes_point_back_at_the_values() {
1401 let values = vec![
1404 b"pear".to_vec(),
1405 b"apple".to_vec(),
1406 b"pear".to_vec(),
1407 b"cherry".to_vec(),
1408 b"apple".to_vec(),
1409 ];
1410 let borrowed = borrow(&values);
1411 let (entries, codes) = dictionary_of(&borrowed);
1412 assert_eq!(entries, vec![b"apple".as_slice(), b"cherry".as_slice(), b"pear".as_slice()]);
1413 assert_eq!(codes, vec![2, 0, 2, 1, 0]);
1414 for (code, value) in codes.iter().zip(&borrowed) {
1415 assert_eq!(entries[*code as usize], *value);
1416 }
1417 }
1418
1419 #[test]
1420 fn a_column_with_nothing_repeated_has_no_duplicates_and_one_with_anything_does() {
1421 let distinct: Vec<Vec<u8>> =
1422 (0..5000).map(|index| format!("value-{index}").into_bytes()).collect();
1423 assert!(!has_duplicates(&borrow(&distinct)));
1424
1425 let mut repeated = distinct.clone();
1427 repeated.push(b"value-0".to_vec());
1428 assert!(has_duplicates(&borrow(&repeated)));
1429
1430 assert!(!has_duplicates(&borrow(&Vec::new())));
1431 assert!(!has_duplicates(&borrow(&[b"one".to_vec()])));
1432 assert!(has_duplicates(&borrow(&vec![b"same".to_vec(); 2])));
1433 }
1434
1435 #[test]
1436 fn long_values_that_differ_only_at_the_end_are_not_confused_for_each_other() {
1437 let stem = "http://www.example.com/a/very/long/path/that/goes/on?session=";
1440 let values: Vec<Vec<u8>> =
1441 (0..2000).map(|index| format!("{stem}{index}").into_bytes()).collect();
1442 assert!(!has_duplicates(&borrow(&values)));
1443 let (entries, codes) = dictionary_of(&borrow(&values));
1444 assert_eq!(entries.len(), values.len());
1445 assert_eq!(codes.len(), values.len());
1446 }
1447
1448 #[test]
1449 fn what_the_chooser_returns_is_the_smallest_of_what_it_was_offered() {
1450 for values in [urls(400), keyed(urls(400)), vec![b"same".to_vec(); 50], Vec::new()] {
1456 let borrowed = borrow(&values);
1457 let chosen = encode(&borrowed).unwrap();
1458 let mut smallest: Option<Vec<u8>> = None;
1459 for kind in offered(&borrowed) {
1460 let Some(bytes) = encode_only(kind, &borrowed).unwrap() else {
1461 continue;
1462 };
1463 if smallest.as_ref().is_none_or(|best| bytes.len() < best.len()) {
1464 smallest = Some(bytes);
1465 }
1466 }
1467 assert_eq!(smallest.as_deref(), Some(chosen.as_slice()), "{}", values.len());
1468 }
1469 }
1470
1471 fn raw_size(values: &[Vec<u8>]) -> usize {
1472 values.iter().map(Vec::len).sum::<usize>() + values.len() * 4
1473 }
1474
1475 #[test]
1476 fn a_matched_chunk_replays_literals_whether_or_not_they_are_compressed() {
1477 let compressed = describe(&round_trip(&keyed(urls(20_000)))).unwrap();
1483 assert!(compressed.starts_with("LZ(") && compressed.contains(", FSST["), "{compressed}");
1484
1485 let buffered = describe(&round_trip(&keyed(urls(300)))).unwrap();
1486 assert!(buffered.starts_with("LZ(") && buffered.contains(", PLAIN("), "{buffered}");
1487 }
1488
1489 #[test]
1490 fn a_copy_back_writes_what_a_byte_at_a_time_copy_writes_at_every_distance() {
1491 let seed: Vec<u8> = (0..40u8).map(|byte| byte.wrapping_mul(37).wrapping_add(11)).collect();
1495 for offset in 1..=seed.len() {
1496 for length in 1..=50 {
1497 let mut wanted = seed.clone();
1498 for _ in 0..length {
1499 wanted.push(wanted[wanted.len() - offset]);
1500 }
1501 let mut out = seed.clone();
1502 out.resize(seed.len() + length + REPLAY_SLACK, 0);
1503 let end = copy_back(&mut out, 0, seed.len(), offset, length).unwrap();
1504 assert_eq!(&out[..end], wanted.as_slice(), "offset {offset} length {length}");
1505 }
1506 }
1507 let mut short = vec![1, 2, 3, 0];
1508 assert!(copy_back(&mut short, 0, 3, 1, 2).is_err(), "past the end of the buffer");
1509 assert!(copy_back(&mut short, 0, 3, 4, 1).is_err(), "further back than the output");
1510 }
1511
1512 #[test]
1513 fn an_empty_chunk_round_trips() {
1514 let bytes = round_trip(&[]);
1515 assert_eq!(kind_of(&bytes), Kind::Plain);
1516 }
1517
1518 #[test]
1519 fn a_constant_column_costs_what_one_value_costs() {
1520 let values = vec![b"https://www.example.com/".to_vec(); 100_000];
1521 let bytes = round_trip(&values);
1522 assert_eq!(kind_of(&bytes), Kind::Constant);
1523 assert_eq!(bytes.len(), 9 + 24);
1524 }
1525
1526 #[test]
1527 fn a_url_column_of_unique_values_is_matched_rather_than_only_compressed() {
1528 let values = keyed(urls(20_000));
1536 let bytes = round_trip(&values);
1537 assert_eq!(kind_of(&bytes), Kind::Lz);
1538
1539 let borrowed: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1542 let fsst = encode_as(Kind::Fsst, &borrowed, 0, &EXHAUSTIVE).unwrap().unwrap();
1543 assert!(bytes.len() < fsst.len(), "{} against FSST {}", bytes.len(), fsst.len());
1544
1545 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1548 assert!(ratio > 4.0, "{ratio:.2}x");
1549 }
1550
1551 #[test]
1552 fn a_sample_of_a_periodic_column_learns_every_phase_of_it() {
1553 let values = urls(20_000);
1558 let borrowed = borrow(&values);
1559 let sample = sample_of(&borrowed);
1560 let mut phases: Vec<&[u8]> = sample
1561 .iter()
1562 .map(|value| {
1563 let query =
1564 value.iter().position(|byte| *byte == b'?').expect("every value has a query");
1565 &value[..query]
1566 })
1567 .collect();
1568 phases.sort_unstable();
1569 phases.dedup();
1570 assert_eq!(phases.len(), 12);
1572 let whole = SymbolTable::train(&borrowed);
1573 let sampled = SymbolTable::train(&sample);
1574 let mut on_whole = Vec::new();
1575 let mut on_sample = Vec::new();
1576 for value in &borrowed {
1577 whole.compress(value, &mut on_whole);
1578 sampled.compress(value, &mut on_sample);
1579 }
1580 assert!(
1583 on_sample.len() < on_whole.len() * 5 / 4,
1584 "{} against {}",
1585 on_sample.len(),
1586 on_whole.len()
1587 );
1588 }
1589
1590 #[test]
1591 fn a_repeating_column_becomes_a_dictionary_of_compressed_entries() {
1592 let distinct = urls(500);
1598 let values: Vec<Vec<u8>> =
1599 (0..50_000).map(|index| distinct[index * 7919 % distinct.len()].clone()).collect();
1600 let bytes = round_trip(&values);
1601 assert_eq!(kind_of(&bytes), Kind::Dict);
1602 let shape = describe(&bytes).unwrap();
1603 assert!(shape.starts_with("DICT(LZ("), "{shape}");
1604 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1605 assert!(ratio > 20.0, "{ratio:.2}x, {shape}");
1606 }
1607
1608 #[test]
1609 fn a_column_of_long_runs_costs_almost_nothing() {
1610 let distinct = urls(50);
1613 let mut values = Vec::new();
1614 for entry in &distinct {
1615 values.extend(std::iter::repeat_n(entry.clone(), 1000));
1616 }
1617 let bytes = round_trip(&values);
1618 let shape = describe(&bytes).unwrap();
1619 assert!(shape.contains("RLE"), "{shape}");
1620 assert!(bytes.len() < 2000, "{} bytes: {shape}", bytes.len());
1621 }
1622
1623 #[test]
1624 fn incompressible_strings_stay_close_to_their_own_size() {
1625 let mut state = 0x2545_f491_4f6c_dd1du64;
1628 let values: Vec<Vec<u8>> = (0..2000)
1629 .map(|_| {
1630 (0..32)
1631 .map(|_| {
1632 state ^= state << 13;
1633 state ^= state >> 7;
1634 state ^= state << 17;
1635 state as u8
1636 })
1637 .collect()
1638 })
1639 .collect();
1640 let bytes = round_trip(&values);
1641 assert!(bytes.len() < 2000 * 32 + 3000, "{} bytes", bytes.len());
1642 }
1643
1644 #[test]
1645 fn lengths_are_stored_rather_than_offsets() {
1646 let values: Vec<Vec<u8>> =
1649 (0..100_000).map(|index| format!("{index:024}").into_bytes()).collect();
1650 let borrowed = borrow(&values);
1651 let bytes = encode_only(Kind::Plain, &borrowed).unwrap().unwrap();
1652 assert_eq!(bytes.len(), 5 + 13 + 100_000 * 24);
1653 }
1654
1655 #[test]
1656 fn empty_strings_are_values_and_not_nulls() {
1657 let values = vec![Vec::new(), b"a".to_vec(), Vec::new(), b"bb".to_vec()];
1658 round_trip(&values);
1659 }
1660
1661 #[test]
1662 fn a_chunk_with_one_value_round_trips() {
1663 round_trip(&[b"only".to_vec()]);
1664 }
1665
1666 #[test]
1667 fn every_candidate_that_applies_decodes_to_the_input() {
1668 let values = urls(3000);
1669 let borrowed = borrow(&values);
1670 let applicable = candidates(&borrowed, 0);
1671 assert!(applicable.len() >= 2, "{applicable:?}");
1672 for kind in applicable {
1673 let bytes = encode_only(kind, &borrowed).unwrap().unwrap();
1674 assert_eq!(decode(&bytes).unwrap(), values, "{}", kind.name());
1675 }
1676 }
1677
1678 #[test]
1679 fn the_chooser_picks_the_smallest_candidate() {
1680 let values = urls(2000);
1681 let borrowed = borrow(&values);
1682 let chosen = encode(&borrowed).unwrap();
1683 for (_, size) in candidate_sizes(&borrowed).unwrap() {
1684 assert!(chosen.len() <= size);
1685 }
1686 }
1687
1688 #[test]
1689 fn a_truncated_chunk_is_an_error_and_not_a_panic() {
1690 let values = urls(40);
1691 let bytes = encode(&borrow(&values)).unwrap();
1692 for len in 0..bytes.len() {
1693 assert!(decode(&bytes[..len]).is_err(), "{len} bytes decoded");
1694 }
1695 }
1696
1697 #[test]
1698 fn trailing_bytes_are_an_error() {
1699 let mut bytes = encode(&borrow(&urls(10))).unwrap();
1700 bytes.push(0);
1701 let error = decode(&bytes).unwrap_err();
1702 assert!(error.message().contains("left over"), "{error}");
1703 }
1704
1705 #[test]
1706 fn an_unknown_tag_is_an_error() {
1707 let error = decode(&[99, 0, 0, 0, 0]).unwrap_err();
1708 assert!(error.message().contains("unknown string encoding tag"), "{error}");
1709 }
1710
1711 #[test]
1712 fn a_dictionary_code_outside_the_dictionary_is_an_error() {
1713 let mut bytes = vec![Kind::Dict.tag()];
1714 put_u32(&mut bytes, 1);
1715 bytes.extend_from_slice(&encode(&[b"one".as_slice()]).unwrap());
1716 bytes.extend_from_slice(&integer::encode(&[9]).unwrap());
1717 let error = decode(&bytes).unwrap_err();
1718 assert!(error.message().contains("not in the dictionary"), "{error}");
1719 }
1720
1721 #[test]
1722 fn a_sorted_column_of_urls_is_front_coded() {
1723 let mut values = urls(20_000);
1727 values.sort();
1728 let bytes = round_trip(&values);
1729 assert_eq!(kind_of(&bytes), Kind::Front);
1730 let shape = describe(&bytes).unwrap();
1731 let mut plain = Vec::new();
1732 let borrowed = borrow(&values);
1733 for (kind, size) in candidate_sizes(&borrowed).unwrap() {
1734 if kind == Kind::Fsst {
1735 plain.push(size);
1736 }
1737 }
1738 let fsst = plain[0];
1739 assert!(bytes.len() * 2 < fsst, "{} against FSST {fsst}: {shape}", bytes.len());
1740 }
1741
1742 #[test]
1743 fn a_column_with_nothing_to_share_is_not_offered_front_coding() {
1744 let mut state = 0x9e37_79b9_7f4a_7c15u64;
1747 let values: Vec<Vec<u8>> = (0..2000)
1748 .map(|_| {
1749 (0..24)
1750 .map(|_| {
1751 state ^= state << 13;
1752 state ^= state >> 7;
1753 state ^= state << 17;
1754 (state % 251) as u8
1755 })
1756 .collect()
1757 })
1758 .collect();
1759 let borrowed = borrow(&values);
1760 assert!(!candidates(&borrowed, 0).contains(&Kind::Front));
1761 }
1762
1763 #[test]
1764 fn a_prefix_longer_than_the_value_before_it_is_an_error() {
1765 let mut bytes = vec![Kind::Front.tag()];
1766 put_u32(&mut bytes, 2);
1767 bytes.extend_from_slice(&integer::encode(&[0, 9]).unwrap());
1768 bytes.extend_from_slice(&encode(&[b"one".as_slice(), b"two".as_slice()]).unwrap());
1769 let error = decode(&bytes).unwrap_err();
1770 assert!(error.message().contains("shares 9 bytes"), "{error}");
1771 }
1772
1773 #[test]
1774 fn a_negative_prefix_is_an_error() {
1775 let mut bytes = vec![Kind::Front.tag()];
1776 put_u32(&mut bytes, 1);
1777 bytes.extend_from_slice(&integer::encode(&[-1]).unwrap());
1778 bytes.extend_from_slice(&encode(&[b"one".as_slice()]).unwrap());
1779 let error = decode(&bytes).unwrap_err();
1780 assert!(error.message().contains("negative shared prefix"), "{error}");
1781 }
1782
1783 #[test]
1784 fn a_negative_length_is_an_error() {
1785 let mut bytes = vec![Kind::Plain.tag()];
1786 put_u32(&mut bytes, 1);
1787 bytes.extend_from_slice(&integer::encode(&[-1]).unwrap());
1788 let error = decode(&bytes).unwrap_err();
1789 assert!(error.message().contains("negative string length"), "{error}");
1790 }
1791
1792 #[test]
1793 fn the_sample_is_spread_across_the_chunk_and_not_taken_from_the_front() {
1794 let mut values: Vec<Vec<u8>> = Vec::new();
1797 for index in 0..20_000 {
1798 let head = if index < 10_000 { "aaaaaaaaaaaaaaaa" } else { "zzzzzzzzzzzzzzzz" };
1799 values.push(format!("{head}/{index:08}").into_bytes());
1800 }
1801 let borrowed = borrow(&values);
1802 let sample = sample_of(&borrowed);
1803 let first_half = sample.iter().filter(|value| value.starts_with(b"aaaa")).count();
1804 let second_half = sample.len() - first_half;
1805 assert!(first_half > 0 && second_half > 0, "{first_half} and {second_half}");
1806 let bytes = round_trip(&values);
1807 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1808 assert!(ratio > 4.0, "{ratio:.2}x");
1809 }
1810}