Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
11e67a3
(wip) benchmark analysis
ppete May 19, 2025
44bcf43
(nojson) remove json from jsonlogic internals
ppete May 24, 2025
83725ce
(nojson) reduce use of json::strings
ppete May 30, 2025
216916e
(wip) moved evaluation towards value variants
ppete Jun 3, 2025
5093ea4
(nojson) moved all operations to use variants
ppete Jun 6, 2025
9a47652
(nojson) manage array_value in value_variant
ppete Jun 6, 2025
32212d7
Update README.md
sbromberger Jun 1, 2025
2ff1d2f
changed to cmake_current_source_dir
sbromberger Jun 12, 2025
f0f8651
fix cmake source dir
sbromberger Jun 12, 2025
2fe9c20
complex benchmark
sbromberger Jun 17, 2025
ca17263
modify devcontainer
sbromberger Jun 18, 2025
1f00613
remove ssh mounts
sbromberger Jun 18, 2025
19872ae
changes to vscode and cmake presets to support vscode development
sbromberger Jun 19, 2025
061af6b
auto-generated benchmarks!
sbromberger Jun 19, 2025
f86ce73
clean up build process and reflect in README.md.
sbromberger Jun 19, 2025
1511868
fix CI
sbromberger Jun 19, 2025
d243a99
update run-tests
sbromberger Jun 19, 2025
0c4f422
cleanup
sbromberger Jun 19, 2025
e1927c6
(API) remove freestanding API
ppete Jun 20, 2025
018e6cd
(arrays) prepare for array optimizations
ppete Jun 23, 2025
3c574da
(ast) remove array_value_base
ppete Jun 30, 2025
f7e4186
(strings) convert string table to managed strings
ppete Jun 30, 2025
875e52a
(merge) Merge updates from master
ppete Dec 9, 2025
fed2c61
(merge) revise string management
ppete Dec 9, 2025
d81eb33
(feature) optimize membership tests
ppete Dec 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
"workspaceFolder": "/workspaces/jsonlogic/cpp",
"customizations": {
"vscode": {
"extensions": [

"extensions": [
"llvm-vs-code-extensions.vscode-clangd"
// add other extensions as needed
]
// add other extensions as needed
]
}
}
// "mounts": [
Expand Down
2 changes: 1 addition & 1 deletion cpp/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@
]
}
]
}
}
4 changes: 1 addition & 3 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ if(JSONLOGIC_ENABLE_TESTS)
set(CTEST_BUILD_FLAGS -j${N})
set(ctest_test_args ${ctest_test_args} PARALLEL_LEVEL ${N})
endif()

add_subdirectory(tests)
endif()

Expand All @@ -99,8 +98,7 @@ if(EXISTS "${COMPILE_COMMANDS_SOURCE}")
"${COMPILE_COMMANDS_LINK}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/build"
RESULT_VARIABLE symlink_result
)

)
if(symlink_result EQUAL 0)
message(STATUS "Created symlink: ${COMPILE_COMMANDS_LINK} -> ${COMPILE_COMMANDS_SOURCE}")
else()
Expand Down
5 changes: 4 additions & 1 deletion cpp/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# JsonLogic for C++

[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/LLNL/jsonlogic)

This is an implementation for [JsonLogic](https://jsonlogic.com/) for C++. The API uses the Boost JSON implementation (e.g.,
[Boost 1.82](https://www.boost.org/doc/libs/1_82_0/libs/json/doc/html/index.html)).

Expand All @@ -11,7 +13,7 @@ The library can be installed using cmake. From the top-level directory,
```
cmake --preset=default
cd build/release
make
make
```
Benchmarks can be made with
```
Expand All @@ -26,6 +28,7 @@ make testeval && make test
## Use

The simplest way is to create Json rule and data options and call jsonlogic::apply.

```cpp
#include <jsonlogic/logic.hpp>

Expand Down
3 changes: 0 additions & 3 deletions cpp/bench/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@

# set(CMAKE_BUILD_TYPE Debug)
include(FetchContent)

set(BUILD_TESTING OFF) # Disable Faker tests
FetchContent_Declare(faker
GIT_REPOSITORY https://github.com/cieslarmichal/faker-cxx.git
GIT_TAG main
)

FetchContent_MakeAvailable(faker)

FetchContent_Declare(
cxxopts
GIT_REPOSITORY https://github.com/jarro2783/cxxopts.git
Expand Down Expand Up @@ -69,7 +67,6 @@ target_include_directories(jl-bench-membership SYSTEM PRIVATE

target_link_libraries(jl-bench-membership PRIVATE jsonlogic faker-cxx)


add_executable(jl-bench-generic src/benchmark-generic.cpp)
target_include_directories(jl-bench-generic SYSTEM PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../bench/include
Expand Down
151 changes: 151 additions & 0 deletions cpp/bench/src/benchmark-complex1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#include <bench.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp>
#include <cstdio>
#include <faker-cxx/location.h>
#include <faker-cxx/number.h>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <jsonlogic/logic.hpp>
#include <string>
#include <vector>

std::string read_file(const std::string &filename) {
std::ifstream file(filename);
if (!file)
throw std::runtime_error("Failed to open file");

return {std::string((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>())};
}

const unsigned long SEED_ = 42;
static const size_t N_ = 1'000'000;
static const int N_RUNS_ = 3;
int main(int argc, const char **argv) try {

// x: double
// y: int
// z: string
// (x / y > 5) or (x < 3.0 and y > 5 and z == "foo") or (y == 4 and x > 10.0
// and "bar" in z)
std::string expr;
try {
expr = read_file("complex1.json");
std::cout << "Successfully read complex1.json from current directory"
<< std::endl;
} catch (const std::exception &) {
try {
expr = read_file("bench/src/complex1.json");
std::cout << "Successfully read complex1.json from bench/src/"
<< std::endl;
} catch (const std::exception &e) {
std::cerr << "Error: Could not find complex1.json: " << e.what()
<< std::endl;
// Print current working directory
std::cerr << "Current directory: " << std::filesystem::current_path()
<< std::endl;

throw;
}
}

std::span<const char *> args(argv, argc);

size_t N = N_;
if (argc > 1) {
N = std::stoul(args[1]);
}
size_t N_RUNS = N_RUNS_;
if (argc > 2) {
N_RUNS = std::stoul(args[2]);
}

size_t SEED = SEED_;
if (argc > 3) {
SEED = std::stoul(args[3]);
}

faker::getGenerator().seed(SEED);
std::vector<double> xs;
xs.reserve(N);
std::vector<uint64_t> ys;
ys.reserve(N);

std::vector<std::string> zs;
zs.reserve(N);

std::vector<std::string> strset{"foo", "bar", "baz", "quux", "foobar"};

// Create data
for (size_t i = 0; i < N; ++i) {
xs.push_back(faker::number::decimal<double>(0, 50));
ys.push_back(faker::number::integer<uint64_t>(0, 255));
auto ind = faker::number::integer<size_t>(0, strset.size() - 1);
zs.push_back(strset[ind]);
}

// JL 1

// Create jsonlogic benchmark
auto jv_xy = boost::json::parse(expr);
boost::json::object data_obj;
jsonlogic::logic_rule rule = jsonlogic::create_logic(jv_xy);

size_t matches = 0;
auto jl_lambda = [&] {
matches = 0;
for (size_t i = 0; i < N; ++i) {
data_obj["x"] = xs[i];
data_obj["y"] = ys[i];
data_obj["z"] = zs[i];
auto varaccess = jsonlogic::json_accessor(boost::json::value_from(data_obj));
auto v_xy = rule.apply(varaccess);

bool val = jsonlogic::truthy(v_xy);

if (val) {
++matches;
}
}
};

auto jl_bench = Benchmark("2ints-jl1", jl_lambda);

// JL 2

auto jl2_lambda = [&] {
matches = 0;
jsonlogic::logic_rule rule = jsonlogic::create_logic(jv_xy);
for (size_t i = 0; i < N; ++i) {
auto v_xy = rule.apply({xs[i], ys[i], zs[i]});
bool val = jsonlogic::truthy(v_xy);

if (val) {
++matches;
}
}
};

auto jl2_bench = Benchmark("2ints-jl2", jl2_lambda);

auto jl_results = jl_bench.run(N_RUNS);
std::cout << "- jl1 matches: " << matches << std::endl;
auto jl2_results = jl2_bench.run(N_RUNS);
std::cout << "jl2 matches: " << matches << std::endl;

jl_results.summarize();
jl2_results.summarize();
//~ jl_results.compare_to(cpp_results);
//~ cpp_results.compare_to(jl_results);

jl2_results.compare_to(jl_results);
return 0;
} catch (const std::exception &e) {
std::cerr << "Fatal error: " << e.what() << '\n';
return 1;
} catch (...) {
std::cerr << "Fatal unkown error\n";
return 2;
}
10 changes: 6 additions & 4 deletions cpp/bench/src/benchmark-equality.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,13 @@ int main(int argc, const char **argv) try {
size_t matches = 0;
auto jl_lambda = [&] {
matches = 0;
auto rule = jsonlogic::create_logic(jv_xy);

for (size_t i = 0; i < N; ++i) {
data_obj["x"] = xs[i];
data_obj["y"] = ys[i];
auto data = boost::json::value_from(data_obj);
auto v_xy = jsonlogic::apply(jv_xy, data);
auto accessor = jsonlogic::json_accessor(boost::json::value_from(data_obj));
auto v_xy = rule.apply(accessor);

bool val = jsonlogic::truthy(v_xy);

Expand All @@ -182,9 +184,9 @@ int main(int argc, const char **argv) try {

auto jl2_lambda = [&] {
matches = 0;
auto [rule, ignore1, igmore2] = jsonlogic::create_logic(jv_xy);
auto rule = jsonlogic::create_logic(jv_xy);
for (size_t i = 0; i < N; ++i) {
auto v_xy = jsonlogic::apply(rule, {xs[i], ys[i]});
auto v_xy = rule.apply({xs[i], ys[i]});
bool val = jsonlogic::truthy(v_xy);

if (val) {
Expand Down
18 changes: 14 additions & 4 deletions cpp/bench/src/benchmark-generic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,11 @@ int main(int argc, const char **argv) try {
data[i].push_back(fake_value(var_types[i]));
}
}

size_t matches = 0;

#if UNSUPPORTED
// JL1: Use boost::json::object for each row
size_t matches = 0;
auto jl1_lambda = [&] {
matches = 0;
boost::json::object data_obj;
Expand All @@ -127,11 +129,12 @@ int main(int argc, const char **argv) try {
}
};
auto jl1_bench = Benchmark("generic-jl1", jl1_lambda);
#endif /*UNSUPPORTED*/

// JL2: Use create_logic and pass values as tuple
auto jl2_lambda = [&] {
matches = 0;
auto [logic_rule, ignore1, ignore2] = jsonlogic::create_logic(rule);
auto jl2 = jsonlogic::create_logic(rule);
for (size_t i = 0; i < N; ++i) {
std::vector<jsonlogic::value_variant> args;
for (size_t v = 0; v < var_names.size(); ++v) {
Expand All @@ -141,26 +144,33 @@ int main(int argc, const char **argv) try {
else if (std::holds_alternative<double>(val))
args.push_back(std::get<double>(val));
else if (std::holds_alternative<std::string>(val))
args.push_back(std::get<std::string>(val));
args.push_back(jsonlogic::managed_string_view(std::get<std::string>(val)));
else if (std::holds_alternative<bool>(val))
args.push_back(std::get<bool>(val));
}
auto result = jsonlogic::apply(logic_rule, args);
auto result = jl2.apply(args);
bool val = jsonlogic::truthy(result);
if (val)
++matches;
}
};
auto jl2_bench = Benchmark("generic-jl2", jl2_lambda);

#if UNSUPPORTED
// Run benchmarks
auto jl1_results = jl1_bench.run(N_RUNS);
std::cout << "JL1 matches: " << matches << std::endl;
#endif /*UNSUPPORTED*/

auto jl2_results = jl2_bench.run(N_RUNS);
std::cout << "JL2 matches: " << matches << std::endl;
#if UNSUPPORTED
jl1_results.summarize();
#endif /*UNSUPPORTED*/
jl2_results.summarize();
#if UNSUPPORTED
jl2_results.compare_to(jl1_results);
#endif /*UNSUPPORTED*/
return 0;
} catch (const std::exception &e) {
std::cerr << "Fatal error: " << e.what() << '\n';
Expand Down
7 changes: 3 additions & 4 deletions cpp/bench/src/benchmark-membership.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,15 @@ int main(int argc, const char **argv) try {
std::cout << "initialized data_obj\n";
// data_obj["haystack"] = haystack;
size_t matches = 0;
jsonlogic::any_expr rule;
std::tie(rule, std::ignore, std::ignore) = jsonlogic::create_logic(jv_in);
jsonlogic::logic_rule rule = jsonlogic::create_logic(jv_in);

auto jl_lambda = [&] {
matches = 0;
for (size_t i = 0; i < N; ++i) {
data_obj["x"] = xs[i];
auto varaccess =
jsonlogic::data_accessor(boost::json::value_from(data_obj));
auto v_in = jsonlogic::apply(rule, varaccess);
jsonlogic::json_accessor(boost::json::value_from(data_obj));
auto v_in = rule.apply(varaccess);

bool val = jsonlogic::truthy(v_in);

Expand Down
Loading