1use crate::{
8 Code, Compressor, FSST_CODE_BASE, FSST_CODE_MASK, Symbol, advance_8byte_word, compare_masked,
9 lossy_pht::LossyPHT,
10};
11use rustc_hash::{FxBuildHasher, FxHashMap};
12use std::cmp::Ordering;
13use std::collections::BinaryHeap;
14
15#[derive(Clone, Copy, Debug, Default)]
17struct CodesBitmap {
18 codes: [u64; 8],
19}
20
21assert_sizeof!(CodesBitmap => 64);
22
23impl CodesBitmap {
24 pub(crate) fn set(&mut self, index: usize) {
26 debug_assert!(
27 index <= FSST_CODE_MASK as usize,
28 "code cannot exceed {FSST_CODE_MASK}"
29 );
30
31 let map = index >> 6;
32 self.codes[map] |= 1 << (index % 64);
33 }
34
35 pub(crate) fn is_set(&self, index: usize) -> bool {
37 debug_assert!(
38 index <= FSST_CODE_MASK as usize,
39 "code cannot exceed {FSST_CODE_MASK}"
40 );
41
42 let map = index >> 6;
43 self.codes[map] & (1 << (index % 64)) != 0
44 }
45
46 pub(crate) fn codes(&self) -> CodesIterator<'_> {
48 CodesIterator {
49 inner: self,
50 index: 0,
51 block: self.codes[0],
52 reference: 0,
53 }
54 }
55
56 pub(crate) fn clear(&mut self) {
58 self.codes[0] = 0;
59 self.codes[1] = 0;
60 self.codes[2] = 0;
61 self.codes[3] = 0;
62 self.codes[4] = 0;
63 self.codes[5] = 0;
64 self.codes[6] = 0;
65 self.codes[7] = 0;
66 }
67}
68
69struct CodesIterator<'a> {
70 inner: &'a CodesBitmap,
71 index: usize,
72 block: u64,
73 reference: usize,
74}
75
76impl Iterator for CodesIterator<'_> {
77 type Item = u16;
78
79 fn next(&mut self) -> Option<Self::Item> {
80 while self.block == 0 {
82 self.index += 1;
83 if self.index >= 8 {
84 return None;
85 }
86 self.block = self.inner.codes[self.index];
87 self.reference = self.index * 64;
88 }
89
90 let position = self.block.trailing_zeros() as usize;
92 let code = self.reference + position;
93
94 if code >= 511 {
95 return None;
96 }
97
98 self.reference = code + 1;
100 self.block = if position == 63 {
101 0
102 } else {
103 self.block >> (1 + position)
104 };
105
106 Some(code as u16)
107 }
108}
109
110#[derive(Debug, Clone)]
111struct Counter {
112 counts1: Vec<usize>,
114
115 counts2: Vec<usize>,
117
118 code1_index: CodesBitmap,
120
121 pair_index: Vec<CodesBitmap>,
126}
127
128const COUNTS1_SIZE: usize = (FSST_CODE_MASK + 1) as usize;
129
130const COUNTS2_SIZE: usize = COUNTS1_SIZE * COUNTS1_SIZE;
136
137impl Counter {
138 fn new() -> Self {
139 let mut counts1 = Vec::with_capacity(COUNTS1_SIZE);
140 let mut counts2 = Vec::with_capacity(COUNTS2_SIZE);
141 unsafe {
144 counts1.set_len(COUNTS1_SIZE);
145 counts2.set_len(COUNTS2_SIZE);
146 }
147
148 Self {
149 counts1,
150 counts2,
151 code1_index: CodesBitmap::default(),
152 pair_index: vec![CodesBitmap::default(); COUNTS1_SIZE],
153 }
154 }
155
156 #[inline]
157 fn record_count1(&mut self, code1: u16) {
158 let base = if self.code1_index.is_set(code1 as usize) {
160 self.counts1[code1 as usize]
161 } else {
162 0
163 };
164
165 self.counts1[code1 as usize] = base + 1;
166 self.code1_index.set(code1 as usize);
167 }
168
169 #[inline]
170 fn record_count2(&mut self, code1: u16, code2: u16) {
171 debug_assert!(code1 == FSST_CODE_MASK || self.code1_index.is_set(code1 as usize));
172 debug_assert!(self.code1_index.is_set(code2 as usize));
173
174 let idx = (code1 as usize) * COUNTS1_SIZE + (code2 as usize);
175 if self.pair_index[code1 as usize].is_set(code2 as usize) {
176 self.counts2[idx] += 1;
177 } else {
178 self.counts2[idx] = 1;
179 }
180 self.pair_index[code1 as usize].set(code2 as usize);
181 }
182
183 #[inline]
184 fn count1(&self, code1: u16) -> usize {
185 debug_assert!(self.code1_index.is_set(code1 as usize));
186
187 self.counts1[code1 as usize]
188 }
189
190 #[inline]
191 fn count2(&self, code1: u16, code2: u16) -> usize {
192 debug_assert!(self.code1_index.is_set(code1 as usize));
193 debug_assert!(self.code1_index.is_set(code2 as usize));
194 debug_assert!(self.pair_index[code1 as usize].is_set(code2 as usize));
195
196 let idx = (code1 as usize) * 512 + (code2 as usize);
197 self.counts2[idx]
198 }
199
200 fn first_codes(&self) -> CodesIterator<'_> {
203 self.code1_index.codes()
204 }
205
206 fn second_codes(&self, code1: u16) -> CodesIterator<'_> {
212 self.pair_index[code1 as usize].codes()
213 }
214
215 fn clear(&mut self) {
218 self.code1_index.clear();
219 for index in &mut self.pair_index {
220 index.clear();
221 }
222 }
223}
224
225pub struct CompressorBuilder {
227 symbols: Vec<Symbol>,
231
232 n_symbols: u8,
235
236 len_histogram: [u8; 8],
240
241 codes_one_byte: Vec<Code>,
245
246 codes_two_byte: Vec<Code>,
248
249 lossy_pht: LossyPHT,
251}
252
253impl CompressorBuilder {
254 pub fn new() -> Self {
256 let symbols = vec![Symbol::ZERO; 511];
260
261 let mut table = Self {
262 symbols,
263 n_symbols: 0,
264 len_histogram: [0; 8],
265 codes_two_byte: Vec::with_capacity(65_536),
266 codes_one_byte: Vec::with_capacity(512),
267 lossy_pht: LossyPHT::new(),
268 };
269
270 for byte in 0..=255 {
272 let symbol = Symbol::from_u8(byte);
273 table.symbols[byte as usize] = symbol;
274 }
275
276 for byte in 0..=255 {
278 table.codes_one_byte.push(Code::new_escape(byte));
280 }
281
282 for idx in 0..=65_535 {
284 table.codes_two_byte.push(Code::new_escape(idx as u8));
285 }
286
287 table
288 }
289}
290
291impl Default for CompressorBuilder {
292 fn default() -> Self {
293 Self::new()
294 }
295}
296
297impl CompressorBuilder {
298 pub fn insert(&mut self, symbol: Symbol, len: usize) -> bool {
309 assert!(self.n_symbols < 255, "cannot insert into full symbol table");
310 assert_eq!(len, symbol.len(), "provided len must equal symbol.len()");
311
312 if len == 2 {
313 self.codes_two_byte[symbol.first2() as usize] =
315 Code::new_symbol_building(self.n_symbols, 2);
316 } else if len == 1 {
317 self.codes_one_byte[symbol.first_byte() as usize] =
319 Code::new_symbol_building(self.n_symbols, 1);
320 } else {
321 if !self.lossy_pht.insert(symbol, len, self.n_symbols) {
323 return false;
324 }
325 }
326
327 self.len_histogram[len - 1] += 1;
329
330 self.symbols[256 + (self.n_symbols as usize)] = symbol;
333 self.n_symbols += 1;
334 true
335 }
336
337 fn clear(&mut self) {
342 for code in 0..(256 + self.n_symbols as usize) {
344 let symbol = self.symbols[code];
345 if symbol.len() == 1 {
346 self.codes_one_byte[symbol.first_byte() as usize] =
348 Code::new_escape(symbol.first_byte());
349 } else if symbol.len() == 2 {
350 self.codes_two_byte[symbol.first2() as usize] =
352 Code::new_escape(symbol.first_byte());
353 } else {
354 self.lossy_pht.remove(symbol);
356 }
357 }
358
359 for i in 0..=7 {
361 self.len_histogram[i] = 0;
362 }
363
364 self.n_symbols = 0;
365 }
366
367 fn finalize(&mut self) -> (u8, Vec<u8>) {
386 let byte_lim = self.n_symbols - self.len_histogram[0];
391
392 let mut codes_by_length = [0u8; 8];
396 codes_by_length[0] = byte_lim;
397 codes_by_length[1] = 0;
398
399 for i in 1..7 {
401 codes_by_length[i + 1] = codes_by_length[i] + self.len_histogram[i];
402 }
403
404 let mut no_suffix_code = 0;
408
409 let mut has_suffix_code = codes_by_length[2];
411
412 let mut new_codes = [0u8; FSST_CODE_BASE as usize];
415
416 let mut symbol_lens = [0u8; FSST_CODE_BASE as usize];
417
418 for i in 0..(self.n_symbols as usize) {
419 let symbol = self.symbols[256 + i];
420 let len = symbol.len();
421 if len == 2 {
422 let has_suffix = self
423 .symbols
424 .iter()
425 .skip(FSST_CODE_BASE as usize)
426 .enumerate()
427 .any(|(k, other)| i != k && symbol.first2() == other.first2());
428
429 if has_suffix {
430 has_suffix_code -= 1;
432 new_codes[i] = has_suffix_code;
433 } else {
434 new_codes[i] = no_suffix_code;
437 no_suffix_code += 1;
438 }
439 } else {
440 new_codes[i] = codes_by_length[len - 1];
442 codes_by_length[len - 1] += 1;
443 }
444
445 self.symbols[new_codes[i] as usize] = symbol;
448 symbol_lens[new_codes[i] as usize] = len as u8;
449 }
450
451 self.symbols.truncate(self.n_symbols as usize);
453
454 for byte in 0..=255 {
457 let one_byte = self.codes_one_byte[byte];
458 if one_byte.extended_code() >= FSST_CODE_BASE {
459 let new_code = new_codes[one_byte.code() as usize];
460 self.codes_one_byte[byte] = Code::new_symbol(new_code, 1);
461 } else {
462 self.codes_one_byte[byte] = Code::UNUSED;
464 }
465 }
466
467 for two_bytes in 0..=65_535 {
470 let two_byte = self.codes_two_byte[two_bytes];
471 if two_byte.extended_code() >= FSST_CODE_BASE {
472 let new_code = new_codes[two_byte.code() as usize];
473 self.codes_two_byte[two_bytes] = Code::new_symbol(new_code, 2);
474 } else {
475 self.codes_two_byte[two_bytes] = self.codes_one_byte[two_bytes & 0xFF];
477 }
478 }
479
480 self.lossy_pht.renumber(&new_codes);
482
483 let mut lengths = Vec::with_capacity(self.n_symbols as usize);
485 for symbol in &self.symbols {
486 lengths.push(symbol.len() as u8);
487 }
488
489 (has_suffix_code, lengths)
490 }
491
492 pub fn build(mut self) -> Compressor {
494 let (has_suffix_code, lengths) = self.finalize();
498
499 Compressor {
500 symbols: self.symbols,
501 lengths,
502 n_symbols: self.n_symbols,
503 has_suffix_code,
504 codes_two_byte: self.codes_two_byte,
505 lossy_pht: self.lossy_pht,
506 }
507 }
508}
509
510#[cfg(not(miri))]
514const GENERATIONS: [usize; 5] = [8usize, 38, 68, 98, 128];
515#[cfg(miri)]
516const GENERATIONS: [usize; 3] = [8usize, 38, 128];
517
518const FSST_SAMPLETARGET: usize = 1 << 14;
519const FSST_SAMPLELINE: usize = 512;
520
521fn make_sample<'a>(str_in: &[&'a [u8]], tot_size: usize) -> Vec<&'a [u8]> {
526 let mut sample: Vec<&[u8]> = Vec::new();
527
528 if tot_size < FSST_SAMPLETARGET {
529 return str_in.to_vec();
530 }
531
532 let mut sample_rnd = fsst_hash(4637947);
533 let sample_lim = FSST_SAMPLETARGET;
534
535 let mut sample_size = 0;
536
537 while sample_size < sample_lim {
538 sample_rnd = fsst_hash(sample_rnd);
539 let line_nr = (sample_rnd as usize) % str_in.len();
540
541 let Some(line) = (line_nr..str_in.len())
544 .chain(0..line_nr)
545 .map(|line_nr| str_in[line_nr])
546 .find(|line| !line.is_empty())
547 else {
548 return sample;
549 };
550
551 let chunks = 1 + ((line.len() - 1) / FSST_SAMPLELINE);
552 sample_rnd = fsst_hash(sample_rnd);
553 let chunk = FSST_SAMPLELINE * ((sample_rnd as usize) % chunks);
554
555 let len = FSST_SAMPLELINE.min(line.len() - chunk);
556
557 sample.push(&line[chunk..chunk + len]);
558 sample_size += len;
559 }
560
561 sample
562}
563
564#[inline]
568pub(crate) fn fsst_hash(value: u64) -> u64 {
569 value.wrapping_mul(2971215073) ^ value.wrapping_shr(15)
570}
571
572impl Compressor {
573 pub fn train(values: &Vec<&[u8]>) -> Self {
583 let mut builder = CompressorBuilder::new();
584
585 if values.is_empty() {
586 return builder.build();
587 }
588
589 let mut counters = Counter::new();
590 let mut pqueue = BinaryHeap::with_capacity(65_536);
591
592 let tot_size: usize = values.iter().map(|s| s.len()).sum();
593 let sampled = tot_size >= FSST_SAMPLETARGET;
594 let sample = make_sample(values, tot_size);
595 for sample_frac in GENERATIONS {
596 for (i, line) in sample.iter().enumerate() {
597 if sample_frac < 128 && ((fsst_hash(i as u64) & 127) as usize) > sample_frac {
598 continue;
599 }
600
601 builder.compress_count(line, &mut counters);
602 }
603
604 pqueue.clear();
606 let prune = sample_frac >= 128 && !sampled;
607 builder.optimize(&counters, sample_frac, &mut pqueue, prune);
608 counters.clear();
609 }
610
611 builder.build()
612 }
613}
614
615impl CompressorBuilder {
616 fn find_longest_symbol(&self, word: u64) -> Code {
618 let entry = self.lossy_pht.lookup(word);
620 let ignored_bits = entry.ignored_bits;
621
622 if !entry.is_unused() && compare_masked(word, entry.symbol.to_u64(), ignored_bits) {
624 return entry.code;
625 }
626
627 let twobyte = self.codes_two_byte[word as u16 as usize];
629 if twobyte.extended_code() >= FSST_CODE_BASE {
630 return twobyte;
631 }
632
633 self.codes_one_byte[word as u8 as usize]
635 }
636
637 fn compress_count(&self, sample: &[u8], counter: &mut Counter) -> usize {
643 let mut gain = 0;
644 if sample.is_empty() {
645 return gain;
646 }
647
648 let mut in_ptr = sample.as_ptr();
649
650 let in_end = unsafe { in_ptr.byte_add(sample.len()) };
652 let in_end_sub8 = in_end as usize - 8;
653
654 let mut prev_code: u16 = FSST_CODE_MASK;
655
656 while (in_ptr as usize) < (in_end_sub8) {
657 let word: u64 = unsafe { std::ptr::read_unaligned(in_ptr as *const u64) };
659 let code = self.find_longest_symbol(word);
660 let code_u16 = code.extended_code();
661
662 gain += (code.len() as usize) - ((code_u16 < 256) as usize);
665
666 counter.record_count1(code_u16);
668 counter.record_count2(prev_code, code_u16);
669
670 if code.len() > 1 {
673 let code_first_byte = self.symbols[code_u16 as usize].first_byte() as u16;
674 counter.record_count1(code_first_byte);
675 counter.record_count2(prev_code, code_first_byte);
676 }
677
678 in_ptr = unsafe { in_ptr.byte_add(code.len() as usize) };
680
681 prev_code = code_u16;
682 }
683
684 let remaining_bytes = unsafe { in_end.byte_offset_from(in_ptr) };
685 assert!(
686 remaining_bytes.is_positive(),
687 "in_ptr exceeded in_end, should not be possible"
688 );
689 let remaining_bytes = remaining_bytes as usize;
690
691 let mut bytes = [0u8; 8];
695 unsafe {
696 std::ptr::copy_nonoverlapping(in_ptr, bytes.as_mut_ptr(), remaining_bytes);
699 }
700 let mut last_word = u64::from_le_bytes(bytes);
701
702 let mut remaining_bytes = remaining_bytes;
703
704 while remaining_bytes > 0 {
705 let code = self.find_longest_symbol(last_word);
707 let code_u16 = code.extended_code();
708
709 gain += (code.len() as usize) - ((code_u16 < 256) as usize);
712
713 counter.record_count1(code_u16);
715 counter.record_count2(prev_code, code_u16);
716
717 if code.len() > 1 {
720 let code_first_byte = self.symbols[code_u16 as usize].first_byte() as u16;
721 counter.record_count1(code_first_byte);
722 counter.record_count2(prev_code, code_first_byte);
723 }
724
725 let advance = code.len() as usize;
727 remaining_bytes -= advance;
728 last_word = advance_8byte_word(last_word, advance);
729
730 prev_code = code_u16;
731 }
732
733 gain
734 }
735
736 fn optimize(
739 &mut self,
740 counters: &Counter,
741 sample_frac: usize,
742 pqueue: &mut BinaryHeap<Candidate>,
743 prune: bool,
744 ) {
745 let mut candidates = FxHashMap::with_capacity_and_hasher(256, FxBuildHasher);
750
751 for code1 in counters.first_codes() {
752 let symbol1 = self.symbols[code1 as usize];
753 let symbol1_len = symbol1.len();
754 let count = counters.count1(code1);
755
756 let min_count = if prune { 1 } else { 5 * sample_frac / 128 };
761 if count < min_count {
762 continue;
763 }
764
765 let mut gain = count * symbol1_len;
766 if symbol1_len == 1 {
769 gain *= 8;
770 }
771
772 *candidates.entry(symbol1).or_insert(0) += gain;
774
775 if sample_frac >= 128 || symbol1_len == 8 {
777 continue;
778 }
779
780 for code2 in counters.second_codes(code1) {
781 let symbol2 = self.symbols[code2 as usize];
782
783 if symbol1_len + symbol2.len() > 8 {
785 continue;
786 }
787 let new_symbol = symbol1.concat(symbol2);
788 let gain = counters.count2(code1, code2) * new_symbol.len();
789
790 *candidates.entry(new_symbol).or_insert(0) += gain;
792 }
793 }
794
795 for (symbol, gain) in candidates {
797 pqueue.push(Candidate { symbol, gain });
798 }
799
800 self.clear();
802
803 let mut n_symbols = 0;
805 while !pqueue.is_empty() && n_symbols < 255 {
806 let candidate = pqueue.pop().unwrap();
807 if prune {
808 let symbol_len = candidate.symbol.len();
809 let saves = if symbol_len == 1 {
810 candidate.gain / 8 } else {
812 candidate.gain
813 };
814 if saves <= symbol_len + 1 {
815 continue;
816 }
817 }
818 if self.insert(candidate.symbol, candidate.symbol.len()) {
819 n_symbols += 1;
820 }
821 }
822 }
823}
824
825#[derive(Copy, Clone, Debug)]
829struct Candidate {
830 gain: usize,
831 symbol: Symbol,
832}
833
834impl Candidate {
835 fn comparable_form(&self) -> (usize, usize) {
836 (self.gain, self.symbol.len())
837 }
838}
839
840impl Eq for Candidate {}
841
842impl PartialEq<Self> for Candidate {
843 fn eq(&self, other: &Self) -> bool {
844 self.comparable_form().eq(&other.comparable_form())
845 }
846}
847
848impl PartialOrd<Self> for Candidate {
849 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
850 Some(self.cmp(other))
851 }
852}
853
854impl Ord for Candidate {
855 fn cmp(&self, other: &Self) -> Ordering {
856 let self_ord = (self.gain, self.symbol.len());
857 let other_ord = (other.gain, other.symbol.len());
858
859 self_ord.cmp(&other_ord)
860 }
861}
862
863#[cfg(test)]
864mod test {
865 use crate::{Compressor, ESCAPE_CODE, builder::CodesBitmap};
866
867 #[test]
868 fn test_builder() {
869 let text = b"hello hello hello hello hello";
871
872 let table = Compressor::train(&vec![text, text, text, text, text]);
874
875 let compressed = table.compress(text);
877
878 assert!(compressed.iter().all(|b| *b != ESCAPE_CODE));
880
881 let compressed = table.compress("xyz123".as_bytes());
883 let decompressed = table.decompressor().decompress(&compressed);
884 assert_eq!(&decompressed, b"xyz123");
885 assert_eq!(
886 compressed,
887 vec![
888 ESCAPE_CODE,
889 b'x',
890 ESCAPE_CODE,
891 b'y',
892 ESCAPE_CODE,
893 b'z',
894 ESCAPE_CODE,
895 b'1',
896 ESCAPE_CODE,
897 b'2',
898 ESCAPE_CODE,
899 b'3',
900 ]
901 );
902 }
903
904 #[test]
905 fn test_bitmap() {
906 let mut map = CodesBitmap::default();
907 map.set(10);
908 map.set(100);
909 map.set(500);
910
911 let codes: Vec<u16> = map.codes().collect();
912 assert_eq!(codes, vec![10u16, 100, 500]);
913
914 let map = CodesBitmap::default();
916 assert!(map.codes().collect::<Vec<_>>().is_empty());
917
918 let mut map = CodesBitmap::default();
920 (0..8).for_each(|i| map.set(64 * i));
921 assert_eq!(
922 map.codes().collect::<Vec<_>>(),
923 (0u16..8).map(|i| 64 * i).collect::<Vec<_>>(),
924 );
925
926 let mut map = CodesBitmap::default();
928 for i in 0..512 {
929 map.set(i);
930 }
931 assert_eq!(
932 map.codes().collect::<Vec<_>>(),
933 (0u16..511u16).collect::<Vec<_>>()
934 );
935 }
936
937 #[test]
938 #[should_panic(expected = "code cannot exceed")]
939 fn test_bitmap_invalid() {
940 let mut map = CodesBitmap::default();
941 map.set(512);
942 }
943
944 #[test]
945 fn test_no_duplicate_symbols() {
946 let text = b"aababcabcdabcde";
948 let corpus: Vec<&[u8]> = std::iter::repeat_n(text.as_slice(), 100).collect();
949 let compressor = Compressor::train(&corpus);
950
951 let symbols = compressor.symbol_table();
952 let lengths = compressor.symbol_lengths();
953
954 let one_byte: Vec<u8> = symbols
956 .iter()
957 .zip(lengths.iter())
958 .filter(|&(_, &len)| len == 1)
959 .map(|(sym, _)| sym.first_byte())
960 .collect();
961 let mut one_byte_sorted = one_byte.clone();
962 one_byte_sorted.sort();
963 one_byte_sorted.dedup();
964 assert_eq!(
965 one_byte.len(),
966 one_byte_sorted.len(),
967 "duplicate 1-byte symbols found"
968 );
969
970 let two_byte: Vec<u16> = symbols
972 .iter()
973 .zip(lengths.iter())
974 .filter(|&(_, &len)| len == 2)
975 .map(|(sym, _)| sym.first2())
976 .collect();
977 let mut two_byte_sorted = two_byte.clone();
978 two_byte_sorted.sort();
979 two_byte_sorted.dedup();
980 assert_eq!(
981 two_byte.len(),
982 two_byte_sorted.len(),
983 "duplicate 2-byte symbols found"
984 );
985 }
986}