diff --git a/src/cosmetic_filter_utils.rs b/src/cosmetic_filter_utils.rs index 8df17b0e..be6a12f5 100644 --- a/src/cosmetic_filter_utils.rs +++ b/src/cosmetic_filter_utils.rs @@ -84,26 +84,67 @@ impl SpecificFilterType { } } -/// Encodes permission bits in the last byte of a script string -/// Returns the script with permission byte prepended +/// Encodes permission bits in the last 2 ascii chars of a script string +/// Returns the script with permission appended pub(crate) fn encode_script_with_permission( mut script: String, permission: PermissionMask, ) -> String { - script.push(permission.to_bits() as char); + const HEX_CHARS: &[u8; 16] = b"0123456789abcdef"; + let high = (permission.to_bits() >> 4) as usize; + let low = (permission.to_bits() & 0x0f) as usize; + + script.push(HEX_CHARS[high] as char); + script.push(HEX_CHARS[low] as char); script } -/// Decodes permission bits from the last byte of a script string +/// Decodes permission bits from the last 2 ascii chars of a script string /// Returns (permission, script) tuple pub(crate) fn decode_script_with_permission(encoded_script: &str) -> (PermissionMask, &str) { - if encoded_script.is_empty() { - return (PermissionMask::default(), encoded_script); + if encoded_script.len() < 2 { + return (PermissionMask::default(), ""); + } + let mut last_chars = encoded_script.char_indices().rev(); + // unwrap: length >= 2 asserted above + let (digit2, digit1) = (last_chars.next().unwrap(), last_chars.next().unwrap()); + fn parse_hex(c: char) -> Option { + match c { + '0'..='9' => Some(c as u8 - b'0'), + 'a'..='f' => Some(c as u8 - b'a' + 10), + _ => None, + } } + if let (Some(d1), Some(d2)) = (parse_hex(digit1.1), parse_hex(digit2.1)) { + let permission = PermissionMask::from_bits(d1 << 4 | d2); + (permission, &encoded_script[..digit1.0]) + } else { + (PermissionMask::default(), "") + } +} + +#[cfg(test)] +mod tests { + use super::*; - let last_char = encoded_script.chars().last().unwrap(); - let permission_bits = last_char as u8; - let permission = PermissionMask::from_bits(permission_bits); - let script = &encoded_script[..encoded_script.len() - 1]; - (permission, script) + #[test] + fn test_encode_decode_script_with_permission() { + for permission in 0u8..=255u8 { + let script = "console.log('测试 🚀 emoji')".to_string(); + let permission = PermissionMask::from_bits(permission); + + let encoded = encode_script_with_permission(script.clone(), permission); + let (decoded_permission, decoded_script) = decode_script_with_permission(&encoded); + + assert_eq!(decoded_permission.to_bits(), permission.to_bits()); + assert_eq!(decoded_script, script); + } + } + + #[test] + fn test_encode_decode_script_with_permission_empty() { + let (permission, script) = decode_script_with_permission(""); + assert_eq!(permission.to_bits(), 0); + assert_eq!(script, ""); + } } diff --git a/src/lib.rs b/src/lib.rs index 2e9c84fa..2e5e4458 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ pub mod cosmetic_filter_cache; mod cosmetic_filter_cache_builder; mod cosmetic_filter_utils; mod data_format; -mod engine; +pub mod engine; pub mod filters; mod flatbuffers; pub mod lists; diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 6c2c5274..19869f4a 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -16,8 +16,8 @@ mod resource_storage; pub(crate) use resource_storage::parse_scriptlet_args; #[doc(inline)] pub use resource_storage::{ - AddResourceError, InMemoryResourceStorage, ResourceStorage, ResourceStorageBackend, - ScriptletResourceError, + AddResourceError, InMemoryResourceStorage, ResourceImpl, ResourceStorage, + ResourceStorageBackend, ScriptletResourceError, }; use memchr::memrchr as find_char_reverse; diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index 03e3c032..141c1187 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -237,9 +237,9 @@ mod tests { ); } let expected_hash: u64 = if cfg!(feature = "css-validation") { - 1870862363610703254 + 6149900431907845995 } else { - 17169786507112655088 + 4163862738495854511 }; assert_eq!(hash(&data), expected_hash, "{}", HASH_MISMATCH_MSG);