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_prefix(bytes: &[u8]) -> Result<(Vec<Vec<u8>>, usize)> {
297 let mut reader = Reader::new(bytes);
298 let values = decode_chunk(&mut reader)?;
299 Ok((values.into_values(), reader.used()))
300}
301
302pub fn describe_prefix(bytes: &[u8]) -> Result<(String, usize)> {
308 let mut reader = Reader::new(bytes);
309 let text = describe_chunk(&mut reader)?;
310 Ok((text, reader.used()))
311}
312
313pub fn decode(bytes: &[u8]) -> Result<Vec<Vec<u8>>> {
319 Ok(decode_flat(bytes)?.into_values())
320}
321
322pub fn candidate_sizes(values: &[&[u8]]) -> Result<Vec<(Kind, usize)>> {
329 let mut sizes = Vec::new();
330 for kind in candidates(values, 0) {
331 if let Some(bytes) = encode_as(kind, values, 0, &EXHAUSTIVE)? {
332 sizes.push((kind, bytes.len()));
333 }
334 }
335 Ok(sizes)
336}
337
338#[must_use]
345pub fn offered(values: &[&[u8]]) -> Vec<Kind> {
346 candidates(values, 0)
347}
348
349pub fn encode_only(kind: Kind, values: &[&[u8]]) -> Result<Option<Vec<u8>>> {
361 encode_as(kind, values, 0, &EXHAUSTIVE)
362}
363
364pub(crate) fn size_as(kind: Kind, values: &[&[u8]], depth: u8) -> Result<Option<usize>> {
369 Ok(encode_as(kind, values, depth, &EXHAUSTIVE)?.map(|bytes| bytes.len()))
370}
371
372pub fn describe(bytes: &[u8]) -> Result<String> {
378 let mut reader = Reader::new(bytes);
379 describe_chunk(&mut reader)
380}
381
382#[must_use]
394pub fn with_symbols(shape: Settled, blocks: &[Vec<&[u8]>]) -> Settled {
395 let kinds = shape.strings();
396 let Some(depth) =
397 (0..=kinds.len()).find(|&at| matches!(kinds.get(at), Some(Kind::Fsst) | None))
398 else {
399 return shape;
400 };
401 let leads =
402 kinds[..depth].iter().all(|kind| matches!(kind, Kind::Front | Kind::Lz | Kind::Dict));
403 if !leads || depth > usize::from(MAX_DEPTH) {
404 return shape;
405 }
406 let mut reached: Vec<Vec<u8>> = Vec::new();
407 for block in blocks {
408 let mut values: Vec<Vec<u8>> = block.iter().map(|value| value.to_vec()).collect();
409 for kind in &kinds[..depth] {
410 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
411 values = match kind {
412 Kind::Front => front_code(&refs).1.into_iter().map(<[u8]>::to_vec).collect(),
413 Kind::Dict => dictionary_of(&refs).0.into_iter().map(<[u8]>::to_vec).collect(),
414 _ => {
415 let joined = refs.concat();
416 lz::tokens_of(&joined).literals.into_iter().map(<[u8]>::to_vec).collect()
417 }
418 };
419 }
420 reached.extend(values);
421 }
422 let refs: Vec<&[u8]> = reached.iter().map(Vec::as_slice).collect();
423 let table = SymbolTable::train(&sample_of(&refs));
424 if table.is_empty() {
425 return shape;
426 }
427 shape.with_symbols(depth as u8, table)
428}
429
430fn encode_at(values: &[&[u8]], depth: u8, chooser: &dyn Chooser) -> Result<Vec<u8>> {
431 let offered = candidates(values, depth);
432 let mut best: Option<Vec<u8>> = None;
433 for kind in chooser.narrow_strings(values, &offered, depth) {
434 let Some(bytes) = encode_as(kind, values, depth, chooser)? else {
435 continue;
436 };
437 if best.as_ref().is_none_or(|current| bytes.len() < current.len()) {
438 best = Some(bytes);
439 }
440 }
441 best.ok_or_else(|| Error::internal("no string encoding applied to the chunk"))
442}
443
444fn candidates(values: &[&[u8]], depth: u8) -> Vec<Kind> {
445 let mut kinds = vec![Kind::Plain];
446 if values.is_empty() {
447 return kinds;
448 }
449 if values.iter().all(|value| *value == values[0]) {
450 return vec![Kind::Constant];
451 }
452 kinds.push(Kind::Fsst);
453 if depth < MAX_DEPTH && has_duplicates(values) {
454 kinds.push(Kind::Dict);
455 }
456 if depth < MAX_DEPTH && sharing_of(values) >= total_len(values) / SHARE_DIVISOR {
457 kinds.push(Kind::Front);
458 }
459 if depth < MAX_DEPTH && total_len(values) >= LZ_FLOOR {
460 kinds.push(Kind::Lz);
461 }
462 kinds
463}
464
465fn sharing_of(values: &[&[u8]]) -> usize {
472 let mut shared = 0;
473 for pair in values.windows(2) {
474 shared += shared_prefix(pair[0], pair[1]);
475 }
476 shared
477}
478
479pub(crate) fn front_code<'a>(values: &[&'a [u8]]) -> (Vec<i64>, Vec<&'a [u8]>) {
485 let mut prefixes = Vec::with_capacity(values.len());
486 let mut suffixes: Vec<&'a [u8]> = Vec::with_capacity(values.len());
487 let mut previous: &[u8] = b"";
488 for value in values {
489 let value: &'a [u8] = value;
490 let shared = shared_prefix(previous, value);
491 prefixes.push(shared as i64);
492 suffixes.push(&value[shared..]);
493 previous = value;
494 }
495 (prefixes, suffixes)
496}
497
498pub(crate) fn front_decode(prefixes: &[i64], suffixes: Vec<Vec<u8>>) -> Result<Vec<Vec<u8>>> {
505 let mut values: Vec<Vec<u8>> = Vec::with_capacity(suffixes.len());
506 for (index, suffix) in suffixes.into_iter().enumerate() {
507 let shared = usize::try_from(prefixes[index])
508 .map_err(|_| Error::internal("a negative shared prefix length"))?;
509 let previous: &[u8] = if index == 0 { b"" } else { &values[index - 1] };
510 if shared > previous.len() {
511 return Err(Error::internal(format!(
512 "a value shares {shared} bytes with a value {} bytes long",
513 previous.len()
514 )));
515 }
516 let mut value = Vec::with_capacity(shared + suffix.len());
517 value.extend_from_slice(&previous[..shared]);
518 value.extend_from_slice(&suffix);
519 values.push(value);
520 }
521 Ok(values)
522}
523
524fn shared_prefix(previous: &[u8], value: &[u8]) -> usize {
525 let limit = previous.len().min(value.len());
526 let mut shared = 0;
527 while shared < limit && previous[shared] == value[shared] {
528 shared += 1;
529 }
530 shared
531}
532
533fn total_len(values: &[&[u8]]) -> usize {
534 values.iter().map(|value| value.len()).sum()
535}
536
537fn encode_as(
538 kind: Kind,
539 values: &[&[u8]],
540 depth: u8,
541 chooser: &dyn Chooser,
542) -> Result<Option<Vec<u8>>> {
543 let mut out = vec![kind.tag()];
544 put_u32(&mut out, u32::try_from(values.len()).map_err(|_| too_long(values.len()))?);
545 match kind {
546 Kind::Constant => {
547 let Some(first) = values.first() else {
548 return Ok(None);
549 };
550 if values.iter().any(|value| value != first) {
551 return Ok(None);
552 }
553 put_u32(&mut out, u32::try_from(first.len()).map_err(|_| too_long(first.len()))?);
554 out.extend_from_slice(first);
555 }
556 Kind::Plain => {
557 out.extend_from_slice(&encode_lengths(values, chooser)?);
558 for value in values {
559 out.extend_from_slice(value);
560 }
561 }
562 Kind::Fsst => {
563 let trained;
564 let table = match chooser.symbols(depth) {
565 Some(table) => table,
566 None => {
567 trained = SymbolTable::train(&sample_of(values));
568 &trained
569 }
570 };
571 if table.is_empty() {
572 return Ok(None);
573 }
574 let mut compressed = Vec::new();
575 let mut lengths = Vec::with_capacity(values.len());
576 for value in values {
577 let before = compressed.len();
578 table.compress(value, &mut compressed);
579 lengths.push((compressed.len() - before) as i64);
580 }
581 table.serialize(&mut out);
582 out.extend_from_slice(&integer::encode_with(&lengths, chooser)?);
583 out.extend_from_slice(&compressed);
584 }
585 Kind::Dict => {
586 let (entries, codes) = dictionary_of(values);
587 if entries.is_empty() {
588 return Ok(None);
589 }
590 out.extend_from_slice(&encode_at(&entries, depth + 1, chooser)?);
591 out.extend_from_slice(&integer::encode_with(&codes, chooser)?);
592 }
593 Kind::Front => {
594 let (prefixes, suffixes) = front_code(values);
595 out.extend_from_slice(&integer::encode_with(&prefixes, chooser)?);
596 out.extend_from_slice(&encode_at(&suffixes, depth + 1, chooser)?);
597 }
598 Kind::Lz => {
599 let mut joined = Vec::with_capacity(total_len(values));
600 let mut sizes = Vec::with_capacity(values.len());
601 for value in values {
602 joined.extend_from_slice(value);
603 sizes.push(value.len() as i64);
604 }
605 let tokens = lz::tokens_of(&joined);
606 out.extend_from_slice(&integer::encode_with(&sizes, chooser)?);
607 out.extend_from_slice(&integer::encode_with(&tokens.lengths, chooser)?);
608 out.extend_from_slice(&integer::encode_with(&tokens.offsets, chooser)?);
609 out.extend_from_slice(&encode_at(&tokens.literals, depth + 1, chooser)?);
610 }
611 }
612 Ok(Some(out))
613}
614
615fn decode_chunk(reader: &mut Reader<'_>) -> Result<Flat> {
616 let kind = Kind::from_tag(reader.u8()?)?;
617 let count = reader.u32()? as usize;
618 match kind {
619 Kind::Constant => {
620 let len = reader.u32()? as usize;
621 let value = reader.bytes(len)?;
622 let mut flat = Flat::with_capacity(count, len.saturating_mul(count));
623 for _ in 0..count {
624 flat.push(value);
625 }
626 Ok(flat)
627 }
628 Kind::Plain => {
629 let lengths = decode_lengths(reader, count)?;
630 let total = sum_of(&lengths)?;
633 let payload = reader.bytes(total)?;
634 let mut flat = Flat::with_capacity(count, total);
635 flat.bytes.extend_from_slice(payload);
636 let mut at = 0;
637 for length in lengths {
638 at += length;
639 flat.ends.push(at);
640 }
641 Ok(flat)
642 }
643 Kind::Fsst => {
644 let runs = read_compressed(reader, count)?;
645 let mut flat = Flat::with_capacity(count, runs.payload.len());
646 let mut at = 0;
647 for index in 0..count {
648 runs.run_into(index, &mut at, &mut flat.bytes)?;
649 flat.ends.push(flat.bytes.len());
650 }
651 Ok(flat)
652 }
653 Kind::Dict => {
654 let dictionary = decode_chunk(reader)?;
655 let codes = decode_integers(reader)?;
656 if codes.len() != count {
657 return Err(Error::internal(format!(
658 "a dictionary chunk says it holds {count} values and has {} codes",
659 codes.len()
660 )));
661 }
662 let mut flat = Flat::with_capacity(count, dictionary.bytes.len());
663 for code in codes {
664 let entry =
665 usize::try_from(code).ok().and_then(|index| dictionary.get(index)).ok_or_else(
666 || Error::internal(format!("code {code} is not in the dictionary")),
667 )?;
668 flat.push(entry);
669 }
670 Ok(flat)
671 }
672 Kind::Front => {
673 let prefixes = decode_integers(reader)?;
674 let suffixes = decode_chunk(reader)?;
675 if prefixes.len() != count || suffixes.len() != count {
676 return Err(Error::internal(format!(
677 "a front coded chunk says it holds {count} values and has {} prefixes and {} suffixes",
678 prefixes.len(),
679 suffixes.len()
680 )));
681 }
682 let mut flat = Flat::with_capacity(count, suffixes.bytes.len());
685 for (index, prefix) in prefixes.iter().enumerate() {
686 let shared = usize::try_from(*prefix)
687 .map_err(|_| Error::internal("a negative shared prefix length"))?;
688 let (from, previous) = if index == 0 {
689 (0, 0)
690 } else {
691 (flat.start(index - 1), flat.ends[index - 1] - flat.start(index - 1))
692 };
693 if shared > previous {
694 return Err(Error::internal(format!(
695 "a value shares {shared} bytes with a value {previous} bytes long"
696 )));
697 }
698 flat.bytes.extend_from_within(from..from + shared);
699 flat.bytes.extend_from_slice(suffixes.get(index).expect("in range"));
700 flat.ends.push(flat.bytes.len());
701 }
702 Ok(flat)
703 }
704 Kind::Lz => {
705 let sizes = decode_integers(reader)?;
706 let lengths = decode_integers(reader)?;
707 let offsets = decode_integers(reader)?;
708 if sizes.len() != count {
709 return Err(Error::internal(format!(
710 "a matched chunk says it holds {count} values and has {} lengths",
711 sizes.len()
712 )));
713 }
714 let mut total = 0usize;
715 let mut widths = Vec::with_capacity(count);
716 for size in sizes {
717 let width = usize::try_from(size)
718 .map_err(|_| Error::internal("a negative string length"))?;
719 total = total
720 .checked_add(width)
721 .ok_or_else(|| Error::internal("a string chunk longer than memory"))?;
722 widths.push(width);
723 }
724 let mut flat = Flat::with_capacity(count, total);
727 replay_literals(reader, &lengths, &offsets, total, &mut flat.bytes)?;
728 if flat.bytes.len() != total {
729 return Err(Error::internal(format!(
730 "a matched chunk rebuilt {} bytes where its lengths add up to {total}",
731 flat.bytes.len()
732 )));
733 }
734 let mut at = 0;
735 for width in widths {
736 at += width;
737 flat.ends.push(at);
738 }
739 Ok(flat)
740 }
741 }
742}
743
744struct Compressed<'a> {
750 table: SymbolTable,
752 lengths: Vec<usize>,
754 payload: &'a [u8],
756}
757
758impl Compressed<'_> {
759 fn run_into(&self, index: usize, at: &mut usize, out: &mut Vec<u8>) -> Result<()> {
768 self.table.decompress(self.run(index, at)?, out)
769 }
770
771 fn run(&self, index: usize, at: &mut usize) -> Result<&[u8]> {
774 let length = *self
775 .lengths
776 .get(index)
777 .ok_or_else(|| Error::internal(format!("run {index} is not in the chunk")))?;
778 let end = at
779 .checked_add(length)
780 .ok_or_else(|| Error::internal("a compressed chunk longer than memory"))?;
781 let run = self
782 .payload
783 .get(*at..end)
784 .ok_or_else(|| Error::internal("a compressed run is past the end of its chunk"))?;
785 *at = end;
786 Ok(run)
787 }
788}
789
790fn read_compressed<'a>(reader: &mut Reader<'a>, count: usize) -> Result<Compressed<'a>> {
799 let (table, used) = SymbolTable::deserialize(reader.rest())?;
800 reader.skip(used)?;
801 let lengths = decode_lengths(reader, count)?;
802 let compressed_len = sum_of(&lengths)?;
805 if compressed_len > reader.remaining() {
806 return Err(Error::internal(format!(
807 "a compressed chunk says it holds {compressed_len} bytes and has {}",
808 reader.remaining()
809 )));
810 }
811 let payload = reader.bytes(compressed_len)?;
812 Ok(Compressed { table, lengths, payload })
813}
814
815fn replay_literals(
828 reader: &mut Reader<'_>,
829 lengths: &[i64],
830 offsets: &[i64],
831 total: usize,
832 out: &mut Vec<u8>,
833) -> Result<()> {
834 if reader.rest().first() == Some(&Kind::Fsst.tag()) {
835 reader.u8()?;
836 let runs = reader.u32()? as usize;
837 let compressed = read_compressed(reader, runs)?;
838 return replay_in_place(&compressed, lengths, offsets, total, out);
839 }
840 let literals = decode_chunk(reader)?;
841 lz::rebuild_into(&literals, lengths, offsets, out)
842}
843
844const REPLAY_SLACK: usize = 16;
850
851fn replay_in_place(
863 compressed: &Compressed<'_>,
864 lengths: &[i64],
865 offsets: &[i64],
866 total: usize,
867 out: &mut Vec<u8>,
868) -> Result<()> {
869 let runs = compressed.lengths.len();
870 if runs != lengths.len() || lengths.len() != offsets.len() {
871 return Err(Error::internal(format!(
872 "a matched chunk has {runs} literal runs, {} lengths and {} offsets",
873 lengths.len(),
874 offsets.len()
875 )));
876 }
877 let base = out.len();
878 let room = total
879 .checked_add(REPLAY_SLACK)
880 .ok_or_else(|| Error::internal("a string chunk longer than memory"))?;
881 out.resize(base + room, 0);
882 let mut read = 0;
883 let mut at = base;
884 for (index, (&length, &offset)) in lengths.iter().zip(offsets).enumerate() {
885 at = compressed.table.decompress_at(compressed.run(index, &mut read)?, out, at)?;
886 let length =
887 usize::try_from(length).map_err(|_| Error::internal("a negative copy length"))?;
888 if length == 0 {
889 continue;
890 }
891 let offset =
892 usize::try_from(offset).map_err(|_| Error::internal("a negative copy offset"))?;
893 at = copy_back(out, base, at, offset, length)?;
894 }
895 if at > base + total {
896 return Err(Error::internal(format!(
897 "a matched chunk rebuilt {} bytes where its lengths add up to {total}",
898 at - base
899 )));
900 }
901 out.truncate(at);
902 Ok(())
903}
904
905fn copy_back(
913 out: &mut [u8],
914 base: usize,
915 at: usize,
916 offset: usize,
917 length: usize,
918) -> Result<usize> {
919 if offset == 0 || offset > at - base {
920 return Err(Error::internal(format!(
921 "a copy reaches {offset} bytes back into {} bytes of output",
922 at - base
923 )));
924 }
925 let end = at
926 .checked_add(length)
927 .filter(|&end| end <= out.len())
928 .ok_or_else(|| Error::internal("a matched chunk rebuilds more than its lengths say"))?;
929 let from = at - offset;
930 let wide = end + REPLAY_SLACK <= out.len();
931 if wide && offset >= 16 {
932 let mut step = 0;
933 while step < length {
934 out.copy_within(from + step..from + step + 16, at + step);
935 step += 16;
936 }
937 } else if wide && offset >= 8 {
938 let mut step = 0;
939 while step < length {
940 out.copy_within(from + step..from + step + 8, at + step);
941 step += 8;
942 }
943 } else {
944 for step in 0..length {
945 out[at + step] = out[from + step];
946 }
947 }
948 Ok(end)
949}
950
951fn describe_chunk(reader: &mut Reader<'_>) -> Result<String> {
952 let kind = Kind::from_tag(reader.u8()?)?;
953 let count = reader.u32()? as usize;
954 Ok(match kind {
955 Kind::Constant => {
956 let len = reader.u32()? as usize;
957 reader.bytes(len)?;
958 "CONSTANT".to_string()
959 }
960 Kind::Plain => {
961 let (shape, lengths) = describe_lengths(reader, count)?;
962 reader.skip(lengths.iter().sum())?;
963 format!("PLAIN({shape})")
964 }
965 Kind::Fsst => {
966 let (table, used) = SymbolTable::deserialize(reader.rest())?;
967 reader.skip(used)?;
968 let (shape, lengths) = describe_lengths(reader, count)?;
969 reader.skip(lengths.iter().sum())?;
970 format!("FSST[{}]({shape})", table.len())
971 }
972 Kind::Dict => {
973 let entries = describe_chunk(reader)?;
974 let codes = describe_integers(reader)?;
975 format!("DICT({entries}, {codes})")
976 }
977 Kind::Front => {
978 let prefixes = describe_integers(reader)?;
979 let suffixes = describe_chunk(reader)?;
980 format!("FRONT({prefixes}, {suffixes})")
981 }
982 Kind::Lz => {
983 let sizes = describe_integers(reader)?;
984 let lengths = describe_integers(reader)?;
985 let offsets = describe_integers(reader)?;
986 let literals = describe_chunk(reader)?;
987 format!("LZ({sizes}, {lengths}, {offsets}, {literals})")
988 }
989 })
990}
991
992fn describe_lengths(reader: &mut Reader<'_>, count: usize) -> Result<(String, Vec<usize>)> {
996 let (shape, _) = integer::describe_prefix(reader.rest())?;
997 let lengths = decode_lengths(reader, count)?;
998 Ok((shape, lengths))
999}
1000
1001fn encode_lengths(values: &[&[u8]], chooser: &dyn Chooser) -> Result<Vec<u8>> {
1002 let lengths: Vec<i64> = values.iter().map(|value| value.len() as i64).collect();
1003 integer::encode_with(&lengths, chooser)
1004}
1005
1006fn decode_lengths(reader: &mut Reader<'_>, count: usize) -> Result<Vec<usize>> {
1007 let lengths = decode_integers(reader)?;
1008 if lengths.len() != count {
1009 return Err(Error::internal(format!(
1010 "a string chunk says it holds {count} values and has {} lengths",
1011 lengths.len()
1012 )));
1013 }
1014 lengths
1015 .into_iter()
1016 .map(|length| {
1017 usize::try_from(length).map_err(|_| Error::internal("a negative string length"))
1018 })
1019 .collect()
1020}
1021
1022fn sum_of(lengths: &[usize]) -> Result<usize> {
1028 lengths
1029 .iter()
1030 .try_fold(0usize, |total, length| total.checked_add(*length))
1031 .ok_or_else(|| Error::internal("a string chunk longer than memory"))
1032}
1033
1034fn decode_integers(reader: &mut Reader<'_>) -> Result<Vec<i64>> {
1038 let (values, used) = integer::decode_prefix(reader.rest())?;
1039 reader.skip(used)?;
1040 Ok(values)
1041}
1042
1043fn describe_integers(reader: &mut Reader<'_>) -> Result<String> {
1044 let (text, used) = integer::describe_prefix(reader.rest())?;
1045 reader.skip(used)?;
1046 Ok(text)
1047}
1048
1049pub(crate) fn sample_of<'a>(values: &[&'a [u8]]) -> Vec<&'a [u8]> {
1068 sample_bytes_of(values, SAMPLE_BYTES)
1069}
1070
1071pub(crate) fn sample_bytes_of<'a>(values: &[&'a [u8]], budget: usize) -> Vec<&'a [u8]> {
1074 let budget = budget.max(1);
1075 let total: usize = values.iter().map(|value| value.len()).sum();
1076 if total <= budget {
1077 return values.to_vec();
1078 }
1079 let stride = total.div_ceil(budget).max(1);
1080 let span = (stride * 2 - 1).max(1) as u64;
1081 let mut state = 0x2545_f491_4f6c_dd1du64;
1082 let mut sample = Vec::with_capacity(values.len() / stride + 1);
1083 let mut at = 0usize;
1084 while at < values.len() {
1085 sample.push(values[at]);
1086 state ^= state << 13;
1087 state ^= state >> 7;
1088 state ^= state << 17;
1089 at += 1 + (state % span) as usize;
1090 }
1091 sample
1092}
1093
1094fn dictionary_of<'a>(values: &[&'a [u8]]) -> (Vec<&'a [u8]>, Vec<i64>) {
1106 let mut order: Vec<u32> = (0..values.len() as u32).collect();
1107 order.sort_unstable_by(|left, right| values[*left as usize].cmp(values[*right as usize]));
1108 let mut entries: Vec<&'a [u8]> = Vec::new();
1109 let mut codes = vec![0i64; values.len()];
1110 for &index in &order {
1111 let value = values[index as usize];
1112 if entries.last() != Some(&value) {
1113 entries.push(value);
1114 }
1115 codes[index as usize] = (entries.len() - 1) as i64;
1116 }
1117 (entries, codes)
1118}
1119
1120fn has_duplicates(values: &[&[u8]]) -> bool {
1130 let Some(slots) = values.len().checked_mul(2).map(usize::next_power_of_two) else {
1131 return false;
1132 };
1133 let mask = slots - 1;
1134 let mut table = vec![u32::MAX; slots];
1135 for (index, value) in values.iter().enumerate() {
1136 let mut at = hash_of(value) as usize & mask;
1137 loop {
1138 let held = table[at];
1139 if held == u32::MAX {
1140 table[at] = index as u32;
1141 break;
1142 }
1143 if values[held as usize] == *value {
1144 return true;
1145 }
1146 at = (at + 1) & mask;
1147 }
1148 }
1149 false
1150}
1151
1152fn hash_of(value: &[u8]) -> u64 {
1159 let mut hash = 0xcbf2_9ce4_8422_2325_u64;
1160 let mut chunks = value.chunks_exact(8);
1161 for chunk in &mut chunks {
1162 let word = u64::from_le_bytes(chunk.try_into().expect("chunks_exact(8) gives eight bytes"));
1163 hash = (hash ^ word).wrapping_mul(0x1_0000_01b3);
1164 }
1165 for byte in chunks.remainder() {
1166 hash = (hash ^ u64::from(*byte)).wrapping_mul(0x1_0000_01b3);
1167 }
1168 (hash ^ (value.len() as u64)).wrapping_mul(0x1_0000_01b3)
1169}
1170
1171fn too_long(len: usize) -> Error {
1172 Error::internal(format!("a string chunk of {len} is longer than the format allows"))
1173}
1174
1175fn put_u32(out: &mut Vec<u8>, value: u32) {
1176 out.extend_from_slice(&value.to_le_bytes());
1177}
1178
1179#[cfg(test)]
1180mod tests {
1181 use super::*;
1182
1183 fn urls(count: usize) -> Vec<Vec<u8>> {
1184 let hosts = ["www.example.com", "shop.example.com", "news.other.example.org"];
1185 let paths = ["/index.html", "/catalog/item", "/search", "/user/profile/settings"];
1186 (0..count)
1187 .map(|index| {
1188 let host = hosts[index % hosts.len()];
1189 let path = paths[(index / 3) % paths.len()];
1190 format!("http://{host}{path}?session={}&ref=google", index * 7).into_bytes()
1191 })
1192 .collect()
1193 }
1194
1195 fn front_lz() -> Settled {
1196 Settled::new(vec![Kind::Front, Kind::Lz], vec![integer::Kind::Packed])
1197 }
1198
1199 #[test]
1203 fn a_block_compressed_against_the_column_table_reads_back() {
1204 let values = urls(4096);
1205 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1206 let blocks: Vec<Vec<&[u8]>> = refs.chunks(1024).take(2).map(<[&[u8]]>::to_vec).collect();
1207 let shape = with_symbols(front_lz(), &blocks);
1208 assert!(shape.symbols(2).is_some(), "FRONT then LZ leaves FSST the third level");
1209 assert!(shape.symbols(1).is_none(), "and only that one");
1210 for block in refs.chunks(1024) {
1211 let encoded = encode_with(block, &shape).expect("encoded");
1212 assert_eq!(decode(&encoded).expect("decoded"), block.to_vec());
1213 }
1214 }
1215
1216 #[test]
1218 fn a_shape_ending_in_plain_gets_no_table() {
1219 let values = urls(1024);
1220 let refs: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1221 let plain = Settled::new(vec![Kind::Lz, Kind::Plain], vec![integer::Kind::Packed]);
1222 let shape = with_symbols(plain, std::slice::from_ref(&refs));
1223 assert!((0..=MAX_DEPTH).all(|depth| shape.symbols(depth).is_none()));
1224 let fsst = Settled::new(vec![Kind::Fsst], vec![integer::Kind::Packed]);
1225 assert!(with_symbols(fsst, &[refs]).symbols(0).is_some());
1226 }
1227
1228 fn keyed(values: Vec<Vec<u8>>) -> Vec<Vec<u8>> {
1232 values
1233 .into_iter()
1234 .enumerate()
1235 .map(|(index, value)| {
1236 let key = (index as u64).wrapping_mul(0x9e37_79b9_7f4a_7c15) % 1_000_000_007;
1237 let mut out = format!("{key:010}/").into_bytes();
1238 out.extend_from_slice(&value);
1239 out
1240 })
1241 .collect()
1242 }
1243
1244 fn borrow(values: &[Vec<u8>]) -> Vec<&[u8]> {
1245 values.iter().map(Vec::as_slice).collect()
1246 }
1247
1248 fn round_trip(values: &[Vec<u8>]) -> Vec<u8> {
1249 let borrowed = borrow(values);
1250 let bytes = encode(&borrowed).unwrap();
1251 let back = decode(&bytes).unwrap();
1252 assert_eq!(back, values, "{}", describe(&bytes).unwrap());
1253 check_flat(&bytes, values);
1254 bytes
1255 }
1256
1257 fn check_flat(bytes: &[u8], values: &[Vec<u8>]) {
1260 let flat = decode_flat(bytes).unwrap();
1261 let shape = describe(bytes).unwrap();
1262 assert_eq!(flat.len(), values.len(), "{shape}");
1263 assert_eq!(flat.iter().collect::<Vec<_>>(), borrow(values), "{shape}");
1264 assert_eq!(flat.bytes(), values.concat(), "{shape}");
1265 assert_eq!(flat.get(values.len()), None, "{shape}");
1266 }
1267
1268 fn kind_of(bytes: &[u8]) -> Kind {
1269 Kind::from_tag(bytes[0]).unwrap()
1270 }
1271
1272 #[test]
1273 fn every_shape_decodes_flat_to_what_it_decodes_split() {
1274 let columns =
1278 [urls(600), keyed(urls(600)), vec![b"same".to_vec(); 400], vec![Vec::new(); 7]];
1279 for values in &columns {
1280 let borrowed = borrow(values);
1281 for kind in offered(&borrowed) {
1282 let Some(bytes) = encode_only(kind, &borrowed).unwrap() else {
1283 continue;
1284 };
1285 assert_eq!(decode(&bytes).unwrap(), *values, "{}", kind.name());
1286 let flat = decode_flat(&bytes).unwrap();
1287 assert_eq!(flat.iter().collect::<Vec<_>>(), borrowed, "{}", kind.name());
1288 assert_eq!(flat.bytes(), values.concat(), "{}", kind.name());
1289 }
1290 }
1291 }
1292
1293 #[test]
1294 fn a_front_coded_chunk_that_shares_more_than_it_has_is_an_error() {
1295 let suffixes: [&[u8]; 2] = [b"abc", b"x"];
1299 let mut bytes = vec![Kind::Front.tag()];
1300 put_u32(&mut bytes, 2);
1301 bytes.extend_from_slice(&integer::encode(&[0, 9]).unwrap());
1302 bytes.extend_from_slice(&encode_only(Kind::Plain, &suffixes).unwrap().unwrap());
1303 let error = decode_flat(&bytes).expect_err("a nine byte prefix of a three byte value");
1304 assert_eq!(error.message(), "a value shares 9 bytes with a value 3 bytes long");
1305 assert_eq!(decode(&bytes).unwrap_err().message(), error.message());
1306 }
1307
1308 #[test]
1309 fn the_dictionary_is_sorted_and_the_codes_point_back_at_the_values() {
1310 let values = vec![
1313 b"pear".to_vec(),
1314 b"apple".to_vec(),
1315 b"pear".to_vec(),
1316 b"cherry".to_vec(),
1317 b"apple".to_vec(),
1318 ];
1319 let borrowed = borrow(&values);
1320 let (entries, codes) = dictionary_of(&borrowed);
1321 assert_eq!(entries, vec![b"apple".as_slice(), b"cherry".as_slice(), b"pear".as_slice()]);
1322 assert_eq!(codes, vec![2, 0, 2, 1, 0]);
1323 for (code, value) in codes.iter().zip(&borrowed) {
1324 assert_eq!(entries[*code as usize], *value);
1325 }
1326 }
1327
1328 #[test]
1329 fn a_column_with_nothing_repeated_has_no_duplicates_and_one_with_anything_does() {
1330 let distinct: Vec<Vec<u8>> =
1331 (0..5000).map(|index| format!("value-{index}").into_bytes()).collect();
1332 assert!(!has_duplicates(&borrow(&distinct)));
1333
1334 let mut repeated = distinct.clone();
1336 repeated.push(b"value-0".to_vec());
1337 assert!(has_duplicates(&borrow(&repeated)));
1338
1339 assert!(!has_duplicates(&borrow(&Vec::new())));
1340 assert!(!has_duplicates(&borrow(&[b"one".to_vec()])));
1341 assert!(has_duplicates(&borrow(&vec![b"same".to_vec(); 2])));
1342 }
1343
1344 #[test]
1345 fn long_values_that_differ_only_at_the_end_are_not_confused_for_each_other() {
1346 let stem = "http://www.example.com/a/very/long/path/that/goes/on?session=";
1349 let values: Vec<Vec<u8>> =
1350 (0..2000).map(|index| format!("{stem}{index}").into_bytes()).collect();
1351 assert!(!has_duplicates(&borrow(&values)));
1352 let (entries, codes) = dictionary_of(&borrow(&values));
1353 assert_eq!(entries.len(), values.len());
1354 assert_eq!(codes.len(), values.len());
1355 }
1356
1357 #[test]
1358 fn what_the_chooser_returns_is_the_smallest_of_what_it_was_offered() {
1359 for values in [urls(400), keyed(urls(400)), vec![b"same".to_vec(); 50], Vec::new()] {
1365 let borrowed = borrow(&values);
1366 let chosen = encode(&borrowed).unwrap();
1367 let mut smallest: Option<Vec<u8>> = None;
1368 for kind in offered(&borrowed) {
1369 let Some(bytes) = encode_only(kind, &borrowed).unwrap() else {
1370 continue;
1371 };
1372 if smallest.as_ref().is_none_or(|best| bytes.len() < best.len()) {
1373 smallest = Some(bytes);
1374 }
1375 }
1376 assert_eq!(smallest.as_deref(), Some(chosen.as_slice()), "{}", values.len());
1377 }
1378 }
1379
1380 fn raw_size(values: &[Vec<u8>]) -> usize {
1381 values.iter().map(Vec::len).sum::<usize>() + values.len() * 4
1382 }
1383
1384 #[test]
1385 fn a_matched_chunk_replays_literals_whether_or_not_they_are_compressed() {
1386 let compressed = describe(&round_trip(&keyed(urls(20_000)))).unwrap();
1392 assert!(compressed.starts_with("LZ(") && compressed.contains(", FSST["), "{compressed}");
1393
1394 let buffered = describe(&round_trip(&keyed(urls(300)))).unwrap();
1395 assert!(buffered.starts_with("LZ(") && buffered.contains(", PLAIN("), "{buffered}");
1396 }
1397
1398 #[test]
1399 fn a_copy_back_writes_what_a_byte_at_a_time_copy_writes_at_every_distance() {
1400 let seed: Vec<u8> = (0..40u8).map(|byte| byte.wrapping_mul(37).wrapping_add(11)).collect();
1404 for offset in 1..=seed.len() {
1405 for length in 1..=50 {
1406 let mut wanted = seed.clone();
1407 for _ in 0..length {
1408 wanted.push(wanted[wanted.len() - offset]);
1409 }
1410 let mut out = seed.clone();
1411 out.resize(seed.len() + length + REPLAY_SLACK, 0);
1412 let end = copy_back(&mut out, 0, seed.len(), offset, length).unwrap();
1413 assert_eq!(&out[..end], wanted.as_slice(), "offset {offset} length {length}");
1414 }
1415 }
1416 let mut short = vec![1, 2, 3, 0];
1417 assert!(copy_back(&mut short, 0, 3, 1, 2).is_err(), "past the end of the buffer");
1418 assert!(copy_back(&mut short, 0, 3, 4, 1).is_err(), "further back than the output");
1419 }
1420
1421 #[test]
1422 fn an_empty_chunk_round_trips() {
1423 let bytes = round_trip(&[]);
1424 assert_eq!(kind_of(&bytes), Kind::Plain);
1425 }
1426
1427 #[test]
1428 fn a_constant_column_costs_what_one_value_costs() {
1429 let values = vec![b"https://www.example.com/".to_vec(); 100_000];
1430 let bytes = round_trip(&values);
1431 assert_eq!(kind_of(&bytes), Kind::Constant);
1432 assert_eq!(bytes.len(), 9 + 24);
1433 }
1434
1435 #[test]
1436 fn a_url_column_of_unique_values_is_matched_rather_than_only_compressed() {
1437 let values = keyed(urls(20_000));
1445 let bytes = round_trip(&values);
1446 assert_eq!(kind_of(&bytes), Kind::Lz);
1447
1448 let borrowed: Vec<&[u8]> = values.iter().map(Vec::as_slice).collect();
1451 let fsst = encode_as(Kind::Fsst, &borrowed, 0, &EXHAUSTIVE).unwrap().unwrap();
1452 assert!(bytes.len() < fsst.len(), "{} against FSST {}", bytes.len(), fsst.len());
1453
1454 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1457 assert!(ratio > 4.0, "{ratio:.2}x");
1458 }
1459
1460 #[test]
1461 fn a_sample_of_a_periodic_column_learns_every_phase_of_it() {
1462 let values = urls(20_000);
1467 let borrowed = borrow(&values);
1468 let sample = sample_of(&borrowed);
1469 let mut phases: Vec<&[u8]> = sample
1470 .iter()
1471 .map(|value| {
1472 let query =
1473 value.iter().position(|byte| *byte == b'?').expect("every value has a query");
1474 &value[..query]
1475 })
1476 .collect();
1477 phases.sort_unstable();
1478 phases.dedup();
1479 assert_eq!(phases.len(), 12);
1481 let whole = SymbolTable::train(&borrowed);
1482 let sampled = SymbolTable::train(&sample);
1483 let mut on_whole = Vec::new();
1484 let mut on_sample = Vec::new();
1485 for value in &borrowed {
1486 whole.compress(value, &mut on_whole);
1487 sampled.compress(value, &mut on_sample);
1488 }
1489 assert!(
1492 on_sample.len() < on_whole.len() * 5 / 4,
1493 "{} against {}",
1494 on_sample.len(),
1495 on_whole.len()
1496 );
1497 }
1498
1499 #[test]
1500 fn a_repeating_column_becomes_a_dictionary_of_compressed_entries() {
1501 let distinct = urls(500);
1507 let values: Vec<Vec<u8>> =
1508 (0..50_000).map(|index| distinct[index * 7919 % distinct.len()].clone()).collect();
1509 let bytes = round_trip(&values);
1510 assert_eq!(kind_of(&bytes), Kind::Dict);
1511 let shape = describe(&bytes).unwrap();
1512 assert!(shape.starts_with("DICT(LZ("), "{shape}");
1513 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1514 assert!(ratio > 20.0, "{ratio:.2}x, {shape}");
1515 }
1516
1517 #[test]
1518 fn a_column_of_long_runs_costs_almost_nothing() {
1519 let distinct = urls(50);
1522 let mut values = Vec::new();
1523 for entry in &distinct {
1524 values.extend(std::iter::repeat_n(entry.clone(), 1000));
1525 }
1526 let bytes = round_trip(&values);
1527 let shape = describe(&bytes).unwrap();
1528 assert!(shape.contains("RLE"), "{shape}");
1529 assert!(bytes.len() < 2000, "{} bytes: {shape}", bytes.len());
1530 }
1531
1532 #[test]
1533 fn incompressible_strings_stay_close_to_their_own_size() {
1534 let mut state = 0x2545_f491_4f6c_dd1du64;
1537 let values: Vec<Vec<u8>> = (0..2000)
1538 .map(|_| {
1539 (0..32)
1540 .map(|_| {
1541 state ^= state << 13;
1542 state ^= state >> 7;
1543 state ^= state << 17;
1544 state as u8
1545 })
1546 .collect()
1547 })
1548 .collect();
1549 let bytes = round_trip(&values);
1550 assert!(bytes.len() < 2000 * 32 + 3000, "{} bytes", bytes.len());
1551 }
1552
1553 #[test]
1554 fn lengths_are_stored_rather_than_offsets() {
1555 let values: Vec<Vec<u8>> =
1558 (0..100_000).map(|index| format!("{index:024}").into_bytes()).collect();
1559 let borrowed = borrow(&values);
1560 let bytes = encode_only(Kind::Plain, &borrowed).unwrap().unwrap();
1561 assert_eq!(bytes.len(), 5 + 13 + 100_000 * 24);
1562 }
1563
1564 #[test]
1565 fn empty_strings_are_values_and_not_nulls() {
1566 let values = vec![Vec::new(), b"a".to_vec(), Vec::new(), b"bb".to_vec()];
1567 round_trip(&values);
1568 }
1569
1570 #[test]
1571 fn a_chunk_with_one_value_round_trips() {
1572 round_trip(&[b"only".to_vec()]);
1573 }
1574
1575 #[test]
1576 fn every_candidate_that_applies_decodes_to_the_input() {
1577 let values = urls(3000);
1578 let borrowed = borrow(&values);
1579 let applicable = candidates(&borrowed, 0);
1580 assert!(applicable.len() >= 2, "{applicable:?}");
1581 for kind in applicable {
1582 let bytes = encode_only(kind, &borrowed).unwrap().unwrap();
1583 assert_eq!(decode(&bytes).unwrap(), values, "{}", kind.name());
1584 }
1585 }
1586
1587 #[test]
1588 fn the_chooser_picks_the_smallest_candidate() {
1589 let values = urls(2000);
1590 let borrowed = borrow(&values);
1591 let chosen = encode(&borrowed).unwrap();
1592 for (_, size) in candidate_sizes(&borrowed).unwrap() {
1593 assert!(chosen.len() <= size);
1594 }
1595 }
1596
1597 #[test]
1598 fn a_truncated_chunk_is_an_error_and_not_a_panic() {
1599 let values = urls(40);
1600 let bytes = encode(&borrow(&values)).unwrap();
1601 for len in 0..bytes.len() {
1602 assert!(decode(&bytes[..len]).is_err(), "{len} bytes decoded");
1603 }
1604 }
1605
1606 #[test]
1607 fn trailing_bytes_are_an_error() {
1608 let mut bytes = encode(&borrow(&urls(10))).unwrap();
1609 bytes.push(0);
1610 let error = decode(&bytes).unwrap_err();
1611 assert!(error.message().contains("left over"), "{error}");
1612 }
1613
1614 #[test]
1615 fn an_unknown_tag_is_an_error() {
1616 let error = decode(&[99, 0, 0, 0, 0]).unwrap_err();
1617 assert!(error.message().contains("unknown string encoding tag"), "{error}");
1618 }
1619
1620 #[test]
1621 fn a_dictionary_code_outside_the_dictionary_is_an_error() {
1622 let mut bytes = vec![Kind::Dict.tag()];
1623 put_u32(&mut bytes, 1);
1624 bytes.extend_from_slice(&encode(&[b"one".as_slice()]).unwrap());
1625 bytes.extend_from_slice(&integer::encode(&[9]).unwrap());
1626 let error = decode(&bytes).unwrap_err();
1627 assert!(error.message().contains("not in the dictionary"), "{error}");
1628 }
1629
1630 #[test]
1631 fn a_sorted_column_of_urls_is_front_coded() {
1632 let mut values = urls(20_000);
1636 values.sort();
1637 let bytes = round_trip(&values);
1638 assert_eq!(kind_of(&bytes), Kind::Front);
1639 let shape = describe(&bytes).unwrap();
1640 let mut plain = Vec::new();
1641 let borrowed = borrow(&values);
1642 for (kind, size) in candidate_sizes(&borrowed).unwrap() {
1643 if kind == Kind::Fsst {
1644 plain.push(size);
1645 }
1646 }
1647 let fsst = plain[0];
1648 assert!(bytes.len() * 2 < fsst, "{} against FSST {fsst}: {shape}", bytes.len());
1649 }
1650
1651 #[test]
1652 fn a_column_with_nothing_to_share_is_not_offered_front_coding() {
1653 let mut state = 0x9e37_79b9_7f4a_7c15u64;
1656 let values: Vec<Vec<u8>> = (0..2000)
1657 .map(|_| {
1658 (0..24)
1659 .map(|_| {
1660 state ^= state << 13;
1661 state ^= state >> 7;
1662 state ^= state << 17;
1663 (state % 251) as u8
1664 })
1665 .collect()
1666 })
1667 .collect();
1668 let borrowed = borrow(&values);
1669 assert!(!candidates(&borrowed, 0).contains(&Kind::Front));
1670 }
1671
1672 #[test]
1673 fn a_prefix_longer_than_the_value_before_it_is_an_error() {
1674 let mut bytes = vec![Kind::Front.tag()];
1675 put_u32(&mut bytes, 2);
1676 bytes.extend_from_slice(&integer::encode(&[0, 9]).unwrap());
1677 bytes.extend_from_slice(&encode(&[b"one".as_slice(), b"two".as_slice()]).unwrap());
1678 let error = decode(&bytes).unwrap_err();
1679 assert!(error.message().contains("shares 9 bytes"), "{error}");
1680 }
1681
1682 #[test]
1683 fn a_negative_prefix_is_an_error() {
1684 let mut bytes = vec![Kind::Front.tag()];
1685 put_u32(&mut bytes, 1);
1686 bytes.extend_from_slice(&integer::encode(&[-1]).unwrap());
1687 bytes.extend_from_slice(&encode(&[b"one".as_slice()]).unwrap());
1688 let error = decode(&bytes).unwrap_err();
1689 assert!(error.message().contains("negative shared prefix"), "{error}");
1690 }
1691
1692 #[test]
1693 fn a_negative_length_is_an_error() {
1694 let mut bytes = vec![Kind::Plain.tag()];
1695 put_u32(&mut bytes, 1);
1696 bytes.extend_from_slice(&integer::encode(&[-1]).unwrap());
1697 let error = decode(&bytes).unwrap_err();
1698 assert!(error.message().contains("negative string length"), "{error}");
1699 }
1700
1701 #[test]
1702 fn the_sample_is_spread_across_the_chunk_and_not_taken_from_the_front() {
1703 let mut values: Vec<Vec<u8>> = Vec::new();
1706 for index in 0..20_000 {
1707 let head = if index < 10_000 { "aaaaaaaaaaaaaaaa" } else { "zzzzzzzzzzzzzzzz" };
1708 values.push(format!("{head}/{index:08}").into_bytes());
1709 }
1710 let borrowed = borrow(&values);
1711 let sample = sample_of(&borrowed);
1712 let first_half = sample.iter().filter(|value| value.starts_with(b"aaaa")).count();
1713 let second_half = sample.len() - first_half;
1714 assert!(first_half > 0 && second_half > 0, "{first_half} and {second_half}");
1715 let bytes = round_trip(&values);
1716 let ratio = raw_size(&values) as f64 / bytes.len() as f64;
1717 assert!(ratio > 4.0, "{ratio:.2}x");
1718 }
1719}