diff --git a/Cargo.toml b/Cargo.toml index 1970159..a7c89d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,8 @@ array_tool = "1.0.3" [lib] name = "jsonpath_lib" path = "src/lib.rs" +crate-type = ["cdylib", "rlib"] [profile.release] #debug = true -#lto = false +#lto = false \ No newline at end of file diff --git a/benchmark/benches_lua_vs_rust/bench_lua_vs_rust.sh b/benchmark/benches_lua_vs_rust/bench_lua_vs_rust.sh deleted file mode 100755 index dee47a0..0000000 --- a/benchmark/benches_lua_vs_rust/bench_lua_vs_rust.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -e - -# http://luajit.org/index.html - -cargo clean && \ -cargo build --release - -export JSONPATH_LIB_PATH="${PWD}/../../target/release" -export LUA_PATH="${PWD}/../../lua/?.lua;" - -echo -time cargo run --release -- 1000 -echo -time luajit example.lua 1000 -echo -time cargo run --release -- 5000 -echo -time luajit example.lua 5000 -echo -time cargo run --release -- 10000 -echo -time luajit example.lua 10000 - diff --git a/benchmark/benches_lua_vs_rust/lib.rs b/benchmark/benches_lua_vs_rust/lib.rs deleted file mode 100644 index e69de29..0000000 diff --git a/benchmark/benches_lua_vs_rust/.gitignore b/lua/.gitignore similarity index 100% rename from benchmark/benches_lua_vs_rust/.gitignore rename to lua/.gitignore diff --git a/benchmark/benches_lua_vs_rust/Cargo.toml b/lua/Cargo.toml similarity index 66% rename from benchmark/benches_lua_vs_rust/Cargo.toml rename to lua/Cargo.toml index a4523f2..fe05a10 100644 --- a/benchmark/benches_lua_vs_rust/Cargo.toml +++ b/lua/Cargo.toml @@ -1,13 +1,14 @@ [package] -name = "lua_vs_rust_benchmark" +name = "jsonpath_lua" version = "0.1.0" authors = ["Changseok Han "] license = "MIT" [dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } -jsonpath_lib = { path = "../../" } +jsonpath_lib = { path = "../" } [[bin]] -name = "lua_vs_rust_benchmark" -path = "example.rs" \ No newline at end of file +name = "bench" +path = "bench_lua_vs_rust/example.rs" + diff --git a/lua/bench_lua_vs_rust.sh b/lua/bench_lua_vs_rust.sh new file mode 100755 index 0000000..668d7f5 --- /dev/null +++ b/lua/bench_lua_vs_rust.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -e + +# http://luajit.org/index.html + +#cargo clean && \ +cargo build --release + +export JSONPATH_LIB_PATH="${PWD}/target/release/deps" +export LUA_PATH="${PWD}/?.lua;" + +echo +time cargo run --release --bin bench -- 1000 +echo +time luajit bench_lua_vs_rust/example.lua 1000 +echo +time cargo run --release --bin bench -- 5000 +echo +time luajit bench_lua_vs_rust/example.lua 5000 +echo +time cargo run --release --bin bench -- 10000 +echo +time luajit bench_lua_vs_rust/example.lua 10000 + diff --git a/benchmark/benches_lua_vs_rust/example.lua b/lua/bench_lua_vs_rust/example.lua similarity index 62% rename from benchmark/benches_lua_vs_rust/example.lua rename to lua/bench_lua_vs_rust/example.lua index 759ec9f..cd833da 100644 --- a/benchmark/benches_lua_vs_rust/example.lua +++ b/lua/bench_lua_vs_rust/example.lua @@ -9,12 +9,12 @@ end print(string.format("%s - %u", "lua iter", iter)); -local file = io.open("../../benchmark/example.json", "r"); +local file = io.open("../benchmark/example.json", "r"); io.input(file) local data = io.read("*a"); io.close(file); -local cb = compile("$..book[?(@.price<30 && @.category==\"fiction\")]"); +local template = compile("$..book[?(@.price<30 && @.category==\"fiction\")]"); for i = 0, iter do - local r = cb(data); + local r = template(data); -- print(r); end diff --git a/benchmark/benches_lua_vs_rust/example.rs b/lua/bench_lua_vs_rust/example.rs similarity index 95% rename from benchmark/benches_lua_vs_rust/example.rs rename to lua/bench_lua_vs_rust/example.rs index 68f63b2..7c48910 100644 --- a/benchmark/benches_lua_vs_rust/example.rs +++ b/lua/bench_lua_vs_rust/example.rs @@ -14,7 +14,7 @@ fn read_json(path: &str) -> String { } fn get_string() -> String { - read_json("../../benchmark/example.json") + read_json("../benchmark/example.json") } fn get_json() -> Value { diff --git a/lua/lib.lua b/lua/lib.lua index 446b024..20eafd2 100644 --- a/lua/lib.lua +++ b/lua/lib.lua @@ -8,10 +8,10 @@ else ext = 'dylib' end -ffi.cdef[[ +ffi.cdef [[ const char* ffi_select(const char *json_str, const char *path); void *ffi_path_compile(const char *path); -const char* ffi_select_with_compiled(void *ptr, const char *json_str); +const char* ffi_select_with_compiled_path(void *ptr, const char *json_str); ]] local jsonpathLibPath = os.getenv("JSONPATH_LIB_PATH"); @@ -20,6 +20,6 @@ local jsonpath = ffi.load(jsonpathLibPath .. '/libjsonpath_lib.' .. ext); function compile(path) local compiledPath = jsonpath.ffi_path_compile(path); return function(jsonStr) - return ffi.string(jsonpath.ffi_select_with_compiled(compiledPath, jsonStr)); + return ffi.string(jsonpath.ffi_select_with_compiled_path(compiledPath, jsonStr)); end end \ No newline at end of file diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs new file mode 100644 index 0000000..ab160bb --- /dev/null +++ b/src/ffi/mod.rs @@ -0,0 +1,57 @@ +use std::ffi::{CStr, CString}; +use std::os::raw::{c_char, c_void}; + +use {parser, select, select_as_str}; + +const INVALID_PATH: &str = "invalid path"; +const INVALID_JSON: &str = "invalud json"; + +fn to_str(v: *const c_char, err_msg: &str) -> &str { + unsafe { CStr::from_ptr(v) }.to_str().expect(err_msg) +} + +fn to_char_ptr(v: &str) -> *const c_char { + let s = CString::new(v).expect(&format!("invalid string: {}", v)); + let ptr = s.as_ptr(); + std::mem::forget(s); + ptr +} + +#[no_mangle] +pub extern "C" fn ffi_select(json_str: *const c_char, path: *const c_char) -> *const c_char { + let json_str = to_str(json_str, INVALID_JSON); + let path = to_str(path, INVALID_PATH); + match select_as_str(json_str, path) { + Ok(v) => to_char_ptr(v.as_str()), + Err(e) => { + panic!("{:?}", e); + } + } +} + +#[no_mangle] +pub extern "C" fn ffi_path_compile(path: *const c_char) -> *mut c_void { + let path = to_str(path, INVALID_PATH); + let ref_node = Box::into_raw(Box::new(parser::Parser::compile(path).unwrap())); + let ptr = ref_node as *mut c_void; + std::mem::forget(ref_node); + ptr +} + +#[no_mangle] +pub extern "C" fn ffi_select_with_compiled_path( + path_ptr: *mut c_void, + json_ptr: *const c_char, +) -> *const c_char { + let node = unsafe { Box::from_raw(path_ptr as *mut parser::Node) }; + let json_str = to_str(json_ptr, INVALID_JSON); + let json = serde_json::from_str(json_str).expect(&format!("invalid json string: {}", json_str)); + + let mut selector = select::Selector::default(); + let found = selector.compiled_path(&node).value(&json).select().unwrap(); + std::mem::forget(node); + + let result = + serde_json::to_string(&found).expect(&format!("json serialize error: {:?}", found)); + to_char_ptr(result.as_str()) +} diff --git a/src/lib.rs b/src/lib.rs index f2a138e..355a1a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,9 +135,9 @@ use serde_json::Value; pub use parser::Parser; pub use select::JsonPathError; pub use select::{Selector, SelectorMut}; -use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_void}; +#[doc(hidden)] +mod ffi; #[doc(hidden)] mod parser; #[doc(hidden)] @@ -468,71 +468,3 @@ where let value = selector.str_path(path)?.value(value).replace_with(fun)?; Ok(value.take().unwrap_or(Value::Null)) } - - -#[no_mangle] -pub extern "C" fn ffi_select(json_str: *const c_char, path: *const c_char) -> *const c_char { - let json_str = match unsafe { CStr::from_ptr(json_str) }.to_str() { - Ok(s) => s, - Err(e) => { - panic!("{:?}", e); - } - }; - - let path = match unsafe { CStr::from_ptr(path) }.to_str() { - Ok(s) => s, - Err(e) => { - panic!("{:?}", e); - } - }; - - match select_as_str(json_str, path) { - Ok(v) => match serde_json::to_string(&v) { - Ok(s) => { - let s = CString::new(s.as_str()).unwrap(); - let p = s.as_ptr(); - std::mem::forget(s); - p - } - Err(e) => { - panic!("{:?}", e); - } - }, - Err(e) => { - panic!("{:?}", e); - } - } -} - - -#[no_mangle] -pub extern "C" fn ffi_path_compile(path: *const c_char) -> *mut c_void { - let path = match unsafe { CStr::from_ptr(path) }.to_str() { - Ok(s) => s, - Err(e) => { - panic!("{:?}", e); - } - }; - - Box::into_raw(Box::new(Parser::compile(path).unwrap())) as *mut c_void -} - -#[no_mangle] -pub extern "C" fn ffi_select_with_compiled( - path_ptr: *mut c_void, - json_ptr: *const c_char, -) -> *const c_char { - let node = unsafe { Box::from_raw(path_ptr as *mut parser::Node) }; - let json_str = unsafe { CStr::from_ptr(json_ptr) }.to_str().expect("invalid 'json_ptr' input"); - let json = serde_json::from_str(json_str).expect(&format!("invalid json string: {}", json_str)); - - let mut selector = Selector::default(); - let found = selector.compiled_path(&node).value(&json).select().unwrap(); - std::mem::forget(node); - - let result = serde_json::to_string(&found).expect(&format!("json serialize error: {:?}", found)); - let result_cstring = CString::new(result.as_str()).expect(&format!("empty result: {:?}", result)); - let result_ptr = result_cstring.as_ptr(); - std::mem::forget(result_cstring); - result_ptr -} diff --git a/src/select/mod.rs b/src/select/mod.rs index ca2fc4d..56c4efe 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -2,10 +2,10 @@ use std::collections::HashSet; use std::fmt; use array_tool::vec::{Intersect, Union}; +use serde_json::map::Entry; use serde_json::{Number, Value}; use parser::*; -use serde_json::map::Entry; fn to_f64(n: &Number) -> f64 { if n.is_i64() {