use alloc::borrow::Cow;
use icu_provider::prelude::*;
use regex_automata::dfa::sparse::DFA;
#[derive(Clone, Debug, yoke::Yokeable, zerofrom::ZeroFrom)]
pub struct SerdeDFA<'data> {
dfa_bytes: Cow<'data, [u8]>,
pattern: Option<Cow<'data, str>>,
}
impl PartialEq for SerdeDFA<'_> {
fn eq(&self, other: &Self) -> bool {
self.dfa_bytes == other.dfa_bytes
}
}
#[cfg(feature = "datagen")]
impl databake::Bake for SerdeDFA<'_> {
fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream {
env.insert("icu_list");
let le_bytes = databake::Bake::bake(&self.deref().to_bytes_little_endian().as_slice(), env);
let be_bytes = databake::Bake::bake(&self.deref().to_bytes_big_endian().as_slice(), env);
databake::quote! {
unsafe {
icu_list::provider::SerdeDFA::from_dfa_bytes_unchecked(
if cfg!(target_endian = "little") {
#le_bytes
} else {
#be_bytes
}
)
}
}
}
}
#[cfg(feature = "datagen")]
impl databake::BakeSize for SerdeDFA<'_> {
fn borrows_size(&self) -> usize {
self.deref().write_to_len()
}
}
#[cfg(feature = "datagen")]
impl serde::Serialize for SerdeDFA<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
if serializer.is_human_readable() {
self.pattern
.as_ref()
.map(|pattern| pattern.serialize(serializer))
.unwrap_or_else(|| {
use serde::ser::Error;
Err(S::Error::custom(
"cannot serialize a binary-deserialized SerdeDFA to JSON",
))
})
} else {
serializer.serialize_bytes(&self.deref().to_bytes_little_endian())
}
}
}
#[cfg(feature = "serde")]
impl<'data> SerdeDFA<'data> {
pub fn maybe_deserialize<'de: 'data, D>(deserializer: D) -> Result<Option<Self>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use icu_provider::serde_borrow_de_utils::CowBytesWrap;
use serde::Deserialize;
#[cfg(feature = "serde_human")]
if deserializer.is_human_readable() {
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
use serde::de::Error;
return SerdeDFA::new(Cow::<str>::deserialize(deserializer)?)
.map(Some)
.map_err(|e| D::Error::custom(e.to_string()));
}
let dfa_bytes = <CowBytesWrap<'de>>::deserialize(deserializer)?.0;
if cfg!(target_endian = "big") {
return Ok(None);
}
DFA::from_bytes(&dfa_bytes).map_err(|e| {
use serde::de::Error;
D::Error::custom(alloc::format!("Invalid DFA bytes: {e}"))
})?;
Ok(Some(SerdeDFA {
dfa_bytes,
pattern: None,
}))
}
}
impl<'data> SerdeDFA<'data> {
pub const unsafe fn from_dfa_bytes_unchecked(dfa_bytes: &'data [u8]) -> Self {
Self {
dfa_bytes: Cow::Borrowed(dfa_bytes),
pattern: None,
}
}
#[cfg(any(feature = "datagen", feature = "serde_human",))]
pub fn new(pattern: Cow<'data, str>) -> Result<Self, icu_provider::DataError> {
use regex_automata::dfa::dense::{Builder, Config};
let Some(anchored_pattern) = pattern.strip_prefix('^') else {
return Err(
DataError::custom("Only anchored regexes (starting with ^) are supported")
.with_display_context(&pattern),
);
};
let mut builder = Builder::new();
let dfa = builder
.configure(
Config::new()
.start_kind(regex_automata::dfa::StartKind::Anchored)
.minimize(true),
)
.build(anchored_pattern)
.map_err(|e| {
icu_provider::DataError::custom("Cannot build DFA")
.with_display_context(anchored_pattern)
.with_debug_context(&e)
})?
.to_sparse()
.map_err(|e| {
icu_provider::DataError::custom("Cannot sparsify DFA")
.with_display_context(anchored_pattern)
.with_debug_context(&e)
})?;
Ok(Self {
dfa_bytes: dfa.to_bytes_native_endian().into(),
pattern: Some(pattern),
})
}
#[allow(clippy::unwrap_used)] pub fn deref(&'data self) -> DFA<&'data [u8]> {
unsafe { DFA::from_bytes_unchecked(&self.dfa_bytes).unwrap().0 }
}
}
#[cfg(all(test, feature = "datagen"))]
mod test {
use super::*;
use regex_automata::Input;
#[test]
fn test_serde_dfa() {
use regex_automata::dfa::Automaton;
let matcher = SerdeDFA::new(Cow::Borrowed("^abc")).unwrap();
assert!(matcher
.deref()
.try_search_fwd(&Input::new("ab").anchored(regex_automata::Anchored::Yes))
.unwrap()
.is_none());
assert!(matcher
.deref()
.try_search_fwd(&Input::new("abc").anchored(regex_automata::Anchored::Yes))
.unwrap()
.is_some());
assert!(matcher
.deref()
.try_search_fwd(&Input::new("abcde").anchored(regex_automata::Anchored::Yes))
.unwrap()
.is_some());
assert!(matcher
.deref()
.try_search_fwd(&Input::new(" abcde").anchored(regex_automata::Anchored::Yes))
.unwrap()
.is_none());
}
#[derive(serde::Deserialize)]
struct OptionSerdeDFA<'data>(
#[serde(borrow, deserialize_with = "SerdeDFA::maybe_deserialize")] Option<SerdeDFA<'data>>,
);
#[test]
#[cfg(target_endian = "little")]
fn test_postcard_serialization() {
let matcher = SerdeDFA::new(Cow::Borrowed("^abc*")).unwrap();
let mut bytes = postcard::to_stdvec(&matcher).unwrap();
assert_eq!(
postcard::from_bytes::<OptionSerdeDFA>(&bytes).unwrap().0,
Some(matcher)
);
bytes[17] ^= 255;
assert!(postcard::from_bytes::<OptionSerdeDFA>(&bytes).is_err());
bytes[17] ^= 255;
bytes.insert(123, 40);
assert!(postcard::from_bytes::<OptionSerdeDFA>(&bytes).is_err());
bytes.remove(123);
assert!(postcard::from_bytes::<OptionSerdeDFA>(&bytes[0..bytes.len() - 5]).is_err());
}
#[test]
fn test_rmp_serialization() {
let matcher = SerdeDFA::new(Cow::Borrowed("^abc*")).unwrap();
let bytes = rmp_serde::to_vec(&matcher).unwrap();
assert_eq!(
rmp_serde::from_slice::<OptionSerdeDFA>(&bytes).unwrap().0,
Some(matcher)
);
}
#[test]
#[cfg(feature = "serde_human")]
fn test_json_serialization() {
let matcher = SerdeDFA::new(Cow::Borrowed("^abc*")).unwrap();
let json = serde_json::to_string(&matcher).unwrap();
assert_eq!(
serde_json::from_str::<OptionSerdeDFA>(&json).unwrap().0,
Some(matcher)
);
assert!(serde_json::from_str::<OptionSerdeDFA>(".*[").is_err());
}
#[test]
fn databake() {
databake::test_bake!(
SerdeDFA,
const,
unsafe {
crate::provider::SerdeDFA::from_dfa_bytes_unchecked(
if cfg!(target_endian = "little") {
b"rust-regex-automata-dfa-sparse\0\0\xFF\xFE\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B(\x01\0\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x01\x80\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\x05\0\x05\x05\x06\x06\x0C\x0C\r\r\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\0\0\x02\0\0\x1B\0\0\x12\0\0\0\x12\0\0\0\0\x03\0\x06\x06\r\r\0\0h\0\0\0h\0\0\0\0\0\0\0\0\x0E\0\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\0\n\x01\0\0\x19\x01\0\0\x12\0\0\0\0\x02\0\x0F\x11\0\0D\0\0\0\0\0\0\0\0\x02\0\x11\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x0F\x10\0\0\xBF\0\0\0\0\0\0\0\0\x02\0\x10\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x11\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\x0F\x0F\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF`\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
} else {
b"rust-regex-automata-dfa-sparse\0\0\0\0\xFE\xFF\0\0\0\x02\0\0\0\0\0\0\0\x02\0\0\0\x0E\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x02\x02\x03\x04\x04\x05\x06\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x08\t\t\t\n\x0B\x0B\x0C\r\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0E\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12\x12\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x14\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x15\x16\x17\x17\x18\x19\x19\x19\x1A\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\x1B\0\0\x01(\0\x01\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\x05\x05\x05\x06\x06\x0C\x0C\r\r\0\0\0\0\0S\0\0\0D\0\0\0S\0\0\0D\0\0\0\0\0\0\x02\0\x1B\0\0\0\0\0\x12\0\0\0\x12\0\0\x03\x06\x06\r\r\0\0\0\0\0h\0\0\0h\0\0\0\0\0\0\x0E\0\0\x02\x02\x04\x07\t\t\x0B\x0E\x13\x13\x14\x14\x15\x15\x16\x16\x17\x17\x18\x18\x19\x19\x1A\x1A\0\0\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0D\0\0\0\xBF\0\0\0\xCE\0\0\0\xDD\0\0\0\xEC\0\0\0\xDD\0\0\0\xFB\0\0\x01\n\0\0\x01\x19\0\0\0\x12\0\0\x02\x0F\x11\0\0\0\0\0D\0\0\0\0\0\0\x02\x11\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x0F\x10\0\0\0\0\0\xBF\0\0\0\0\0\0\x02\x10\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x11\0\0\0\0\0\xDD\0\0\0\0\0\0\x02\x0F\x0F\0\0\0\0\0\xDD\0\0\0\0\0\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\x03\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\x01\0\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\0\0\0`\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0#\0\0\0\x12\0\0\0\t\0\0\0\x12\0\0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
},
)
},
icu_list
);
}
}