use std::cmp;
use std::collections::BTreeMap;
use std::fmt;
use packed::pattern::{PatternID, Patterns};
use packed::teddy::Teddy;
#[derive(Clone, Debug)]
pub struct Builder {
fat: Option<bool>,
avx: Option<bool>,
}
impl Default for Builder {
fn default() -> Builder {
Builder::new()
}
}
impl Builder {
pub fn new() -> Builder {
Builder { fat: None, avx: None }
}
pub fn build(&self, patterns: &Patterns) -> Option<Teddy> {
self.build_imp(patterns)
}
pub fn fat(&mut self, yes: Option<bool>) -> &mut Builder {
self.fat = yes;
self
}
pub fn avx(&mut self, yes: Option<bool>) -> &mut Builder {
self.avx = yes;
self
}
fn build_imp(&self, patterns: &Patterns) -> Option<Teddy> {
use packed::teddy::runtime;
if patterns.len() > 64 {
return None;
}
let has_ssse3 = is_x86_feature_detected!("ssse3");
let has_avx = is_x86_feature_detected!("avx2");
let avx = if self.avx == Some(true) {
if !has_avx {
return None;
}
true
} else if self.avx == Some(false) {
if !has_ssse3 {
return None;
}
false
} else if !has_ssse3 && !has_avx {
return None;
} else {
has_avx
};
let fat = match self.fat {
None => avx && patterns.len() > 32,
Some(false) => false,
Some(true) if !avx => return None,
Some(true) => true,
};
let mut compiler = Compiler::new(patterns, fat);
compiler.compile();
let Compiler { buckets, masks, .. } = compiler;
match (masks.len(), avx, fat) {
(1, false, _) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddySlim1Mask128(
runtime::TeddySlim1Mask128 {
mask1: runtime::Mask128::new(masks[0]),
},
),
}),
(1, true, false) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddySlim1Mask256(
runtime::TeddySlim1Mask256 {
mask1: runtime::Mask256::new(masks[0]),
},
),
}),
(1, true, true) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddyFat1Mask256(
runtime::TeddyFat1Mask256 {
mask1: runtime::Mask256::new(masks[0]),
},
),
}),
(2, false, _) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddySlim2Mask128(
runtime::TeddySlim2Mask128 {
mask1: runtime::Mask128::new(masks[0]),
mask2: runtime::Mask128::new(masks[1]),
},
),
}),
(2, true, false) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddySlim2Mask256(
runtime::TeddySlim2Mask256 {
mask1: runtime::Mask256::new(masks[0]),
mask2: runtime::Mask256::new(masks[1]),
},
),
}),
(2, true, true) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddyFat2Mask256(
runtime::TeddyFat2Mask256 {
mask1: runtime::Mask256::new(masks[0]),
mask2: runtime::Mask256::new(masks[1]),
},
),
}),
(3, false, _) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddySlim3Mask128(
runtime::TeddySlim3Mask128 {
mask1: runtime::Mask128::new(masks[0]),
mask2: runtime::Mask128::new(masks[1]),
mask3: runtime::Mask128::new(masks[2]),
},
),
}),
(3, true, false) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddySlim3Mask256(
runtime::TeddySlim3Mask256 {
mask1: runtime::Mask256::new(masks[0]),
mask2: runtime::Mask256::new(masks[1]),
mask3: runtime::Mask256::new(masks[2]),
},
),
}),
(3, true, true) => Some(Teddy {
buckets,
max_pattern_id: patterns.max_pattern_id(),
exec: runtime::Exec::TeddyFat3Mask256(
runtime::TeddyFat3Mask256 {
mask1: runtime::Mask256::new(masks[0]),
mask2: runtime::Mask256::new(masks[1]),
mask3: runtime::Mask256::new(masks[2]),
},
),
}),
_ => unreachable!(),
}
}
}
#[derive(Clone)]
struct Compiler<'p> {
patterns: &'p Patterns,
buckets: Vec<Vec<PatternID>>,
masks: Vec<Mask>,
}
impl<'p> Compiler<'p> {
fn new(patterns: &'p Patterns, fat: bool) -> Compiler<'p> {
let mask_len = cmp::min(3, patterns.minimum_len());
assert!(1 <= mask_len && mask_len <= 3);
Compiler {
patterns,
buckets: vec![vec![]; if fat { 16 } else { 8 }],
masks: vec![Mask::default(); mask_len],
}
}
fn compile(&mut self) {
let mut lonibble_to_bucket: BTreeMap<Vec<u8>, usize> = BTreeMap::new();
for (id, pattern) in self.patterns.iter() {
let lonybs = pattern.low_nybbles(self.masks.len());
if let Some(&bucket) = lonibble_to_bucket.get(&lonybs) {
self.buckets[bucket].push(id);
} else {
let bucket = (self.buckets.len() - 1)
- (id as usize % self.buckets.len());
self.buckets[bucket].push(id);
lonibble_to_bucket.insert(lonybs, bucket);
}
}
for (bucket_index, bucket) in self.buckets.iter().enumerate() {
for &pat_id in bucket {
let pat = self.patterns.get(pat_id);
for (i, mask) in self.masks.iter_mut().enumerate() {
if self.buckets.len() == 8 {
mask.add_slim(bucket_index as u8, pat.bytes()[i]);
} else {
mask.add_fat(bucket_index as u8, pat.bytes()[i]);
}
}
}
}
}
}
impl<'p> fmt::Debug for Compiler<'p> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut buckets = vec![vec![]; self.buckets.len()];
for (i, bucket) in self.buckets.iter().enumerate() {
for &patid in bucket {
buckets[i].push(self.patterns.get(patid));
}
}
f.debug_struct("Compiler")
.field("buckets", &buckets)
.field("masks", &self.masks)
.finish()
}
}
#[derive(Clone, Copy, Default)]
pub struct Mask {
lo: [u8; 32],
hi: [u8; 32],
}
impl Mask {
fn add_slim(&mut self, bucket: u8, byte: u8) {
assert!(bucket < 8);
let byte_lo = (byte & 0xF) as usize;
let byte_hi = ((byte >> 4) & 0xF) as usize;
self.lo[byte_lo] |= 1 << bucket;
self.lo[byte_lo + 16] |= 1 << bucket;
self.hi[byte_hi] |= 1 << bucket;
self.hi[byte_hi + 16] |= 1 << bucket;
}
fn add_fat(&mut self, bucket: u8, byte: u8) {
assert!(bucket < 16);
let byte_lo = (byte & 0xF) as usize;
let byte_hi = ((byte >> 4) & 0xF) as usize;
if bucket < 8 {
self.lo[byte_lo] |= 1 << bucket;
self.hi[byte_hi] |= 1 << bucket;
} else {
self.lo[byte_lo + 16] |= 1 << (bucket % 8);
self.hi[byte_hi + 16] |= 1 << (bucket % 8);
}
}
pub fn lo128(&self) -> [u8; 16] {
let mut tmp = [0; 16];
tmp.copy_from_slice(&self.lo[..16]);
tmp
}
pub fn lo256(&self) -> [u8; 32] {
self.lo
}
pub fn hi128(&self) -> [u8; 16] {
let mut tmp = [0; 16];
tmp.copy_from_slice(&self.hi[..16]);
tmp
}
pub fn hi256(&self) -> [u8; 32] {
self.hi
}
}
impl fmt::Debug for Mask {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (mut parts_lo, mut parts_hi) = (vec![], vec![]);
for i in 0..32 {
parts_lo.push(format!("{:02}: {:08b}", i, self.lo[i]));
parts_hi.push(format!("{:02}: {:08b}", i, self.hi[i]));
}
f.debug_struct("Mask")
.field("lo", &parts_lo)
.field("hi", &parts_hi)
.finish()
}
}