Path: blob/main/cranelift/codegen/meta/src/unique_table.rs
1692 views
//! An index-accessed table implementation that avoids duplicate entries.1use std::collections::HashMap;2use std::hash::Hash;3use std::slice;45/// Collect items into the `table` list, removing duplicates.6pub(crate) struct UniqueTable<'entries, T: Eq + Hash> {7table: Vec<&'entries T>,8map: HashMap<&'entries T, usize>,9}1011impl<'entries, T: Eq + Hash> UniqueTable<'entries, T> {12pub fn new() -> Self {13Self {14table: Vec::new(),15map: HashMap::new(),16}17}1819pub fn add(&mut self, entry: &'entries T) -> usize {20match self.map.get(&entry) {21None => {22let i = self.table.len();23self.table.push(entry);24self.map.insert(entry, i);25i26}27Some(&i) => i,28}29}3031pub fn len(&self) -> usize {32self.table.len()33}34pub fn iter(&self) -> slice::Iter<'_, &'entries T> {35self.table.iter()36}37}3839/// A table of sequences which tries to avoid common subsequences.40pub(crate) struct UniqueSeqTable<T: PartialEq + Clone> {41table: Vec<T>,42}4344impl<T: PartialEq + Clone> UniqueSeqTable<T> {45pub fn new() -> Self {46Self { table: Vec::new() }47}48pub fn add(&mut self, values: &[T]) -> usize {49if values.is_empty() {50return 0;51}52if let Some(offset) = find_subsequence(values, &self.table) {53offset54} else {55let table_len = self.table.len();5657// Try to put in common the last elements of the table if they're a prefix of the new58// sequence.59//60// We know there wasn't a full match, so the best prefix we can hope to find contains61// all the values but the last one.62let mut start_from = usize::min(table_len, values.len() - 1);63while start_from != 0 {64// Loop invariant: start_from <= table_len, so table_len - start_from >= 0.65if values[0..start_from] == self.table[table_len - start_from..table_len] {66break;67}68start_from -= 1;69}7071self.table72.extend(values[start_from..values.len()].iter().cloned());73table_len - start_from74}75}76pub fn len(&self) -> usize {77self.table.len()78}79pub fn iter(&self) -> slice::Iter<'_, T> {80self.table.iter()81}82}8384/// Try to find the subsequence `sub` in the `whole` sequence. Returns None if85/// it's not been found, or Some(index) if it has been. Naive implementation86/// until proven we need something better.87fn find_subsequence<T: PartialEq>(sub: &[T], whole: &[T]) -> Option<usize> {88assert!(!sub.is_empty());89// We want i + sub.len() <= whole.len(), i.e. i < whole.len() + 1 - sub.len().90if whole.len() < sub.len() {91return None;92}93let max = whole.len() - sub.len();94for i in 0..=max {95if whole[i..i + sub.len()] == sub[..] {96return Some(i);97}98}99None100}101102#[test]103fn test_find_subsequence() {104assert_eq!(find_subsequence(&vec![1], &vec![4]), None);105assert_eq!(find_subsequence(&vec![1], &vec![1]), Some(0));106assert_eq!(find_subsequence(&vec![1, 2], &vec![1]), None);107assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 2]), Some(0));108assert_eq!(find_subsequence(&vec![1, 2], &vec![1, 3]), None);109assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 2]), Some(1));110assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1]), None);111assert_eq!(find_subsequence(&vec![1, 2], &vec![0, 1, 3, 1, 2]), Some(3));112assert_eq!(113find_subsequence(&vec![1, 1, 3], &vec![1, 1, 1, 3, 3]),114Some(1)115);116}117118#[test]119fn test_optimal_add() {120let mut seq_table = UniqueSeqTable::new();121// [0, 1, 2, 3]122assert_eq!(seq_table.add(&vec![0, 1, 2, 3]), 0);123assert_eq!(seq_table.add(&vec![0, 1, 2, 3]), 0);124assert_eq!(seq_table.add(&vec![1, 2, 3]), 1);125assert_eq!(seq_table.add(&vec![2, 3]), 2);126assert_eq!(seq_table.len(), 4);127// [0, 1, 2, 3, 4]128assert_eq!(seq_table.add(&vec![2, 3, 4]), 2);129assert_eq!(seq_table.len(), 5);130// [0, 1, 2, 3, 4, 6, 5, 7]131assert_eq!(seq_table.add(&vec![4, 6, 5, 7]), 4);132assert_eq!(seq_table.len(), 8);133// [0, 1, 2, 3, 4, 6, 5, 7, 8, 2, 3, 4]134assert_eq!(seq_table.add(&vec![8, 2, 3, 4]), 8);135assert_eq!(seq_table.add(&vec![8]), 8);136assert_eq!(seq_table.len(), 12);137}138139140