From ea7599c0121486aaf6ef819748a9ab0c47368f46 Mon Sep 17 00:00:00 2001 From: freestrings Date: Fri, 23 Aug 2019 00:00:01 +0900 Subject: [PATCH] first commit lua ffi --- benchmark/benches_lua_vs_rust/.gitignore | 4 ++ benchmark/benches_lua_vs_rust/Cargo.toml | 13 ++++ .../benches_lua_vs_rust/bench_lua_vs_rust.sh | 25 +++++++ benchmark/benches_lua_vs_rust/example.lua | 20 ++++++ benchmark/benches_lua_vs_rust/example.rs | 46 ++++++++++++ benchmark/benches_lua_vs_rust/lib.rs | 0 lua/lib.lua | 25 +++++++ src/lib.rs | 70 +++++++++++++++++++ 8 files changed, 203 insertions(+) create mode 100644 benchmark/benches_lua_vs_rust/.gitignore create mode 100644 benchmark/benches_lua_vs_rust/Cargo.toml create mode 100755 benchmark/benches_lua_vs_rust/bench_lua_vs_rust.sh create mode 100644 benchmark/benches_lua_vs_rust/example.lua create mode 100644 benchmark/benches_lua_vs_rust/example.rs create mode 100644 benchmark/benches_lua_vs_rust/lib.rs create mode 100644 lua/lib.lua diff --git a/benchmark/benches_lua_vs_rust/.gitignore b/benchmark/benches_lua_vs_rust/.gitignore new file mode 100644 index 0000000..3343429 --- /dev/null +++ b/benchmark/benches_lua_vs_rust/.gitignore @@ -0,0 +1,4 @@ +.idea/* +.vscode +/target/ +Cargo.lock \ No newline at end of file diff --git a/benchmark/benches_lua_vs_rust/Cargo.toml b/benchmark/benches_lua_vs_rust/Cargo.toml new file mode 100644 index 0000000..a4523f2 --- /dev/null +++ b/benchmark/benches_lua_vs_rust/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "lua_vs_rust_benchmark" +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 = "../../" } + +[[bin]] +name = "lua_vs_rust_benchmark" +path = "example.rs" \ 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 new file mode 100755 index 0000000..dee47a0 --- /dev/null +++ b/benchmark/benches_lua_vs_rust/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" +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/example.lua b/benchmark/benches_lua_vs_rust/example.lua new file mode 100644 index 0000000..759ec9f --- /dev/null +++ b/benchmark/benches_lua_vs_rust/example.lua @@ -0,0 +1,20 @@ +require("lib") + +local iter; +if arg[1] == nil or arg[1] == '' then + iter = 5000; +else + iter = tonumber(arg[1]); +end + +print(string.format("%s - %u", "lua iter", iter)); + +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\")]"); +for i = 0, iter do + local r = cb(data); +-- print(r); +end diff --git a/benchmark/benches_lua_vs_rust/example.rs b/benchmark/benches_lua_vs_rust/example.rs new file mode 100644 index 0000000..68f63b2 --- /dev/null +++ b/benchmark/benches_lua_vs_rust/example.rs @@ -0,0 +1,46 @@ +extern crate jsonpath_lib as jsonpath; +extern crate serde; +extern crate serde_json; + +use std::io::Read; + +use serde_json::Value; + +fn read_json(path: &str) -> String { + let mut f = std::fs::File::open(path).unwrap(); + let mut contents = String::new(); + f.read_to_string(&mut contents).unwrap(); + contents +} + +fn get_string() -> String { + read_json("../../benchmark/example.json") +} + +fn get_json() -> Value { + let string = get_string(); + serde_json::from_str(string.as_str()).unwrap() +} + +fn get_path() -> &'static str { + r#"$..book[?(@.price<30 && @.category=="fiction")]"# +} + +fn main() { + let args: Vec = std::env::args().collect(); + let iter = if args.len() < 2 { 5000_usize } else { args[1].as_str().parse::().unwrap() }; + + println!("rust iter - {}", iter); + + let json = get_json(); + for _ in 0..iter { + let mut selector = jsonpath::Selector::default(); + let _ = selector.str_path(get_path()); + selector.value(&json); + let r = selector.select(); + if r.is_err() { + panic!(); + } +// println!("{:?}", serde_json::to_string(&r.expect("")).unwrap()); + } +} \ No newline at end of file diff --git a/benchmark/benches_lua_vs_rust/lib.rs b/benchmark/benches_lua_vs_rust/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/lua/lib.lua b/lua/lib.lua new file mode 100644 index 0000000..446b024 --- /dev/null +++ b/lua/lib.lua @@ -0,0 +1,25 @@ +local ffi = require('ffi') + +local ext + +if ffi.os == 'Linux' then + ext = 'so' +else + ext = 'dylib' +end + +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); +]] + +local jsonpathLibPath = os.getenv("JSONPATH_LIB_PATH"); +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)); + end +end \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index be993e8..f2a138e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,6 +135,8 @@ 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 parser; @@ -466,3 +468,71 @@ 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 +}