Skip to content

Commit

Permalink
Merge pull request #6 from breadrock1/feat/multiple-instances
Browse files Browse the repository at this point in the history
Feature: Impled multiple instances
  • Loading branch information
breadrock1 authored Jan 6, 2025
2 parents 1ef738d + 6f32192 commit a327c27
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 121 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
* Fix: Unpack and load native libraries @breadrock1 in https://github.com/breadrock1/Adblock-coffee/pull/3


**Full Changelog**: https://github.com/breadrock1/Adblock-coffee/compare/adblock-coffee-1.0.3...adblock-coffee-1.0.6
**Full Changelog**: https://github.com/breadrock1/adblock-coffee/compare/adblock-coffee-1.0.6...adblock-coffee-1.1.7
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Adblock-coffee
# adblock-coffee
[![Create release](https://github.com/breadrock1/Adblock-coffee/actions/workflows/release.yml/badge.svg)](https://github.com/breadrock1/Adblock-coffee/actions/workflows/release.yml)
[![Pipelines](https://img.shields.io/github/actions/workflow/status/breadrock1/Adblock-coffee/build.yml)](https://img.shields.io/github/actions/workflow/status/breadrock1/Adblock-coffee/build.yml)
[![Last release: ](https://img.shields.io/github/v/release/breadrock1/Adblock-coffee)](https://img.shields.io/github/v/release/breadrock1/Adblock-coffee)
Expand Down
10 changes: 5 additions & 5 deletions adblock-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
[package]
name = "adblock-coffee"
version = "0.1.4"
version = "1.1.7"
edition = "2021"
authors = ["Bread White <breadrock1@gmail.com>"]

[lib]
crate_type = ["cdylib"]

[dependencies]
anyhow = "^1.0"
adblock = "^0.8"
anyhow = "1.0.95"
adblock = "0.9.4"
env_logger = "0.11.3"
jni = "^0.21"
jni = "0.21.1"
lazy_static = "1.5.0"
log = "0.4.22"
once_cell = "1.19.0"
thiserror = "^1.0"
thiserror = "2.0.9"

[target.'cfg(target_os = "android")'.dependencies]
android_logger = "^0.14"
27 changes: 8 additions & 19 deletions adblock-rs/src/adblock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ use adblock::lists::ParseOptions;
use adblock::request::Request;
use adblock::{Engine, FilterSet};

use crate::errors::RustException;
use crate::errors::Result;

pub(crate) struct AdvtBlocker {
engine: Engine,
}

impl AdvtBlocker {
pub fn new(filter_list: Vec<String>) -> Self {
#[cfg(not(debug_assertions))]
let debug_info = false;

#[cfg(debug_assertions)]
let debug_info = true;

let mut filter_set = FilterSet::new(debug_info);
filter_set.add_filters(&filter_list, ParseOptions::default());

Expand All @@ -21,24 +26,8 @@ impl AdvtBlocker {
}
}

pub fn recreate(&mut self, filter_list: Vec<String>) {
let debug_info = true;
let mut filter_set = FilterSet::new(debug_info);
filter_set.add_filters(&filter_list, ParseOptions::default());

let filter_engine = Engine::from_filter_set(filter_set, true);
self.engine = filter_engine;
}

pub fn check_network_urls(
&self,
url: &str,
src_url: &str,
req_type: &str,
) -> Result<bool, RustException> {
let request = Request::new(url, src_url, req_type)
.map_err(|err| RustException::CreateRequest(err.to_string()))?;

pub fn check_network_urls(&self, url: &str, src_url: &str, req_type: &str) -> Result<bool> {
let request = Request::new(url, src_url, req_type)?;
let blocker_result = self.engine.check_network_request(&request);
Ok(blocker_result.matched)
}
Expand Down
25 changes: 12 additions & 13 deletions adblock-rs/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@ use jni::errors::{Error, Exception, ToException};

use adblock::request::RequestError;
use std::fmt::Debug;
use std::str::Utf8Error;
use std::sync::PoisonError;
use thiserror::Error;

pub(crate) type Result<T> = std::result::Result<T, RustException>;

#[derive(Debug, Error)]
pub enum RustException {
#[error("Failed while creating request: {0}")]
CreateRequest(String),
#[error("Failed while extracting parameter: {0}")]
ExtractParameter(String),
#[error("failed to create request: {0}")]
CreateRequest(#[from] RequestError),
#[error("failed to extract java parameter: {0}")]
ExtractParameter(#[from] Utf8Error),
#[error("failed to parse java object: {0}")]
ParseJavaObject(String),
#[error("Failed while lock mutex for AdvtBlocker: {0}")]
MutexGuardLock(String),
#[error("Jvm runtime error: {0}")]
InstanceAccess(String),
#[error("JVM runtime error: {0}")]
JvmException(String),
}

Expand All @@ -26,15 +31,9 @@ impl ToException for RustException {
}
}

impl From<RequestError> for RustException {
fn from(value: RequestError) -> Self {
RustException::CreateRequest(value.to_string())
}
}

impl<T> From<PoisonError<T>> for RustException {
fn from(value: PoisonError<T>) -> Self {
RustException::MutexGuardLock(value.to_string())
RustException::InstanceAccess(value.to_string())
}
}

Expand Down
38 changes: 26 additions & 12 deletions adblock-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,22 @@ use jni::JNIEnv;

use crate::wrapper::*;

const RUST_EXCEPTION_CLASS: &str = "com/example/adblock/exception/RustException";

#[no_mangle]
pub extern "system" fn Java_com_example_adblock_AdvtBlocker_initObject(
mut env: JNIEnv,
_class: JObject,
rules: JObjectArray,
) -> jlong {
init_object_wrapped(&mut env, &rules).unwrap_or_else(|err| {
log::error!("{:?}", err);
-1_i64 as jlong
})
match init_object_wrapped(&mut env, &rules) {
Ok(ptr) => ptr,
Err(err) => {
env.throw_new(RUST_EXCEPTION_CLASS, err.to_string())
.expect("failed to find RustException java class");
-1_i64 as jlong
}
}
}

#[no_mangle]
Expand All @@ -27,10 +33,14 @@ pub extern "system" fn Java_com_example_adblock_AdvtBlocker_destroyObject(
_class: JObject,
ptr: jlong,
) -> jboolean {
destroy_object_wrapped(&mut env, ptr).unwrap_or_else(|err| {
log::error!("{:?}", err);
false as jboolean
})
match destroy_object_wrapped(&mut env, ptr) {
Ok(status) => status,
Err(err) => {
env.throw_new(RUST_EXCEPTION_CLASS, err.to_string())
.expect("failed to find RustException java class");
false as jboolean
}
}
}

#[no_mangle]
Expand All @@ -42,8 +52,12 @@ pub extern "system" fn Java_com_example_adblock_AdvtBlocker_checkNetworkUrls(
src_url: JString,
req_type: JString,
) -> jboolean {
check_net_urls_wrapped(&mut env, ptr, &url, &src_url, &req_type).unwrap_or_else(|err| {
log::error!("{:?}", err);
false as jboolean
})
match check_net_urls_wrapped(&mut env, ptr, &url, &src_url, &req_type) {
Ok(result) => result,
Err(err) => {
env.throw_new(RUST_EXCEPTION_CLASS, err.to_string())
.expect("failed to find RustException java class");
false as jboolean
}
}
}
16 changes: 14 additions & 2 deletions adblock-rs/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,29 @@ lazy_static! {

#[cfg(target_os = "android")]
init.call_once(|| {
#[cfg(not(debug_assertions))]
let level_filter = LevelFilter::Info;

#[cfg(debug_assertions)]
let level_filter = LevelFilter::Debug;

android_logger::init_once(
Config::default()
.with_tag("Tag_RustAdvtBlocker")
.with_max_level(LevelFilter::max()),
.with_max_level(level_filter),
);
});

#[cfg(not(target_os = "android"))]
init.call_once(|| {
#[cfg(not(debug_assertions))]
let level_filter = LevelFilter::Info;

#[cfg(debug_assertions)]
let level_filter = LevelFilter::Debug;

let mut builder = Builder::new();
builder.filter_level(LevelFilter::Debug);
builder.filter_level(level_filter);
builder.init();
});

Expand Down
120 changes: 58 additions & 62 deletions adblock-rs/src/wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,102 +1,98 @@
use jni::objects::{JObjectArray, JString};
use jni::sys::{jboolean, jlong};
use jni::objects::{JList, JObjectArray, JString};
use jni::sys::{jboolean, jint, jlong};
use jni::JNIEnv;
use lazy_static::lazy_static;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::Mutex;

use crate::adblock::AdvtBlocker;
use crate::errors::RustException;
use crate::errors::{Result, RustException};

lazy_static! {
static ref INSTANCE: Lazy<Mutex<AdvtBlocker>> = Lazy::new(|| {
let init: Mutex<AdvtBlocker> = Mutex::new(AdvtBlocker::default());
init
});
static ref INSTANCE_POOL: Lazy<Mutex<HashMap<jlong, AdvtBlocker>>> =
Lazy::new(|| { Mutex::new(HashMap::default()) });
}

pub(crate) fn init_object_wrapped(
env: &mut JNIEnv,
rules: &JObjectArray,
) -> Result<jlong, RustException> {
pub(crate) fn init_object_wrapped(env: &mut JNIEnv, rules: &JObjectArray) -> Result<jlong> {
let conv_rules = extract_list_str(env, rules)?;
let mut instance_lock = INSTANCE.lock().map_err(RustException::from)?;

instance_lock.recreate(conv_rules);
let advt_instance = AdvtBlocker::new(conv_rules);
let ptr = Box::into_raw(Box::new(&advt_instance)) as jlong;

let mut instance_lock = INSTANCE_POOL.lock()?;
instance_lock.insert(ptr, advt_instance);

let ptr = Box::into_raw(Box::new(&INSTANCE));
Ok(ptr as jlong)
Ok(ptr)
}

pub(crate) fn destroy_object_wrapped(
_env: &mut JNIEnv,
_ptr: jlong,
) -> Result<jboolean, RustException> {
let instance_lock = INSTANCE.lock().map_err(RustException::from)?;
pub(crate) fn destroy_object_wrapped(_env: &mut JNIEnv, ptr: jlong) -> Result<jboolean> {
let mut instance_lock = INSTANCE_POOL.lock()?;

let Some(instance) = instance_lock.remove(&ptr) else {
let msg = format!("failed to remove instance: {ptr:?}");
return Err(RustException::InstanceAccess(msg));
};

drop(instance_lock);
drop(instance);
Ok(true as jboolean)
}

pub(crate) fn check_net_urls_wrapped(
env: &mut JNIEnv,
_ptr: jlong,
ptr: jlong,
url: &JString,
src_url: &JString,
req_type: &JString,
) -> Result<jboolean, RustException> {
let advt_blocker = INSTANCE.lock().map_err(RustException::from)?;
) -> Result<jboolean> {
let instance_lock = INSTANCE_POOL.lock()?;
let Some(advt_blocker) = instance_lock.get(&ptr) else {
let msg = format!("failed to get instance: {ptr:?}");
return Err(RustException::InstanceAccess(msg));
};

let url_str = extract_str(env, url)?;
let src_url_str = extract_str(env, src_url)?;
let req_type_str = extract_str(env, req_type)?;

let check_result = advt_blocker.check_network_urls(
url_str.as_str(),
src_url_str.as_str(),
req_type_str.as_str(),
)?;

Ok(check_result as jboolean)
}

fn extract_str<'a>(env: &'a mut JNIEnv, j_obj: &'a JString) -> Result<String, RustException> {
let j_str = env.get_string(&j_obj).map_err(RustException::from)?;

let str_obj = j_str
.to_str()
.map_err(|err| RustException::ExtractParameter(err.to_string()))?;

Ok(str_obj.to_string())
advt_blocker
.check_network_urls(&url_str, &src_url_str, &req_type_str)
.map(|result| result as jboolean)
}

fn extract_list_str<'a>(
env: &'a mut JNIEnv,
j_obj_arr: &'a JObjectArray,
) -> Result<Vec<String>, RustException> {
let j_list = env.get_list(&j_obj_arr).map_err(RustException::from)?;

let j_list_size = j_list.size(env).map_err(RustException::from)?;
fn extract_list_str<'a>(env: &'a mut JNIEnv, j_obj_arr: &'a JObjectArray) -> Result<Vec<String>> {
let j_list = env.get_list(j_obj_arr)?;
let j_list_size = j_list.size(env)?;

let mut list_data = Vec::with_capacity(j_list_size as usize);
for index in 0..j_list_size {
let j_obj_get_result = j_list.get(env, index);
if j_obj_get_result.is_err() {
let err = j_obj_get_result.err().unwrap();
log::warn!("failed to parse rules: {:?}", err);
continue;
match extract_entity(env, &j_list, index) {
Ok(data) => list_data.push(data),
Err(err) => {
log::error!("failed to extract str from java object: {err:#?}");
continue;
}
}
}

Ok(list_data)
}

let j_obj_opt = j_obj_get_result?;
if j_obj_opt.is_none() {
log::warn!("parsed rule is none. skipped...");
continue;
fn extract_entity(env: &mut JNIEnv, j_list: &JList, index: jint) -> Result<String> {
let j_obj_opt = j_list.get(env, index)?;
let j_str = match j_obj_opt {
Some(j_obj) => JString::from(j_obj),
None => {
let msg = format!("parsed rule is none: {j_obj_opt:?}. skipped...");
return Err(RustException::ParseJavaObject(msg));
}
};

let j_str = JString::from(j_obj_opt.unwrap());
let str_data = extract_str(env, &j_str)?;
list_data.push(str_data);
}
extract_str(env, &j_str)
}

Ok(list_data)
fn extract_str<'a>(env: &'a mut JNIEnv, j_obj: &'a JString) -> Result<String> {
let j_str = env.get_string(j_obj)?;
let str_obj = j_str.to_str()?;
Ok(str_obj.to_string())
}
Loading

0 comments on commit a327c27

Please sign in to comment.