vyre 0.3.0

GPU bytecode condition engine
Documentation
use crate::bytecode::Program;
use crate::pattern::{
    CompiledPattern, PatternMapping, RuleEntry, RuleMatch, MAX_CACHED_POSITIONS, MAX_RULE_STRINGS,
};

/// Configuration for building a compiled rule index.
#[derive(Debug, Clone, Copy)]
pub struct IndexConfig {
    /// Maximum strings per rule to provision in GPU layouts.
    pub max_strings: usize,
    /// Maximum cached positions per string.
    pub max_positions: usize,
}

impl IndexConfig {
    /// Configuration for compact fingerprint-style rulesets.
    pub fn fingerprint_tier() -> Self { Self { max_strings: 8, max_positions: 1 } }
    /// Configuration for behavioral rulesets.
    pub fn behavioral_tier() -> Self { Self { max_strings: 32, max_positions: 16 } }
    /// Configuration for maximal forensic fidelity.
    pub fn forensic_tier() -> Self { Self { max_strings: 256, max_positions: 256 } }
}

/// GPU-ready compiled rule index.
#[derive(Debug, Clone)]
pub struct CompiledRuleIndex {
    pub(crate) rules: Vec<RuleEntry>,
    pub(crate) patterns: Vec<CompiledPattern>,
    pub(crate) mapping: PatternMapping,
    pub(crate) programs: Vec<Program>,
    pub(crate) pattern_set: warpstate::PatternSet,
    pub(crate) max_cached_positions: usize,
    pub(crate) max_strings_per_rule: usize,
}

impl CompiledRuleIndex {
    /// Build an index from pre-compiled components.
    ///
    /// # Panics
    ///
    /// Panics in debug builds if `programs.len() != rules.len()` or if any
    /// program fails structural validation.
    pub fn build(
        rules: Vec<RuleEntry>,
        patterns: Vec<CompiledPattern>,
        mapping: PatternMapping,
        programs: Vec<Program>,
        pattern_set: warpstate::PatternSet,
    ) -> Self {
        debug_assert_eq!(
            programs.len(),
            rules.len(),
            "CompiledRuleIndex requires one program per rule"
        );
        for (i, program) in programs.iter().enumerate() {
            debug_assert!(
                program.validate().is_ok(),
                "program {i} failed validation: {:?}",
                program.validate().err()
            );
        }
        Self {
            rules,
            patterns,
            mapping,
            programs,
            pattern_set,
            max_cached_positions: MAX_CACHED_POSITIONS,
            max_strings_per_rule: MAX_RULE_STRINGS,
        }
    }

    /// Apply a configuration profile to the index.
    pub fn with_config(mut self, config: IndexConfig) -> Self {
        self.max_cached_positions = config.max_positions;
        self.max_strings_per_rule = config.max_strings;
        self
    }

    /// Return the configured cached-position limit.
    pub fn max_cached_positions(&self) -> usize { self.max_cached_positions }
    /// Return the configured string-capacity limit.
    pub fn max_strings_per_rule(&self) -> usize { self.max_strings_per_rule }
    /// Return the number of compiled rules.
    pub fn rule_count(&self) -> usize { self.rules.len() }
    /// Borrow rule metadata.
    pub fn rules(&self) -> &[RuleEntry] { &self.rules }
    /// Borrow compiled patterns.
    pub fn patterns(&self) -> &[CompiledPattern] { &self.patterns }
    /// Borrow scatter mappings.
    pub fn mapping(&self) -> &PatternMapping { &self.mapping }
    /// Borrow compiled programs.
    pub fn programs(&self) -> &[Program] { &self.programs }
    /// Borrow the matcher pattern set.
    pub fn pattern_set(&self) -> &warpstate::PatternSet { &self.pattern_set }

    pub(crate) fn materialize_matches(&self, hits: &[bool]) -> Vec<RuleMatch> {
        hits.iter()
            .enumerate()
            .filter(|(_, hit)| **hit)
            .map(|(rule_id, _)| RuleMatch {
                rule_name: self.rules[rule_id].name.clone(),
                tags: self.rules[rule_id].tags.clone(),
            })
            .collect()
    }
}