use super::BackendError;
pub const REGEX_ACCELERATOR_EVIDENCE_SCHEMA_VERSION: u32 = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RegexAcceleratorClass {
RxpLike,
Dpu,
Fpga,
Software,
}
impl RegexAcceleratorClass {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::RxpLike => "rxp_like",
Self::Dpu => "dpu",
Self::Fpga => "fpga",
Self::Software => "software",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RegexAcceleratorStreamMode {
Unavailable,
Block,
Streaming,
StatefulStreaming,
}
impl RegexAcceleratorStreamMode {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Unavailable => "unavailable",
Self::Block => "block",
Self::Streaming => "streaming",
Self::StatefulStreaming => "stateful_streaming",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RegexAcceleratorMatchSchema {
Unavailable,
Offsets,
PatternIdOffsets,
StreamPatternIdOffsets,
}
impl RegexAcceleratorMatchSchema {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Unavailable => "unavailable",
Self::Offsets => "offsets",
Self::PatternIdOffsets => "pattern_id_offsets",
Self::StreamPatternIdOffsets => "stream_pattern_id_offsets",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegexAcceleratorCapability {
pub backend: &'static str,
pub accelerator_class: RegexAcceleratorClass,
pub supported: bool,
pub device_signature: &'static str,
pub rule_capacity: u32,
pub stream_mode: RegexAcceleratorStreamMode,
pub match_schema: RegexAcceleratorMatchSchema,
pub unsupported_reason: &'static str,
}
impl RegexAcceleratorCapability {
#[must_use]
pub const fn unsupported(
backend: &'static str,
accelerator_class: RegexAcceleratorClass,
unsupported_reason: &'static str,
) -> Self {
Self {
backend,
accelerator_class,
supported: false,
device_signature: "",
rule_capacity: 0,
stream_mode: RegexAcceleratorStreamMode::Unavailable,
match_schema: RegexAcceleratorMatchSchema::Unavailable,
unsupported_reason,
}
}
#[must_use]
pub const fn supported(
backend: &'static str,
accelerator_class: RegexAcceleratorClass,
device_signature: &'static str,
rule_capacity: u32,
stream_mode: RegexAcceleratorStreamMode,
match_schema: RegexAcceleratorMatchSchema,
) -> Self {
Self {
backend,
accelerator_class,
supported: true,
device_signature,
rule_capacity,
stream_mode,
match_schema,
unsupported_reason: "",
}
}
pub fn require_supported(self) -> Result<Self, BackendError> {
if self.supported {
return Ok(self);
}
Err(BackendError::UnsupportedFeature {
name: format!("regex_accelerator:{}", self.accelerator_class.as_str()),
backend: self.backend.to_string(),
})
}
#[must_use]
pub const fn evidence(self, transfer_bytes: u64) -> RegexAcceleratorEvidence {
RegexAcceleratorEvidence {
schema_version: REGEX_ACCELERATOR_EVIDENCE_SCHEMA_VERSION,
backend: self.backend,
accelerator_class: self.accelerator_class,
supported: self.supported,
device_signature: self.device_signature,
rule_capacity: self.rule_capacity,
stream_mode: self.stream_mode,
match_schema: self.match_schema,
unsupported_reason: self.unsupported_reason,
transfer_bytes,
match_parity_required: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegexAcceleratorEvidence {
pub schema_version: u32,
pub backend: &'static str,
pub accelerator_class: RegexAcceleratorClass,
pub supported: bool,
pub device_signature: &'static str,
pub rule_capacity: u32,
pub stream_mode: RegexAcceleratorStreamMode,
pub match_schema: RegexAcceleratorMatchSchema,
pub unsupported_reason: &'static str,
pub transfer_bytes: u64,
pub match_parity_required: bool,
}
impl RegexAcceleratorEvidence {
#[must_use]
pub fn is_complete(self) -> bool {
if self.schema_version != REGEX_ACCELERATOR_EVIDENCE_SCHEMA_VERSION {
return false;
}
if !self.match_parity_required || self.backend.is_empty() {
return false;
}
if self.supported {
!self.device_signature.is_empty()
&& self.rule_capacity != 0
&& self.stream_mode != RegexAcceleratorStreamMode::Unavailable
&& self.match_schema != RegexAcceleratorMatchSchema::Unavailable
&& self.unsupported_reason.is_empty()
} else {
self.device_signature.is_empty()
&& self.rule_capacity == 0
&& self.stream_mode == RegexAcceleratorStreamMode::Unavailable
&& self.match_schema == RegexAcceleratorMatchSchema::Unavailable
&& !self.unsupported_reason.is_empty()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unsupported_regex_accelerator_fails_closed_with_evidence() {
let capability = RegexAcceleratorCapability::unsupported(
"wgpu",
RegexAcceleratorClass::RxpLike,
"backend has no RXP-like regex accelerator",
);
let error = capability
.require_supported()
.expect_err("Fix: unsupported regex accelerator capability must fail closed");
match error {
BackendError::UnsupportedFeature { name, backend } => {
assert_eq!(name, "regex_accelerator:rxp_like");
assert_eq!(backend, "wgpu");
}
other => panic!("expected UnsupportedFeature, got {other:?}"),
}
let evidence = capability.evidence(0);
assert!(!evidence.supported);
assert_eq!(evidence.unsupported_reason, "backend has no RXP-like regex accelerator");
assert_eq!(evidence.stream_mode, RegexAcceleratorStreamMode::Unavailable);
assert_eq!(evidence.match_schema, RegexAcceleratorMatchSchema::Unavailable);
assert!(evidence.match_parity_required);
assert!(evidence.is_complete());
}
#[test]
fn supported_regex_accelerator_reports_device_signature_and_schema() {
let capability = RegexAcceleratorCapability::supported(
"rxp",
RegexAcceleratorClass::RxpLike,
"bluefield-rxp:v1",
4096,
RegexAcceleratorStreamMode::StatefulStreaming,
RegexAcceleratorMatchSchema::StreamPatternIdOffsets,
);
let supported = capability
.require_supported()
.expect("Fix: supported regex accelerator capability should pass");
let evidence = supported.evidence(8192);
assert!(evidence.supported);
assert_eq!(evidence.device_signature, "bluefield-rxp:v1");
assert_eq!(evidence.rule_capacity, 4096);
assert_eq!(
evidence.stream_mode,
RegexAcceleratorStreamMode::StatefulStreaming
);
assert_eq!(
evidence.match_schema,
RegexAcceleratorMatchSchema::StreamPatternIdOffsets
);
assert_eq!(evidence.transfer_bytes, 8192);
assert!(evidence.match_parity_required);
assert!(evidence.is_complete());
}
}