use super::encoding::{DIRECT_BOUNDARY_SHIFT, DIRECT_PT_SHIFT, DIRECT_RULE_BIT};
use super::rule::RuleShape;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub(super) enum PatternKind {
Simple = 0,
And = 1,
Not = 2,
}
#[derive(Debug, Clone)]
pub(super) struct PatternEntry {
pub(super) rule_idx: u32,
pub(super) offset: u8,
pub(super) pt_index: u8,
pub(super) kind: PatternKind,
pub(super) shape: RuleShape,
pub(super) boundary: u8,
pub(super) and_count: u8,
}
#[derive(Clone)]
pub(super) struct PatternIndex {
entries: Vec<PatternEntry>,
ranges: Vec<(usize, usize)>,
has_boundary: bool,
}
pub(super) enum PatternDispatch<'a> {
SingleEntry(&'a PatternEntry),
Entries(&'a [PatternEntry]),
}
impl PatternIndex {
pub(super) fn new(dedup_entries: Vec<Vec<PatternEntry>>) -> Self {
let mut entries = Vec::with_capacity(dedup_entries.iter().map(|bucket| bucket.len()).sum());
let mut ranges = Vec::with_capacity(dedup_entries.len());
for bucket in dedup_entries {
let start = entries.len();
let len = bucket.len();
entries.extend(bucket);
ranges.push((start, len));
}
let has_boundary = entries.iter().any(|e| e.boundary != 0);
Self {
entries,
ranges,
has_boundary,
}
}
pub(super) fn has_boundary(&self) -> bool {
self.has_boundary
}
pub(super) fn heap_bytes(&self) -> usize {
self.entries.capacity() * size_of::<PatternEntry>()
+ self.ranges.capacity() * size_of::<(usize, usize)>()
}
#[inline(always)]
pub(super) fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
#[inline(always)]
pub(super) fn all_simple(&self) -> bool {
self.entries
.iter()
.all(|entry| entry.kind == PatternKind::Simple)
&& self.ranges.iter().all(|&(_, len)| len == 1)
}
pub(super) fn build_value_map(&self) -> Vec<u32> {
let mut value_map = Vec::with_capacity(self.ranges.len());
for (dedup_idx, &(start, len)) in self.ranges.iter().enumerate() {
if len == 1 {
let entry = unsafe { self.entries.get_unchecked(start) };
if entry.kind == PatternKind::Simple
&& (entry.pt_index as u32) < 8
&& entry.rule_idx < (1 << DIRECT_BOUNDARY_SHIFT)
{
let encoded = DIRECT_RULE_BIT
| ((entry.pt_index as u32) << DIRECT_PT_SHIFT)
| ((entry.boundary as u32) << DIRECT_BOUNDARY_SHIFT)
| entry.rule_idx;
value_map.push(encoded);
continue;
}
}
value_map.push(dedup_idx as u32);
}
value_map
}
#[inline(always)]
pub(super) fn dispatch_indirect(&self, raw_value: u32) -> PatternDispatch<'_> {
debug_assert!(
raw_value & DIRECT_RULE_BIT == 0,
"dispatch_indirect called with DIRECT_RULE_BIT set"
);
let pattern_idx = raw_value as usize;
debug_assert!(pattern_idx < self.ranges.len());
let &(start, len) = unsafe { self.ranges.get_unchecked(pattern_idx) };
debug_assert!(start + len <= self.entries.len());
if len == 1 {
PatternDispatch::SingleEntry(unsafe { self.entries.get_unchecked(start) })
} else {
PatternDispatch::Entries(unsafe { self.entries.get_unchecked(start..start + len) })
}
}
}
#[cfg(test)]
mod tests {
use super::super::encoding::{DIRECT_PT_MASK, DIRECT_RULE_MASK};
use super::*;
#[test]
fn test_pattern_index_direct_rule_encoding() {
let entries = vec![vec![PatternEntry {
rule_idx: 5,
offset: 0,
pt_index: 2,
kind: PatternKind::Simple,
shape: RuleShape::SingleAnd,
boundary: 0,
and_count: 1,
}]];
let index = PatternIndex::new(entries);
let value_map = index.build_value_map();
assert_eq!(value_map.len(), 1);
let raw = value_map[0];
assert!(raw & DIRECT_RULE_BIT != 0, "should set DIRECT_RULE_BIT");
let rule_idx = (raw & DIRECT_RULE_MASK) as usize;
let pt_index = ((raw & DIRECT_PT_MASK) >> DIRECT_PT_SHIFT) as u8;
assert_eq!(rule_idx, 5);
assert_eq!(pt_index, 2);
}
#[test]
fn test_pattern_index_dispatch_single_entry() {
let entries = vec![vec![PatternEntry {
rule_idx: 0,
offset: 0,
pt_index: 0,
kind: PatternKind::And,
shape: RuleShape::Bitmask,
boundary: 0,
and_count: 2,
}]];
let index = PatternIndex::new(entries);
let value_map = index.build_value_map();
assert!(
value_map[0] & DIRECT_RULE_BIT == 0,
"And kind should not get DIRECT_RULE_BIT"
);
match index.dispatch_indirect(value_map[0]) {
PatternDispatch::SingleEntry(entry) => {
assert_eq!(entry.rule_idx, 0);
assert_eq!(entry.kind, PatternKind::And);
}
_ => panic!("expected SingleEntry dispatch"),
}
}
#[test]
fn test_pattern_index_dispatch_multi_entry() {
let entries = vec![vec![
PatternEntry {
rule_idx: 0,
offset: 0,
pt_index: 0,
kind: PatternKind::Simple,
shape: RuleShape::SingleAnd,
boundary: 0,
and_count: 1,
},
PatternEntry {
rule_idx: 1,
offset: 0,
pt_index: 0,
kind: PatternKind::Simple,
shape: RuleShape::SingleAnd,
boundary: 0,
and_count: 1,
},
]];
let index = PatternIndex::new(entries);
let value_map = index.build_value_map();
assert!(value_map[0] & DIRECT_RULE_BIT == 0);
match index.dispatch_indirect(value_map[0]) {
PatternDispatch::Entries(slice) => assert_eq!(slice.len(), 2),
_ => panic!("expected Entries dispatch"),
}
}
}