#![cfg_attr(docsrs, feature(doc_cfg))]
extern crate self as comtains;
pub use comtains_macros::byte_set;
#[derive(Clone, Copy, Debug)]
pub struct ByteSet {
pub(crate) contains: fn(&[u8]) -> bool,
pub(crate) len: usize,
#[allow(dead_code)]
pub(crate) metadata: &'static debug::ByteSetMetadata,
}
impl ByteSet {
#[doc(hidden)]
pub const fn __from_parts(
contains: fn(&[u8]) -> bool,
len: usize,
metadata: &'static debug::ByteSetMetadata,
) -> Self {
Self {
contains,
len,
metadata,
}
}
#[inline(always)]
pub fn contains(&self, candidate: &[u8]) -> bool {
(self.contains)(candidate)
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[allow(dead_code)]
pub(crate) fn debug_nodes(&self) -> &'static [debug::DebugNode] {
self.metadata.nodes
}
#[allow(dead_code)]
pub(crate) fn debug_edges(&self) -> &'static [debug::DebugEdge] {
self.metadata.edges
}
}
#[allow(dead_code)]
pub mod debug {
#[derive(Clone, Copy, Debug)]
pub struct DebugNode {
pub terminal: bool,
pub child_start: usize,
pub child_len: usize,
}
#[derive(Clone, Copy, Debug)]
pub struct DebugEdge {
pub byte: u8,
pub target: usize,
pub weight: usize,
}
#[derive(Clone, Copy, Debug)]
pub struct ByteSetMetadata {
pub nodes: &'static [DebugNode],
pub edges: &'static [DebugEdge],
}
pub const EMPTY_NODES: [DebugNode; 0] = [];
pub const EMPTY_EDGES: [DebugEdge; 0] = [];
}
#[cfg(test)]
mod tests {
use super::*;
const EXAMPLE_SET: ByteSet =
byte_set![b"\xA0\xB1", b"\xA1\xB2", b"\xA1\xB2\xC3", b"\xA1\xB2\xC4"];
#[test]
fn finds_present_sequences() {
assert!(EXAMPLE_SET.contains(b"\xA0\xB1"));
assert!(EXAMPLE_SET.contains(b"\xA1\xB2"));
assert!(EXAMPLE_SET.contains(b"\xA1\xB2\xC3"));
assert!(EXAMPLE_SET.contains(b"\xA1\xB2\xC4"));
}
#[test]
fn rejects_missing_sequences() {
assert!(!EXAMPLE_SET.contains(b""));
assert!(!EXAMPLE_SET.contains(b"\xA1"));
assert!(!EXAMPLE_SET.contains(b"\xA1\xB2\x00"));
assert!(!EXAMPLE_SET.contains(b"\xA1\xB3"));
assert!(!EXAMPLE_SET.contains(b"\xA1\xB2\xC5"));
}
#[test]
fn exposes_weighted_trie_structure() {
let nodes = EXAMPLE_SET.debug_nodes();
let edges = EXAMPLE_SET.debug_edges();
assert!(
nodes.len() >= 3,
"expected at least three nodes in the trie"
);
let root = &nodes[0];
assert!(
root.child_len >= 2,
"root should branch to shared A0 and A1 prefixes"
);
let first_child = &edges[root.child_start];
let second_child = &edges[root.child_start + 1];
assert_eq!(first_child.byte, 0xA1, "most common prefix should be A1");
assert_eq!(
first_child.weight, 3,
"A1 branch should cover three sequences"
);
assert_eq!(
second_child.byte, 0xA0,
"less common branch should come second"
);
assert_eq!(
second_child.weight, 1,
"A0 branch should cover one sequence"
);
let shared_node = &nodes[first_child.target];
assert!(
shared_node.child_len >= 1,
"branch A1 should have further children"
);
let mut found_b2 = false;
let mut idx = shared_node.child_start;
while idx < shared_node.child_start + shared_node.child_len {
if edges[idx].byte == 0xB2 {
assert_eq!(
edges[idx].weight, 3,
"A1B2 should be shared by all remaining sequences"
);
found_b2 = true;
break;
}
idx += 1;
}
assert!(found_b2, "expected a child edge for byte B2 after A1");
}
#[test]
fn supports_string_literals() {
const STRING_SET: ByteSet = byte_set!["AA", "AB", "ABC"];
assert!(STRING_SET.contains(b"AA"));
assert!(STRING_SET.contains(b"AB"));
assert!(!STRING_SET.contains(b"A"));
assert!(!STRING_SET.contains(b"AC"));
}
#[test]
fn supports_array_literals() {
const ARRAY_SET: ByteSet = byte_set![[0x10, 0x20], [0x10, 0x20, 0x30]];
assert!(ARRAY_SET.contains(&[0x10, 0x20]));
assert!(ARRAY_SET.contains(&[0x10, 0x20, 0x30]));
assert!(!ARRAY_SET.contains(&[0x10]));
assert!(!ARRAY_SET.contains(&[0x10, 0x30]));
}
}