From 7c763cbc089347c94223805ca9c5d634cbc7062d Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 20 Oct 2025 15:04:35 +0400 Subject: [PATCH 1/6] Expose ResourceImpl --- src/resources/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; From 5c330da1c9e36f5a42cfbc9b18306d7aa77dc7d3 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 20 Oct 2025 15:22:32 +0400 Subject: [PATCH 2/6] Fix decode_script_with_permission() --- src/cosmetic_filter_utils.rs | 41 +++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/cosmetic_filter_utils.rs b/src/cosmetic_filter_utils.rs index 8df17b0e..075dc1dd 100644 --- a/src/cosmetic_filter_utils.rs +++ b/src/cosmetic_filter_utils.rs @@ -94,16 +94,41 @@ pub(crate) fn encode_script_with_permission( script } -/// Decodes permission bits from the last byte of a script string +/// Decodes permission bits from the last char 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 let Some(last_char_start) = encoded_script.char_indices().last() { + let last_char = last_char_start.1; + let permission_bits = last_char as u8; + let permission = PermissionMask::from_bits(permission_bits); + (permission, &encoded_script[..last_char_start.0]) + } else { + (PermissionMask::default(), encoded_script) } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[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); - 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) + 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, ""); + } } From e0458073f3d83b83db4b3a49db5cba8a6b6adde5 Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 20 Oct 2025 15:49:09 +0400 Subject: [PATCH 3/6] Export EngineDebugInfo --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 2e9c84fa..4522db9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,10 @@ pub use engine::Engine; #[doc(inline)] pub use lists::FilterSet; +#[cfg(feature = "debug-info")] +#[doc(inline)] +pub use engine::EngineDebugInfo; + #[cfg(test)] #[path = "../tests/test_utils.rs"] mod test_utils; From d69d1ef08474a78b6cffe697b06c013bcd8457ea Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 20 Oct 2025 20:52:30 +0400 Subject: [PATCH 4/6] Make mod engine pub --- src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4522db9c..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; @@ -42,10 +42,6 @@ pub use engine::Engine; #[doc(inline)] pub use lists::FilterSet; -#[cfg(feature = "debug-info")] -#[doc(inline)] -pub use engine::EngineDebugInfo; - #[cfg(test)] #[path = "../tests/test_utils.rs"] mod test_utils; From 8f5883df520bae62fcd36c543feb6b4c5fe14b8c Mon Sep 17 00:00:00 2001 From: Anton Lazarev Date: Mon, 20 Oct 2025 11:04:45 -0700 Subject: [PATCH 5/6] encode permission as hex string --- src/cosmetic_filter_utils.rs | 35 +++++++++++++++++++++++++---------- tests/unit/engine.rs | 2 +- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/cosmetic_filter_utils.rs b/src/cosmetic_filter_utils.rs index 075dc1dd..24e69262 100644 --- a/src/cosmetic_filter_utils.rs +++ b/src/cosmetic_filter_utils.rs @@ -84,26 +84,41 @@ 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 bytes 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); + let permission_string = format!("{:02x}", permission.to_bits()); + debug_assert_eq!(permission_string.len(), 2); + script.push_str(&permission_string); script } -/// Decodes permission bits from the last char of a script string +/// Decodes permission bits from the last 2 ascii bytes of a script string /// Returns (permission, script) tuple pub(crate) fn decode_script_with_permission(encoded_script: &str) -> (PermissionMask, &str) { - if let Some(last_char_start) = encoded_script.char_indices().last() { - let last_char = last_char_start.1; - let permission_bits = last_char as u8; - let permission = PermissionMask::from_bits(permission_bits); - (permission, &encoded_script[..last_char_start.0]) + 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 { + if c >= '0' && c <= '9' { + Some(c as u8 - b'0') + } else if c >= 'a' && c <= 'f' { + Some(c as u8 - b'a' + 10) + } else { + 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(), encoded_script) + (PermissionMask::default(), "") } } diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index 03e3c032..eae49a21 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -237,7 +237,7 @@ mod tests { ); } let expected_hash: u64 = if cfg!(feature = "css-validation") { - 1870862363610703254 + 6149900431907845995 } else { 17169786507112655088 }; From 2c4a4338078b5b4921d41425a33b1db7314713cd Mon Sep 17 00:00:00 2001 From: Mikhail Atuchin Date: Mon, 20 Oct 2025 23:55:05 +0400 Subject: [PATCH 6/6] Adjust hex encoding impl --- src/cosmetic_filter_utils.rs | 23 ++++++++++++----------- tests/unit/engine.rs | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/cosmetic_filter_utils.rs b/src/cosmetic_filter_utils.rs index 24e69262..be6a12f5 100644 --- a/src/cosmetic_filter_utils.rs +++ b/src/cosmetic_filter_utils.rs @@ -84,19 +84,22 @@ impl SpecificFilterType { } } -/// Encodes permission bits in the last 2 ascii bytes of a script string +/// 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 { - let permission_string = format!("{:02x}", permission.to_bits()); - debug_assert_eq!(permission_string.len(), 2); - script.push_str(&permission_string); + 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 2 ascii bytes 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.len() < 2 { @@ -106,12 +109,10 @@ pub(crate) fn decode_script_with_permission(encoded_script: &str) -> (Permission // unwrap: length >= 2 asserted above let (digit2, digit1) = (last_chars.next().unwrap(), last_chars.next().unwrap()); fn parse_hex(c: char) -> Option { - if c >= '0' && c <= '9' { - Some(c as u8 - b'0') - } else if c >= 'a' && c <= 'f' { - Some(c as u8 - b'a' + 10) - } else { - None + 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)) { diff --git a/tests/unit/engine.rs b/tests/unit/engine.rs index eae49a21..141c1187 100644 --- a/tests/unit/engine.rs +++ b/tests/unit/engine.rs @@ -239,7 +239,7 @@ mod tests { let expected_hash: u64 = if cfg!(feature = "css-validation") { 6149900431907845995 } else { - 17169786507112655088 + 4163862738495854511 }; assert_eq!(hash(&data), expected_hash, "{}", HASH_MISMATCH_MSG);