Compare commits

...

255 Commits

Author SHA1 Message Date
Daniel Lemire 124a33c160 Update JSON example and add section on special cases
Removed code block formatting for JSON example and added a section header for special cases.
2025-09-11 19:32:38 -04:00
Daniel Lemire a0b870e9c2 Add example for deserializing 'Car' type
Updated the documentation to include an example of deserializing a 'Car' type using simdjson.
2025-09-11 19:31:15 -04:00
Daniel Lemire 68699eb73c release 4.0.0 2025-09-11 19:25:10 -04:00
Daniel Lemire 06453f1637 Remove Fuzzing Status badge from README
Removed Fuzzing Status badge from README.
2025-09-09 18:44:21 -04:00
Daniel Lemire eb828d97ba fixing a few typos 2025-09-09 18:41:00 -04:00
Daniel Lemire 00f84fb448 simplify 2025-09-09 14:21:14 -04:00
Daniel Lemire 5bf0954c6e hop 2025-09-08 19:26:34 -04:00
Daniel Lemire 3056d54fa3 adding instructions 2025-09-08 17:47:14 -04:00
Daniel Lemire 241b9f6ea7 adding bad/good player test 2025-09-08 17:04:52 -04:00
Daniel Lemire 015daad6a9 Merge branch 'master' of github.com:simdjson/simdjson 2025-09-05 19:50:05 -04:00
Daniel Lemire fc3b7766c0 typo fix 2025-09-05 19:49:57 -04:00
Daniel Lemire b5c78288e5 can we run the address sanitizer under VS in CI ? (#2435)
* can we run the address sanitizer under VS in CI ?

* fixed typo
2025-09-05 18:32:15 -04:00
Daniel Lemire bd86bb0cfb adding UTF-8 test 2025-09-05 14:56:02 -04:00
Daniel Lemire 4840a0347c extra documentation. 2025-09-05 14:44:53 -04:00
Daniel Lemire 7f68baec1e minor tweak 2025-09-05 08:01:04 -04:00
Daniel Lemire e2ea5fb8de This PR adds -> and * operators to our error types and it (#2433)
improves slightly the documentation.
2025-09-04 22:35:01 -04:00
Daniel Lemire 7dfd165ecd Merge branch 'master' of github.com:simdjson/simdjson 2025-09-04 09:46:07 -04:00
Daniel Lemire 4dfa0a2407 guarding 2025-09-03 16:45:09 -04:00
Daniel Lemire e252a21f65 removing expand workaround (#2431)
* removing expland workaround

* minor fixes
2025-09-03 13:30:35 -04:00
Daniel Lemire 89f2b634df guarding from benchmark + rvv 2025-09-02 13:06:25 -04:00
Daniel Lemire addca203d5 adding a 'car builder' benchmark (#2428)
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2025-08-26 09:16:18 -04:00
Felipe Monteiro 1825d7e88c Add "node:" prefix on fs import (#2426) 2025-08-25 10:21:13 -04:00
Daniel Lemire f87d6af3e9 fix issue 2424 (#2425) 2025-08-24 16:04:56 -04:00
Daniel Lemire 8fff57578c let us default on developer mode when building with VScode 2025-08-23 20:59:58 -04:00
Daniel Lemire 056d66926a Renames a few macros and extends slightly our basic builder (#2422)
* This PR renames a few macros and extends slightly our basic builder

* minor tuning
2025-08-20 09:01:58 -04:00
Daniel Lemire b434a50681 doing more to discourage value_unsafe(); (#2421) 2025-08-19 16:10:38 -04:00
Daniel Lemire e8ff2fa692 This is a small reorg of the new convert code so that we only expose 'simdjson::from' as experimental (#2418)
* This is a small reorg of the new convert code so that we only expose 'simdjson::from'.
This can be changed in a future release, but we don't want our users to start depending
on code that we might need to change.

* marking simdjson::from as experimental

* updating tests to match recent changes
2025-08-15 15:13:36 -04:00
Daniel Lemire 1ebf115b2b improving the documentation of raw json access (#2416)
* improving the documentation of raw json access

* minor fix

* documentation update

* guarding for exceptions

* fixing exception issue

* fix test

* update.

* saving comments

* more technical fixes

* correcting path in ci test

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2025-08-15 10:26:20 -04:00
Joshua Gawley 172fbe785d Use std::string_view in parser.load function (#2417) 2025-08-15 10:26:03 -04:00
Daniel Lemire a10f096af2 let us just say that it should not work 2025-08-14 11:49:42 -04:00
Daniel Lemire a85e474360 documentation update 2025-08-14 09:33:11 -04:00
Daniel Lemire 05752d6c6f Update ubuntu22-cxx20.yml 2025-08-13 21:17:41 -04:00
Joseph Olabisi fb9a689dff Adding DOM support for json path with wildcard (#2346)
* wip brute-force wildcard for json_path

* wip - bruteforce surface wildcard with result

* partially handle keys with wildcard

* wip - nested paths/pointers on wildcard results

* wip

* wip - handling child properties of wildcard result

* done - handling child properties of wildcard result

* fix key

* add support for wildcard for arrays

* handle array INCORRECT_TYPE

* rename at_path_new to at_path_with_wildcard

* add benchmark

* fix benchmark

* use memcmp

* minor improvements

* refactor to tail recursion

* nit

* approximately 30% improvement in runtime

* nit

* corrected logic

* nit

* cleanup

* add some initial tests

* cleanup 2

* modified:   CMakeLists.txt

* cleanup

* cleanup

* cleanup

* cleanup

* restore examples/quickstart/CMakeLists.txt

* cleanup

* restore quickstart.cpp

* revert array-inl.h

* cleanup array-inl.h

* revert object-inl.h

* cleanup object-inl.h

* cleanup

* nit

* nit

* add test

* address some feedbacks

* address additional feedbacks (copilot)

* final changes based on feedback - reduce string allocations

* fix bug in object-inl.h

* fix logic for wildcards inside arrays

* add test for wildcard in nested array

* refactor and create util for getting key and json path

* minor error handling

* refactor process_json_path_of_child_element from recursion to loop in order to prevent stack overflow

* fix bug with array, add boundary check to get_next_key_and_json_path function

* add more tests

* fix boundary check and unnecessary string allocation

* minor changes

* fix

* fix jsonpathutil

* remove unneccessary string allocation

* some minor fixes

* documentation

* removing printout

* various fixes

* reorg of the CI test file

---------

Co-authored-by: Daniel Lemire <daniel@lemire.me>
2025-08-13 21:16:32 -04:00
Daniel Lemire 472f818fe5 trimming repeated code 2025-08-13 20:40:04 -04:00
Daniel Lemire 5c9c133632 improve how we pad strings. (#2415) 2025-08-13 18:41:14 -04:00
Daniel Lemire faf921bc7e introducing a thread-local parser and removing ranges (#2412)
* introducing a thread-local parser

* adding functionality to release the memory

* some more documentation.

* fixing build

* adding benchmarks for 'from'

* generalizing the code somewhat.

* adding tests, fixing the benchmark (now with arrays and streams), and a
minor update to document_stream

* adding missing files (I forgot to check them).

* We cannot use [[nodiscard]] without guarding it, it is C++17

* fixing the cmake

* marking it as experimental

* removing ranges support (it is too experimental)

* putting back documentation.

* guarding SIMDJSON_CONSTEVAL more carefully.

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2025-08-13 18:08:25 -04:00
Daniel Lemire 626eedc3d8 documentation update 2025-08-13 16:24:55 -04:00
Daniel Lemire 294aa64f94 Update bug_report.md 2025-08-13 15:01:53 -04:00
Daniel Lemire 397b40e574 more documentation tuning 2025-08-11 15:39:10 -04:00
Daniel Lemire fdc93528d1 updating documentation. 2025-08-11 13:48:01 -04:00
Francisco Geiman Thiesen 3e00a431a0 Merge pull request #2406 from simdjson/fix-convert-ci-failures
Introducing simplified api with from/to adapters (thanks to @the-moisrex for driving this) and fixing all the CI errors.
2025-08-08 10:42:32 -07:00
Pavel Novikov d3626c6a37 removed now unnecessary <utility> header, (#2411)
disabled copy ctor and assignment for `vector_with_small_buffer`
2025-08-08 08:39:28 -04:00
Daniel Lemire 686a1869c8 updating commit 2025-08-08 08:38:41 -04:00
Daniel Lemire 4c0f86db44 minor fixes 2025-08-07 23:59:30 -04:00
Daniel Lemire 6c4c934457 added a new test. 2025-08-07 18:11:44 -04:00
Daniel Lemire fcae795042 minor fix 2025-08-07 16:05:24 -04:00
Daniel Lemire b7c51156b9 Sped up serialization fix (#2409)
* clang format

* added `chars()` method

* implemented vector with small buffer instead of `std::vector`

* added missing <utility> header

* minor fixes

---------

Co-authored-by: Pavel Novikov <dev-ape@yandex.ru>
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2025-08-07 16:04:20 -04:00
Pavel Novikov 662e3d96c7 Sped up serialization by 10..70%-ish (#2408)
* clang format

* added `chars()` method

* implemented vector with small buffer instead of `std::vector`

* added missing <utility> header
2025-08-07 16:03:39 -04:00
Daniel Lemire 64009f7063 more code simplification. 2025-08-07 13:25:18 -04:00
Daniel Lemire 74fb3ecac7 removing another pragma and some code simplification. 2025-08-07 13:23:21 -04:00
Daniel Lemire 26abf1d180 removing macros 2025-08-07 11:39:51 -04:00
Francisco Geiman Thiesen e878dc6be3 Addressing @the-moisrex review. 2025-08-06 03:05:32 +00:00
Francisco Geiman Thiesen efa03f5733 Fix to_bad_array test to handle both exception and non-exception error cases
The test was failing in CI with g++-13 because it only handled the
exception case. However, the array() method is marked noexcept and
returns a simdjson_result that may contain an error code instead of
throwing an exception.

This fix checks for both cases:
1. If array_result.error() is not SUCCESS, verify it's INCORRECT_TYPE
2. If no error is returned initially, the exception may be thrown when
   iterating over the result

This ensures the test passes regardless of whether the error is
reported via error code or exception.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-05 20:51:17 +00:00
Francisco Geiman Thiesen 8519623257 Fix unused parameter warning in json_iterator::assert_valid_position for SIMDJSON_CLANG_VISUAL_STUDIO
Added (void)position; to suppress unused parameter warning when compiling with SIMDJSON_CLANG_VISUAL_STUDIO defined, where the position parameter isn't used in the SIMDJSON_ASSUME statements.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-05 19:27:01 +00:00
Francisco Geiman Thiesen 484a092c31 Adding a few tests for the simdjson::to adapter. 2025-08-05 16:29:59 +00:00
Francisco Geiman Thiesen 8bb520adbf Merge master and regenerate amalgamated files
Resolved conflicts by regenerating the amalgamated single-header
files (simdjson.h, simdjson.cpp, and singleheader.zip) using the
amalgamate.py script after merging latest changes from master.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 20:16:47 +00:00
Francisco Geiman Thiesen ec8e6a6758 Fix -Werror=effc++ warnings and test issues in convert.h
This commit addresses multiple issues:

1. Fixed -Werror=effc++ warnings by using #pragma to disable the
   warning for constructors that cannot initialize all members in
   the member initialization list due to error handling requirements.

2. Added proper error tracking (m_error member) to handle cases where
   document initialization fails, preventing segfaults when using
   invalid documents.

3. Fixed lifetime issues in tests where temporary auto_parser objects
   were being used, causing dangling references. Tests now properly
   store the parser object before using it.

4. Simplified range adaptor tests that were expecting features not
   yet implemented in simdjson's ondemand API.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 19:26:58 +00:00
Francisco Geiman Thiesen 769528b6c0 Fix segmentation fault in auto_parser constructor for C++20
The issue was that we were trying to initialize ondemand::document
directly from simdjson_result<ondemand::document> in the member
initializer list. This caused a segfault in C++20 builds.

The fix explicitly handles the simdjson_result in the constructor
body, checking for errors and using value_unsafe() to extract the
document. This avoids potential issues with implicit conversions
and ensures proper error handling.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 19:01:42 +00:00
Francisco Geiman Thiesen 9fbc577be7 Fix member initialization order warning in auto_parser
The compiler was warning about member initialization order mismatch.
C++ initializes members in the order they are declared in the class,
not the order they appear in the initializer list.

Fixed by reordering member declarations to match the initialization
order needed: m_doc must be initialized before m_parser since we
need to call parser.iterate() before moving the parser.

This fixes the -Werror=reorder compilation error in CI.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 18:48:07 +00:00
Francisco Geiman Thiesen a7c95e9cc8 Fix initialization order in auto_parser constructor
The issue was that we were calling m_parser.iterate() after moving
the parser, which could leave it in an invalid state. In C++20,
this might behave differently than C++17.

Fixed by reordering the member initializer list to call
parser.iterate() BEFORE moving the parser into m_parser.

This ensures the document is created while the parser is still valid.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 18:36:14 +00:00
Francisco Geiman Thiesen 248d4eb2aa Try using parentheses instead of braces for document initialization
The issue might be related to how brace initialization vs parentheses
initialization handles implicit conversion from simdjson_result<document>
to document. This could be compiler-specific behavior.

Using parentheses initialization to ensure the conversion operator
is called properly.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 18:12:50 +00:00
Francisco Geiman Thiesen 559493e2bf Fix auto_parser constructor to handle document initialization correctly
The issue was that the auto_parser constructor was using implicit
conversion from simdjson_result<document> to document, which could
cause issues with certain implementations (particularly fallback).

Changed to use value_unsafe() to explicitly extract the document
after the parser is fully initialized. This ensures the document
is in a valid state for subsequent operations.

This fixes the ondemand_convert_tests failure in CI with clang++-16.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 15:28:40 +00:00
Francisco Geiman Thiesen 75acae5c46 Fix C++20 compatibility issues in convert.h
- Remove constexpr from functions that call non-constexpr methods
- The no_errors and to<T> adaptors were marked constexpr but call
  simdjson_result methods that are not constexpr in C++20
- This was causing compilation failures in CI for C++20 builds
- Tests now compile and pass with both C++17 and C++20

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 06:01:50 +00:00
Francisco Geiman Thiesen bd761ef573 Fix ranges support for C++20 compatibility
Instead of disabling the feature, provide C++20-compatible implementation
of the pipe operators for ranges support. The range_adaptor_closure is
C++23-only, so we implement our own pipe operators for C++20.

This preserves the core functionality of the PR while ensuring
compatibility across different compiler versions.
2025-08-03 05:39:10 +00:00
Francisco Geiman Thiesen 76ed73f07f Disable ranges-dependent tests to fix compilation
The test_no_errors() and to_clean_array() tests depend on the C++23
ranges features that we disabled. This commit conditionally compiles
these tests out when ranges support is disabled.
2025-08-03 05:34:34 +00:00
Francisco Geiman Thiesen 1b59b38de8 Disable C++23 ranges features to fix CI compatibility
The ranges features were causing compatibility issues across different
compilers and platforms. Disabling them for now until C++23 support
is more widespread.

This should fix the remaining Ubuntu and Windows CI failures.
2025-08-03 05:11:31 +00:00
Francisco Geiman Thiesen 4794b5d936 Update amalgamated files with convert.h fixes
Regenerate singleheader/simdjson.cpp and singleheader/simdjson.h
to include all the fixes for CI compatibility issues.
2025-08-02 17:06:45 +00:00
Francisco Geiman Thiesen 114f14924c Simplify ranges feature detection for C++23
Only enable range_adaptor_closure features when compiling with C++23
or later, as this feature is not available in C++20 implementations.
2025-08-02 16:04:58 +00:00
Francisco Geiman Thiesen e94825c9a8 Fix preprocessor check for __cpp_lib_ranges_zip
Add defined() check before comparing the value to avoid preprocessor
errors in compilers where this macro doesn't exist (like g++-13
with certain configurations).
2025-08-02 15:43:31 +00:00
Francisco Geiman Thiesen 333fd72f98 Fix CI failures in convert.h implementation
- Fix deprecated reflect_value warning by using reflect_constant
- Fix std::const_iterator C++23 requirement by using auto_iterator
- Fix C++23 std::ranges::range_adaptor_closure availability check
- Add convert.h to main simdjson.h includes

These changes ensure compatibility across different C++ standards
and compiler versions, fixing the Ubuntu CI failures.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-02 15:04:25 +00:00
Daniel Lemire dd4d02617f removing unnecessary hack 2025-07-31 18:40:54 -04:00
Daniel Lemire e28d63334e fix 2025-07-31 13:07:29 -04:00
Daniel Lemire 3d1ab87ecb Merge branch 'master' into release_candidate_4_0_0 2025-07-31 10:24:54 -04:00
Borislav Stanimirov d365cfb4a1 remove cmake_policy (#2404)
* properly gitignore Visual Studio artifacts

* remove cmake_policy
2025-07-31 10:18:28 -04:00
Daniel Lemire b9e308a727 marking them as 'really inline' 2025-07-28 11:47:09 -04:00
Brad Bramble aa6817d5aa Fix linker errors for downstream users caused by non-inline symbols in header (#2403) 2025-07-28 11:44:32 -04:00
Francisco Geiman Thiesen 90e4a66c93 Bringing a bit more use-cases for reflection based serializations (optional). Also adding string-based enum handling as requested on X. (#2395)
* Adding type validation, enhancing optional type support and adding test a few more tests.

* Adding support for string-based enum serlalization and deserialization.

* Removing unintentional endline.

* Removing trailing whitespace.

* Adding simpler api as suggested by moisrex.

* Removing explicit optiona<int> and optional<std::string> references and using concepts instead! Credit goes to Lemire for pointing this out and suggesting a concepts based approach here.

* Removing tests that are not relevant for this branch.

* Removing api related changes. That will be done by moisrex.

* Removing unnecessary new endlines.

* Removing tests related to api changes and cleaning-up irrelevant tests.

* removing broken reference

* Removing trailing whitespace
2025-07-23 09:24:05 +02:00
evbse e6240f18c4 Add version to amalgamated files (#2400) 2025-07-23 09:20:06 +02:00
M. Bahoosh d43fb6ff84 test for no_errors 2025-07-21 05:24:12 -10:00
M. Bahoosh 5eec29a6db Moving iterator's storage to auto_parser 2025-07-21 05:18:13 -10:00
M. Bahoosh 228501f786 From/To adaptors 2025-07-21 03:22:20 -10:00
M. Bahoosh 9d9f2427c5 Make auto_parser and auto_iterator comply with ranges. 2025-07-20 03:22:07 -10:00
M. Bahoosh 8d53840253 Removing unneeded code 2025-07-19 08:37:04 -10:00
M. Bahoosh 0a82fb110f Auto Iterator 2025-07-19 08:35:49 -10:00
M. Bahoosh 11f273c580 Moving ondemand::document into auto_parser 2025-07-19 04:37:13 -10:00
M. Bahoosh 31662531ac Basic Auto Parser 2025-07-19 03:58:21 -10:00
Daniel Lemire 97eb557388 Merge branch 'release_candidate_4_0_0' of github.com:simdjson/simdjson into release_candidate_4_0_0 2025-07-16 12:08:03 -04:00
Daniel Lemire 41ced28821 adding a space 2025-07-16 12:07:49 -04:00
Daniel Lemire 1078eb4034 minor update to the release candidate (#2394) 2025-07-16 12:05:09 -04:00
evbse 6029af75b3 Improve fallback implementation (#2393) 2025-07-15 14:39:09 -04:00
Daniel Lemire 0cd774097f [skip ci] doc fixes 2025-07-14 23:46:30 -04:00
Daniel Lemire b5e27af4da release candidate 4.0.0 2025-07-14 15:55:00 -04:00
Daniel Lemire c806e955c4 C++26 static reflection (#2282)
* Initial work on JSON builder

* moving the files back to ondemand for now.

* tweak

* more later

* update

* minor edits

* dropping vs arm (missing support)

* adding tests. we still specialized write_string_escaped

* tweaking

* fix typo

* tweaking the approach

* minor fix

* missing store

* another missing store

* Attempt at fixing failing serialization tests. (#2292)

* Fixing appeand_float typo (#2294)

* applying a couple of fixes

* updating single header

* fix for pre C++17 if constexpr

* Fixing unused argument problem and updating the singleheader file

* various pedantic fixes

* Sketch of builder

* reordering.

* simplify

* Adding draft of static reflection based deserialization

* Updating simdjson singleheader

* patching the automated deserialization.

* automated

* Adding support for smart pointers of user defined types.

* Adding specialization for smart pointers for basic types. I think it is highly likely that this can be done in a more generic way.

* Referncing a later version of rapidjson that fixed the issue related with assignment attempt of a const variable for GenericStringRef class.

* guarding the tests

* adding documentation for string_builder

* saving

* rename to 'append'

* saving

* non-functional benchmarks (#2342)

* non-functional benchmarks

* Fix typo

* various fixes

* tweaking

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
Co-authored-by: Francisco Geiman Thiesen <franciscogthiesen@gmail.com>

* tuning

* various minor fixes

* minor tweak

* minor simplification

* updating amal

* adding a cast

* update

* fancy casting

* removing dead code

* Pushing latest changes. CITM benchmark is still not working.

* Still not working, but now I am getting only 10 errors.

* add static reflection benchmark to 'large random' benchmark and allows (#2349)

deserialization (with static reflection) from objects and arrays.

Co-authored-by: Daniel Lemire <dlemire@lemire.me>

* Removing std::map from CitmCatalog definition, since that is not currently supported.

* Added free to rust bench, segfault is still happening..

* The syntax changed: ^E became ^^E. (#2350)

* The syntax changed: ^E became ^^E.

* guarding

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>

* Adding support for string_view_keyed_map types.

* Adding concepts as a conditional include.

* updating single-header

* Adding concepts to ondemand deps

* rust benchmark is finally working

* Fixing small typo in docs.

* adding docker config and instructions so that our users can test the static reflection (#2358)

* adding docker config and instructions so that our users can test the
static reflection

* completing the instructions

* pruning white spaces

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>

* minor optimizations on the JSON builder branch

* avoiding undef behaviour

* saving

* somewhat nicer builder

* make it possible to run just one benchmark

* adding linux perf

* fixing minor issue

* updating swar

* Adding real world compilation benchmark (#2379)

* Adding compilation benchmark for json parsing with and without reflection

* Moving it to the benchmark folder, also reducing a bit the number of iterations.

* Removing script from root folder.

* Reducing number of iterations

* Update benchmark/benchmark_reflection_usage_compilation.sh

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Update benchmark/benchmark_reflection_usage_compilation.sh

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Update benchmark/benchmark_reflection_usage_compilation.sh

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Making the script more customizable and also test whether the compiler being used supports reflection before actually running the benchmark

---------

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Using define_static_string from  https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3491r2.html (#2389)

* Applying changes needed after latest reflection paper updates.

* Working, but no template for yet.

* Updating single-header to incldue the use of define_static_string.

* copying over master

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
Co-authored-by: Francisco Geiman Thiesen <franciscogthiesen@gmail.com>
2025-07-14 15:43:52 -04:00
Daniel Lemire 4acaf2ea1c improving DOM ranges test and silencing a warning (#2385)
* improving DOM ranges test and silencing a warning

* moving test_main to macros (even though it is not a macro)

* moving test_main
2025-07-04 15:46:50 -04:00
Daniel Lemire 86adda06d0 clarifying NDEBUG usage (#2388)
* clarifying NDEBUG usage

* init
2025-07-04 15:46:37 -04:00
Daniel Lemire 8df5f96eba guarding the POSITION_INDEPENDENT_CODE (#2386) 2025-06-30 16:41:49 -04:00
xkszltl 937667796c Replace -fPIC with POSITION_INDEPENDENT_CODE. (#2383) 2025-06-30 11:26:27 -04:00
Cuda Chen 7d86b04247 Add document and tests of std::ranges support of DOM API (#2381)
* Add document and tests of std::ranges support of DOM API

* Alter lambda

* Fix require of range
2025-06-24 15:13:44 -04:00
Daniel Lemire 265022ad8f Fixing MSYS2 issue (#2380)
* Google Benchmarks does not support MSYS2.

* narrowing it down
2025-06-21 21:26:52 -04:00
Daniel Lemire 0c0ce1bd48 3.13.0 2025-06-04 00:46:03 -04:00
Daniel Lemire f64c004cb7 making it easier to convert to std::string (#2378)
* making it easier to convert to std::string

* typo

* guarding C++20

* bad semi-colon
2025-06-04 00:22:10 -04:00
Daniel Lemire d7d19db6ae Emscripten (#2377)
* emscripten

* cmake_policy

* space
2025-06-03 09:10:59 -04:00
Daniel Lemire a260c967ed adding test for issue 2375 (#2376)
* adding test for issue 2375

* update

* removing space
2025-06-02 09:24:40 -04:00
Daniel Lemire 16e390d81a fix for issue 2373 (#2374)
* fix for issue 2373

* bumping google bench

* removing json11 since dropbox retired it

* removing json11 as a dependency
2025-05-31 00:56:19 -04:00
Daniel Lemire 8d34e14000 Update dom.md 2025-05-31 00:27:28 -04:00
shuiyisong b717136fd9 chore: add greptimedb in real world usage (#2372) 2025-05-07 16:33:24 -04:00
Daniel Lemire f3ac74caf8 slightly better documemntation of the C++20 features (#2371)
* slightly better documemntation of the C++20 features

* adding another remark
2025-04-23 18:32:51 -04:00
Daniel Lemire 2369927661 Moving from Ubuntu 20 to 24 (#2366) 2025-04-18 17:56:46 -04:00
Daniel Lemire 818c0491a1 Treat -0 as -0.0 when SIMDJSON_MINUS_ZERO_AS_FLOAT is set (#2364)
* In the DOM API, treat -0 as -0.0

* documenting...

* adding it to On-Demand
2025-04-18 13:56:02 -04:00
Daniel Lemire 9f14f90a64 [documentation] We run on practically all systems. (#2365)
* We run on practically all systems.

* Update doc/basics.md

Co-authored-by: Antoine Pitrou <pitrou@free.fr>

---------

Co-authored-by: Antoine Pitrou <pitrou@free.fr>
2025-04-18 13:48:13 -04:00
Daniel Lemire 00843d2711 better documentation 2025-04-16 11:45:52 -04:00
Daniel Lemire 2887a17bab bumping up nlohmann/json 2025-04-13 14:43:30 -04:00
huangqinjin d84c934768 do not try to include <string_view> without C++17 for MSVC (#2360)
Doing that will emit warning at https://github.com/microsoft/STL/blob/vs-2022-17.13/stl/inc/string_view#L12.
2025-03-28 19:23:15 -04:00
huangqinjin 7cec7c7ae4 fix VS2017 exception detection (#2359)
MSVC option /EHsc defines __cpp_exceptions only in VS2019 and above,
_CPPUNWIND should be used before VS2019.

https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros#microsoft-specific-predefined-macros
2025-03-28 19:22:56 -04:00
Daniel Lemire c52b010a57 missing string_view include (#2361) 2025-03-28 19:22:38 -04:00
Daniel Lemire ee301599b1 Update basics.md 2025-03-28 11:53:07 -04:00
Daniel Lemire 7382dc2be8 preparing patch release 3.12.3 (#2357) 2025-03-28 11:26:19 -04:00
Daniel Lemire 8c14e0c56f fixing issue 2354: get_bool() on document with trailing spaces (#2356) 2025-03-27 15:01:10 -04:00
Daniel Lemire a9a62feb75 Map proto (#2353)
* std::map support

* completed

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2025-03-23 16:32:48 -04:00
Ezequiel Ramis Folberg b9228b4d3c Update README.md (#2352) 2025-03-22 15:49:02 -04:00
Dirk Stolle 726c3eb611 fix several typos (#2348) 2025-03-17 09:57:08 -04:00
Daniel Lemire 4cdc4f18ef documenting fatal errors 2025-03-13 13:26:50 -04:00
Dirk Eddelbuettel f3b034ac38 Address 'whitespace in literal' decreation warning from clang++-20 2025-03-06 14:28:01 -06:00
Daniel Lemire 9c2e8a8f39 upgrading uraimo/run-on-arch-action 2025-03-01 14:32:13 -05:00
yun dfa43f6cdd doc: replace ndjson[dot]org with ndjson spec (#2340)
the `ndjson[dot]org` expired, and point to incorrect website with malware.
more in https://github.com/ndjson/ndjson.github.io/issues/24

follow up #2234
2025-02-21 09:07:39 +00:00
Daniel Lemire 797e61742c release bump 2025-02-14 16:12:28 -05:00
Daniel Lemire f289412e0a fixing performance issue under Zen 4 processors (#2335)
* fixing performance issue under Zen 4 processors

* trying something else

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2025-02-14 16:11:36 -05:00
Daniel Lemire 7bd79b4445 Update README.md 2025-02-14 16:03:01 -05:00
Daniel Lemire 078e2c9073 patch release 3.12.1 2025-02-11 13:46:03 -05:00
Daniel Lemire d7b6b20511 creating zip files. 2025-02-11 13:43:34 -05:00
Daniel Lemire dbea3bbd62 Update README.md 2025-01-29 11:48:36 -05:00
Daniel Lemire e422933414 release 3.12.0 2025-01-27 20:35:57 -05:00
Daniel Lemire de4d69b367 Introducing dual licensing (#2328)
* Introducing dual licensing

* adding missing file
2025-01-27 20:34:35 -05:00
Paul Dreik b8675a7f7b fix fuzzing when running on icelake (#2327) 2025-01-26 05:36:33 +01:00
Paul Dreik 5642bb93a4 fix fuzzer CI job (#2324) 2025-01-16 13:33:53 -05:00
Daniel Lemire 1b23a77e03 3.11.6 2025-01-14 21:33:29 -05:00
Daniel Lemire 57699bfed8 release candidate (#2323) 2025-01-09 11:01:48 -05:00
Daniel Lemire 648303b26a typo 2025-01-08 21:29:11 -05:00
Daniel Lemire 9008960e36 fixed comment 2025-01-08 20:35:30 -05:00
Daniel Lemire 8a9e8a1792 fixing issue 2322 2025-01-08 20:28:25 -05:00
Daniel Lemire ba33e9e78f release 2025-01-06 19:33:53 -05:00
Daniel Lemire d98b351eef let us be more careful with concepts, we require __cpp_concepts >= 201907L and check for old versions of Apple clang (#2321)
* let us be more careful with concepts, we require __cpp_concepts >= 202002L

* checking Apple version

* let us be more specific

* removing extra endif
2025-01-06 19:23:09 -05:00
Daniel Lemire 5488dca126 Accidentally, it appears that PPC64 was always disabled by default. (#2320) 2025-01-03 11:01:04 -05:00
Daniel Lemire 7712ecf164 adding char bit check (pedantic) (#2316) 2024-12-31 14:34:13 -05:00
Paul Dreik 2803ca3093 use clang 19 in fuzzer job (#2315)
* use clang 19 in fuzzer job

* fix syntax error in msys2-clang.yml
2024-12-22 15:00:58 -05:00
Daniel Lemire e7f2463920 some tweaks 2024-12-17 16:27:31 -05:00
Daniel Lemire 5bfa0b098c patch release 3.11.3 (#2313)
* preparing patch release 3.11.3
2024-12-12 13:38:22 -05:00
Daniel Lemire f7ba9cb11b Update basics.md 2024-12-10 11:47:21 -05:00
Daniel Lemire d4bf0cc7ec sync 2024-12-09 21:36:43 -05:00
Daniel Lemire 2fbbea0b15 Merge branch 'master' of github.com:simdjson/simdjson 2024-12-09 21:36:12 -05:00
Daniel Lemire c16486f702 release candidate 3.11.2 2024-12-09 21:35:21 -05:00
zhanglistar f615112093 Disabling memory sanitizer with parse_string (#2310) 2024-12-09 10:20:33 -05:00
Daniel Lemire 6bbcbfbb95 version 3.11.1 2024-12-07 11:13:00 -05:00
Valery Mironov e6578fea39 Fix missing override (#2305) 2024-12-07 11:12:25 -05:00
Joyee Cheung 6fb050d04e fix: fix deprecation from -Wdeprecated-literal-operator (#2307)
Otherwise simdjson doesn't build with V8's Node.js fork, which
uses -Werror,-Wdeprecated-literal-operator and latest version
of clang.
2024-12-07 11:12:14 -05:00
Daniel Lemire b4242d3b4f release candidate (#2304) 2024-12-05 22:05:39 -05:00
Daniel Lemire cbfe2d4a21 Update vs17-arm-ci.yml 2024-12-02 20:24:24 -05:00
halx99 79126ca323 fix: processor checks inconsistently problem (#2299) 2024-12-02 20:23:28 -05:00
Daniel Lemire 3f87f958d7 documentation 2024-12-02 00:06:20 -05:00
Daniel Lemire cab383e1de Update README.md 2024-11-30 20:43:20 -05:00
Daniel Lemire 9b314922a1 additional documentation 2024-11-08 11:53:34 -05:00
Daniel Lemire 587beae307 prevent int to pointer implicit conversion for operator[](const char *) (#2286) 2024-11-01 18:11:43 -04:00
Daniel Lemire f53981d945 Update dom.md 2024-11-01 12:23:35 -04:00
Daniel Lemire 41b4ffd6aa Update basics.md 2024-11-01 12:15:40 -04:00
Daniel Lemire 6cd8a6d776 adding pad function (#2283) 2024-10-29 21:26:01 -04:00
Daniel Lemire 3c0d032ded Candidate for C++20 deserialization features (#2276)
* tag_invoke based custom types (#2219)

* tag_invoke based custom types

Now you can use tag_invoke to add a custom type or a group of custom types.

* Fixing macro usage + Fixing noexcept

* Fixing the usage of #include

We don't need <concepts> at all seems like it

* Fixing tag_invoke impl for MSVC

* Making `tag_invoke` to support `ondemand::document` as well + docs (#2228)

* Making `tag_invoke` to support `ondemand::document` as well + docs

* Fix typos and doc update by @lemire

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Better docs by @lemire

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* Preserving the old, disallowing in the new

I'm disabling `document::get() &&` if the user has provided a `tag_invoke`d version; otherwise, we retain the compatibility.

---------

Co-authored-by: Daniel Lemire <daniel@lemire.me>

* fix: correct small issues with deserialize (#2232)

* Extending the deserialization code with more defaults + docs (#2233)

* Make custom types easier with some predefined cases + docs

* missing include

* adding Ubuntu 24 CXX 20

* using concepts all the way

* minor tweak

* tiny tweak

* tweaks

* more tweaking

* saving

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>

* Making `tag_invoke` a "put" as opposed to a "get" (#2256)

* fix: add tests related to issue 2227 (#2229)

* fix: add tests related to issue 2227

* avoiding name clash

* pedantic fix

* deprecate rvalue get on document

* selectively deprecating

* Fix ndjson spec link (#2234)

* fix ndjson spec link

The link in the readme of parse_many links to a casino spam site

* fix link

* [no-ci] Update README.md

* Make simdjson compile again

* Enable SIMDJSON_SINGLEHEADER=OFF in VS Code

With singleheader on, clangd can't find the right
include files.

* Add missing include directives to static build targets of simdjson. (#2240)

* adding a warning

* adding warning regarding SIMDJSON_BUILD_STATIC_LIB

* release candidate

* pedantic viable size

* Making tag_invoke a feeder instead of a producer

* adding missing undef silencer (#2253)

* Ignore pragma once when amalgamating source files (#2248)

With gcc it causes an error in `simdjson.cpp`:
```
simdjson.cpp:548:9: warning: #pragma once in main file
  548 | #pragma once
      |         ^~~~
```

It had previously been commented out in:
https://github.com/simdjson/simdjson/commit/6ef555e6fb79363fae057a9a46b52cd208d9e305

However, this was lost in an upgrade:
https://github.com/simdjson/simdjson/commit/2a4ff7346813b120f2b5b40e95d69352b593cc9c

* Update CI (#2254)

* adding missing undef silencer

* Updating CI

* more fixes

* fix

* big endian fix

* Moving to the new tag_invoke signature

* Fix nlohmann ambiguity on C++23-enabled clang

* Revert "Merge branch 'master' of https://github.com/simdjson/simdjson into builder_development_branch_extra"

This reverts commit 3eeecbab34, reversing
changes made to 6858b208b4.

---------

Co-authored-by: Daniel Lemire <daniel@lemire.me>
Co-authored-by: Sasha Lopoukhine <superlopuh@gmail.com>
Co-authored-by: John Keiser <john@johnkeiser.com>
Co-authored-by: Tan Li Boon <undisputed-seraphim@users.noreply.github.com>
Co-authored-by: tobil4sk <tobil4sk@outlook.com>

* update CI on the builder_development_branch (no code change) (#2262)

* typo

* General madness simpler, no simpler!!! (#2267)

* Minimal tag_invokes for STL types

* simpler madness

* adding a comment

* missing file

* minor tweaks to style

* fixing incorrect max/min usage

* updating single

* simplify

* validating the idea

* putting back the concept

* moving the include

* guarding

* Cheap General Madness (#2268)

* Some General Concepts and their deserializations

* Resolving ambiguity

* Add missing #include

* C++20 custom deserializer: better documentation (#2269)

* mostly a documentation update.

* missing cpp

* [no-ci] fix comment

* various minor fixes

---------

Co-authored-by: Daniel Lemire <dlemire@lemire.me>

---------

Co-authored-by: M. Bahoosh <12122474+the-moisrex@users.noreply.github.com>
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
Co-authored-by: M. Bahoosh <moisrex@gmail.com>

* minor update

* More documentation regarding builder (#2270)

* minor update

* more improvment to our documentation (builder branch)

* putting back missing functions

* merge candidate

---------

Co-authored-by: M. Bahoosh <moisrex@gmail.com>
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
Co-authored-by: Sasha Lopoukhine <superlopuh@gmail.com>
Co-authored-by: John Keiser <john@johnkeiser.com>
Co-authored-by: Tan Li Boon <undisputed-seraphim@users.noreply.github.com>
Co-authored-by: tobil4sk <tobil4sk@outlook.com>
Co-authored-by: M. Bahoosh <12122474+the-moisrex@users.noreply.github.com>
2024-10-18 20:37:38 -04:00
Daniel Lemire 36f5dbcb75 documentation for threading... (#2277)
* documentation for threading...

* remove const

* doc
2024-10-11 12:34:11 -04:00
Daniel Lemire 49901fb254 doc 2024-10-10 21:32:51 -04:00
Daniel Lemire c066b5421b Jsonpath (#2272)
* Add support for JSONPath with '$' prefix and integrate into dom::at_path (#2266)

- Updated `json_path_to_pointer_conversion` to support JSONPath starting with the '$' prefix, while maintaining compatibility with the existing implementation.
- Moved `json_path_to_pointer_conversion` to a separate header file for better modularity and to support JSONPath queries in `dom` mode.
- Implemented `at_path` functionality in `dom` mode to enable querying JSON using JSONPath.
- Added unit tests to validate the new JSONPath support in `dom` mode and ensure compatibility with both standard and existing JSONPath formats.

* two minor fixes

* more tests and documentation

* damn compiler warnings

* more documentation fixes

---------

Co-authored-by: Zhengguo Yang <yangzhgg@gmail.com>
2024-10-08 10:37:59 -04:00
Daniel Lemire ec7550a70e fixes issue 2271 (#2273) 2024-10-08 10:36:12 -04:00
Daniel Lemire 3ef3078e51 documentation for issue 2259 (clarifying) (#2260) 2024-09-21 09:10:35 -04:00
tobil4sk fd06782c97 Make document::is_null behavior consistent with documentation (#2258)
* Make null-like value test consistent with docs

According to the docs: `INCORRECT_TYPE If the JSON value begins with 'n'
and is not 'null'.`

* Match document::is_null behavior to documentation

* Test other token beginning with n with is_null
2024-09-20 20:58:41 -04:00
Daniel Lemire 9303efbd0c [no-ci] Update basics.md 2024-09-20 20:56:11 -04:00
Daniel Lemire 6c979f15cc deps: switch to CPM (#2257)
* deps: switch to CPM

* missing file

* setting the dependencies on URL download

* using fixed version of cxxopts
2024-09-20 13:29:47 -04:00
Daniel Lemire 09ccabbe6c enable BMI, but only when LLVM is the compiler (#2255) 2024-09-20 10:59:54 -04:00
Daniel Lemire e00cc8c6dc Update CI (#2254)
* adding missing undef silencer

* Updating CI

* more fixes

* fix

* big endian fix
2024-09-18 09:09:11 -04:00
tobil4sk c10b32d463 Ignore pragma once when amalgamating source files (#2248)
With gcc it causes an error in `simdjson.cpp`:
```
simdjson.cpp:548:9: warning: #pragma once in main file
  548 | #pragma once
      |         ^~~~
```

It had previously been commented out in:
https://github.com/simdjson/simdjson/commit/6ef555e6fb79363fae057a9a46b52cd208d9e305

However, this was lost in an upgrade:
https://github.com/simdjson/simdjson/commit/2a4ff7346813b120f2b5b40e95d69352b593cc9c
2024-09-17 15:41:12 -04:00
Daniel Lemire 025a44348a adding missing undef silencer (#2253) 2024-09-16 21:04:09 -04:00
Daniel Lemire 70a68da941 pedantic viable size 2024-09-06 01:19:47 -04:00
Daniel Lemire e341c8b438 release candidate 2024-08-26 09:37:33 -04:00
Daniel Lemire 0ac0a80e28 Merge branch 'master' of github.com:simdjson/simdjson 2024-08-26 09:37:03 -04:00
Daniel Lemire 6b9117c029 adding warning regarding SIMDJSON_BUILD_STATIC_LIB 2024-08-26 09:36:44 -04:00
Daniel Lemire dd92151971 adding a warning 2024-08-26 09:34:39 -04:00
Tan Li Boon 0679c247f4 Add missing include directives to static build targets of simdjson. (#2240) 2024-08-26 09:24:01 -04:00
John Keiser 615218a3ad Merge pull request #2237 from simdjson/jkeiser/simdjson-vscode
Reduce errors in vscode
2024-08-18 22:04:20 -07:00
John Keiser 4c1b0a41d8 Enable SIMDJSON_SINGLEHEADER=OFF in VS Code
With singleheader on, clangd can't find the right
include files.
2024-08-18 14:21:00 -07:00
John Keiser ef563a4b09 Make simdjson compile again 2024-08-18 11:21:26 -07:00
Daniel Lemire 7a9ff93388 [no-ci] Update README.md 2024-08-15 12:52:47 -04:00
Sasha Lopoukhine fc61d7c7ba Fix ndjson spec link (#2234)
* fix ndjson spec link

The link in the readme of parse_many links to a casino spam site

* fix link
2024-08-10 10:11:05 -04:00
Daniel Lemire d506af0a79 fix: add tests related to issue 2227 (#2229)
* fix: add tests related to issue 2227

* avoiding name clash

* pedantic fix

* deprecate rvalue get on document

* selectively deprecating
2024-08-07 20:15:06 -04:00
Daniel Lemire ccf8694510 v3.10.0 2024-08-01 09:32:54 -04:00
Daniel Lemire 9b67497ed0 Allows field::unescape_key to take in a string parameter + additional dev. checks for string overflow (#2224)
* This PR does the following:

1. Upgrade cxxopts.
2. Allows field::unescape_key to take in a string parameter (syntaxic sugar).
3. Adds a dev. check to detect a string buffer overflow (indicating broken code). Note that this is unrecoverable and indicates bad code.

* tweak
2024-08-01 09:31:50 -04:00
Daniel Lemire 0336684df7 [no-ci] Update basics.md 2024-07-31 11:10:01 -04:00
Daniel Lemire c19320dd6e making it more precise 2024-07-31 10:08:04 -04:00
Daniel Lemire 412a5680e8 update 2024-07-31 10:06:47 -04:00
Daniel Lemire a05a56856d fix: use On-Demand throughout. (#2222) 2024-07-29 15:54:21 -04:00
didarpin 58173a6a1f Added the functionality to convert dom::object and dom::array to dom::element. (#2221)
Co-authored-by: didarpin <didarpin@163.com>
2024-07-25 22:26:20 -04:00
Francisco Geiman Thiesen 1721032cfd Merge pull request #2220 from simdjson/adding_macros_for_cpp20_cpp23
fix: add macros to detected C++20 and C++23
2024-07-25 17:44:36 -07:00
Daniel Lemire b73877f95e Update compiler_check.h 2024-07-23 14:25:27 -04:00
Daniel Lemire 09723897e9 fix: add macros to detected C++20 and C++23 2024-07-23 14:23:00 -04:00
Daniel Lemire 49e231b634 [no-ci] Update README.md 2024-07-16 16:05:32 -04:00
Daniel Lemire acdbbab916 adding an example of value capture with std::string_view (#2216)
* adding an example of value capture with std::string_view

* minor fix

* minor fix
2024-07-16 15:51:43 -04:00
Daniel Lemire 5090247c34 adding test (#2214) 2024-07-13 11:40:13 -04:00
Daniel Lemire 4180e05730 [no-ci] fix 'null_ptr' written as 'null_nullptrptr' in the comments 2024-07-11 08:25:27 -04:00
Daniel Lemire feea2bce2c Create config.yml 2024-07-04 16:48:09 -04:00
Daniel Lemire 692f43cd84 Update standard-issue-template.md 2024-07-04 16:46:34 -04:00
Daniel Lemire 3240d55bcc chore: add RelWithDebInfo to our CI tests (#2209)
* chore: add RelWithDebInfo to our tests

* fix

* chore: add various build types to ubuntu ci
2024-07-04 16:26:22 -04:00
Daniel Lemire 66fd28fc00 Marking a few functions as pure (no side-effect) (#2210)
* marking a few trivial functions as pure

* adding other marks

* additional marks

* vs will issue warnings, so don't use [[gnu::pure]] when __clang__ or __GNUC__ is not defined
2024-07-04 16:26:11 -04:00
Daniel Lemire 5f638951c6 Update basics.md 2024-06-27 14:49:57 -04:00
Daniel Lemire 3e94eea939 adding another example (#2206)
Co-authored-by: Daniel Lemire <dlemire@lemire.me>
2024-06-27 13:22:44 -04:00
Tunghohin 0e8311f812 reset moved object's viable_size to 0 in the move ctor of padded_string (#2204) 2024-06-25 18:06:32 -04:00
Daniel Lemire 3620e9d151 version bump 2024-06-11 15:37:22 -04:00
Daniel Lemire d017cd7ca4 Merge branch 'master' of github.com:simdjson/simdjson 2024-06-11 14:08:20 -04:00
Daniel Lemire d9d1ff5856 chore: remove unneeded gcc13 ci tests 2024-06-11 14:07:56 -04:00
Daniel Lemire eb8f2bce14 fix: add test for issue 2199 (#2200)
* fix: add test for issue 2199

* added ubuntu 24 workflow + silencing a warning
2024-06-11 13:55:55 -04:00
Dirk Stolle 2a4ff73468 update string_view lite to version 1.8.0 (#2197)
This is the header as seen for the tag v1.8.0,
commit a47222b9855dd6e6d1eac38acaa495822e2caa69, on
<https://github.com/martinmoene/string-view-lite>.
2024-06-10 10:29:25 -04:00
Daniel Lemire 77fc2b8447 doc: explaining the page trick (#2196)
* doc: explaining the page trick

* simplify

* did as john said

* trying something else

* flipping order

* trying some other order

* hmmm
2024-06-07 22:12:08 -04:00
Daniel Lemire ba8b66a633 Update basics.md 2024-06-07 12:49:32 -04:00
Daniel Lemire 66eec5feaf Update basics.md 2024-06-05 08:55:12 -04:00
Janeczko Jakub 3964f3e5d2 pull size_t from the std namespace (#2191) 2024-06-03 14:08:23 -04:00
Daniel Lemire ee8515122d version bump 2024-05-30 10:53:40 -04:00
halx99 5d35e7ca1f Fix compile error on llvm-19 (#2187) 2024-05-30 10:52:38 -04:00
spershin deefc88b9c Adding path for parsing incomplete json. (#2189)
1. Allows processing inclomplete, damaged, corrupted json to some extent.
2. Pariity with the Presto Java functionality.
3. Protected with SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON define.
4. Does not interfere with the normal path (can co-exist).
5. Tested in production forkflow.
2024-05-30 10:52:09 -04:00
Yuriy Chernyshov c80dda7c58 Fix building simdjson against libc++ with _LIBCPP_REMOVE_TRANSITIVE_INCLUDES defined (#2184)
```
src/implementation.cpp:193:20: error: no template named 'is_trivially_destructible' in namespace 'std'; did you mean 'is_trivially_move_constructible'?
static_assert(std::is_trivially_destructible<detect_best_supported_implementation_on_first_use>::value, "detect_best_supported_implementation_on_first_use should be trivially destructible");
              ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
                   is_trivially_move_constructible
```
2024-05-23 16:54:23 -04:00
Daniel Lemire d2954ef68b Update ubuntu22-gcc13.yml 2024-05-23 16:53:48 -04:00
Daniel Lemire ac719827ff fix: solve issue 2181 (#2182) 2024-05-11 20:44:38 -04:00
Daniel Lemire 6ea77392a7 Update basics.md 2024-05-10 12:21:26 -04:00
pnck e2f879751c fix: issue #2154 (#2178) 2024-05-10 00:33:09 -04:00
Daniel Lemire bf7834179c version bump 2024-05-07 19:15:43 -04:00
Daniel Lemire 69ab8848bf fix: satisfy a C++20 compiler warning (#2177) 2024-05-07 18:04:59 -04:00
Daniel Lemire 7288323e36 Update basics.md 2024-05-06 21:09:56 -04:00
Daniel Lemire 3f381cf3ee Update README.md 2024-05-06 11:11:34 -04:00
Daniel Lemire 52406402ed fix: replace vm*vq_u8 by vm*vq_u32 for better performance under some systems. 2024-05-01 14:53:48 -04:00
Daniel Lemire 799d8e3fc9 fix ref 2024-04-26 14:09:42 -04:00
Daniel Lemire 6c647d9f3b Update README.md 2024-04-24 16:23:45 -04:00
Daniel Lemire 8519e24f12 issue 2170 (#2172) 2024-04-24 01:41:40 -04:00
Daniel Lemire fffb62743c removing power fuzzer. 2024-04-23 23:50:49 -04:00
Daniel Lemire c888075f8d makes implementations trivially destructible (#2171) 2024-04-23 23:49:20 -04:00
Daniel Lemire 9b8aac5f22 [skip ci] updating documentation 2024-04-22 09:18:49 -04:00
Javier Blazquez 40b414d184 add Windows ARM64EC build and CI support (#2168)
* add Windows ARM64EC support

* add ARM64EC to vs17-arm-ci workflow
2024-04-09 14:56:58 -04:00
Daniel Lemire c85e8a7db1 version bump 2024-04-05 15:19:02 -04:00
Daniel Lemire 83f390f8e7 fix: adopt recommendation from issue 2163 (#2164) 2024-04-05 15:17:57 -04:00
Daniel Lemire 82563dcf70 correcting version bump 2024-04-04 14:08:25 -04:00
Daniel Lemire 646bd3f2c3 version bump 2024-04-04 12:24:07 -04:00
Daniel Lemire 4b0a908bb2 [skip ci] adding documentation regarding new hardware support 2024-04-04 12:22:03 -04:00
Jinyang He 4c98e51c53 Add LoongArch LSX and LASX support (#2159)
* Add LoongArch SX support

* Add LoongArch ASX support
2024-04-04 12:09:36 -04:00
Daniel Lemire 58e3d5d9cc fix: fix path to cross-compiler 2024-04-03 22:37:46 -04:00
Daniel Lemire a22a92dc72 trying to add loong to our ci tests (#2160)
* trying to add loong to our ci tests

* tuning loongarch

* updating the loong toolset
2024-04-03 15:18:45 -04:00
Daniel Lemire c4654553dd [skip ci] windows and unicode 2024-03-26 09:31:50 -04:00
Daniel Lemire 5997f82c37 Update basics.md 2024-03-20 14:26:11 -04:00
Daniel Lemire bee410b315 comment 2024-03-20 01:52:42 -04:00
Daniel Lemire b115e3ef61 some updates to our CI (#2151) 2024-03-17 11:59:50 -04:00
Daniel Lemire 669229df5d more documentation. 2024-03-17 10:29:41 -04:00
Daniel Lemire b6a2286e62 adding some documentation for escaped_key(). 2024-03-17 10:27:13 -04:00
Twice 3a97c57773 Add escaped_key method to ondemand::field (#2150) 2024-03-17 10:10:31 -04:00
297 changed files with 136380 additions and 6667 deletions
+1
View File
@@ -25,6 +25,7 @@ CompileFlags:
Diagnostics:
Suppress:
- pp_including_mainfile_in_preamble
- unused-includes
---
# Amalgamated files that require or partly define an implementation
If:
+4 -1
View File
@@ -9,7 +9,8 @@ assignees: ''
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Basics is an overview of how to use simdjson and its APIs to parse JSON: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Builder is an overview of how to use simdjson to generate JSON: https://github.com/simdjson/simdjson/blob/master/doc/builder.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017). If you wish to support features that are not part of RFC 8259, then you should not refer to your issue as a bug.
@@ -55,6 +56,8 @@ We support up-to-date 64-bit ARM and x64 FreeBSD, macOS, Windows and Linux syste
pre-release version of a compiler, do not report it as a bug to simdjson. However, we always
invite contributions either in the form an analysis or of a code contribution.
Under Windows, we support Visual Studio (both with LLVM and without). We do not support MinGW and other alternate compiler systems. Windows users should be aware that there [is a long-running bug with GCC under Windows](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412).
**Indicate whether you are willing or able to provide a bug fix as a pull request**
If you plan to contribute to simdjson, please read our guide:
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
+2 -1
View File
@@ -9,7 +9,8 @@ assignees: ''
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Basics is an overview of how to use simdjson and its APIs to parse JSON: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Builder is an overview of how to use simdjson to generate JSON: https://github.com/simdjson/simdjson/blob/master/doc/builder.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
@@ -9,7 +9,8 @@ assignees: ''
Before submitting an issue, please ensure that you have read the documentation:
* Basics is an overview of how to use simdjson and its APIs: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Basics is an overview of how to use simdjson and its APIs to parse JSON: https://github.com/simdjson/simdjson/blob/master/doc/basics.md
* Builder is an overview of how to use simdjson to generate JSON: https://github.com/simdjson/simdjson/blob/master/doc/builder.md
* Performance shows some more advanced scenarios and how to tune for them: https://github.com/simdjson/simdjson/blob/master/doc/performance.md
* Contributing: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
* We follow the [JSON specification as described by RFC 8259](https://www.rfc-editor.org/rfc/rfc8259.txt) (T. Bray, 2017).
@@ -18,7 +19,7 @@ We do not make changes to simdjson without clearly identifiable benefits, which
Is your issue:
1. A bug report? If so, please point at a reproducible test. Indicate whether you are willing or able to provide a bug fix as a pull request.
1. A bug report? If so, please point at a reproducible test. Indicate whether you are willing or able to provide a bug fix as a pull request. As a matter of policy, we do not consider a compiler warning to be a bug.
2. A build issue? If so, provide all possible details regarding your system configuration. If we cannot reproduce your issue, we cannot fix it.
+49 -4
View File
@@ -1,8 +1,53 @@
Short title (summary):
Description
- What did you change and why? (1-3 sentences)
- Issue reproduced / related issue: link the issue if relevant (e.g. #123)
Type of change
- [ ] Bug fix
- [ ] New feature
- [ ] Refactor / cleanup
- [ ] Documentation / tests
- [ ] Other (please describe):
How to verify / test
- Add additional tests to verify bugs or new features.
- If you claim performance gains, you should provide benchmark numbers using high quality benchmarking code.
Our tests check whether you have introduced trailing white space. If such a test fails, please check the "artifacts button" above, which if you click it gives a link to a downloadable file to help you identify the issue. You can also run scripts/remove_trailing_whitespace.sh locally if you have a bash shell and the sed command available on your system.
Please read before contributing:
- CONTRIBUTING: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md
- HACKING: https://github.com/simdjson/simdjson/blob/master/HACKING.md
If you plan to contribute to simdjson, please read our
CONTRIBUTING guide: https://github.com/simdjson/simdjson/blob/master/CONTRIBUTING.md and our
HACKING guide: https://github.com/simdjson/simdjson/blob/master/HACKING.md
If you can, we recommend running our tests with the sanitizers turned on.
For non-Visual Studio users, it is as easy as doing:
```bash
cmake -B build -D SIMDJSON_SANITIZE=ON -D SIMDJSON_DEVELOPER_MODE=ON
cmake --build build
ctest --test-dir build
```
Our CI checks, among other things, for trailing whitespace. If a test fails for that reason,
use the "artifacts" button to download the artifact and inspect the problematic lines,
or run `scripts/remove_trailing_whitespace.sh` locally if you have a bash shell and `sed`.
Checklist before submitting
- [ ] I added/updated tests covering my change (if applicable)
- [ ] Code builds locally and passes my check
- [ ] Documentation / README updated if needed
- [ ] Commits are atomic and messages are clear
- [ ] I linked the related issue (if applicable)
Final notes
- For large PRs, prefer smaller incremental PRs or request staged review.
Thanks for the contribution!
+29
View File
@@ -0,0 +1,29 @@
name: Ubuntu ppc64le (GCC 11)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3
name: Test
id: runcmd
with:
arch: aarch64
distro: ubuntu_latest
githubToken: ${{ github.token }}
install: |
apt-get update -q -y
apt-get install -y cmake make g++
run: |
cmake -DSIMDJSON_SANITIZE_UNDEFINED=ON -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
cmake --build build -j=2
ctest --output-on-failure --test-dir build
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
fuzz-seconds: 600
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v1
uses: actions/upload-artifact@v4
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
+2 -3
View File
@@ -1,9 +1,8 @@
name: Doxygen GitHub Pages
on:
push:
branches:
- master
release:
types: [created]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
+17
View File
@@ -0,0 +1,17 @@
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
- name: Verify
run: emcc -v
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v3.6.0
- name: Configure
run: emcmake cmake -B build
- name: Build # We build but do not test
run: cmake --build build
@@ -4,7 +4,7 @@ on: [push, pull_request]
jobs:
whitespace:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Remove whitespace and check the diff
@@ -24,7 +24,7 @@ jobs:
echo "no trailing whitespace found, good!"
fi
- name: Archive whitespace patch
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
if: always()
with:
name: whitespace-patch
+7 -7
View File
@@ -24,7 +24,7 @@ jobs:
implementations: haswell westmere fallback
UBSAN_OPTIONS: halt_on_error=1
MAXLEN: -max_len=4000
CLANGVERSION: 15
CLANGVERSION: 19
# which optimization level to use for the sanitizer build (see build_fuzzer.variants.sh)
OPTLEVEL: -O3
@@ -34,18 +34,18 @@ jobs:
sudo apt update
sudo apt-get install --quiet ninja-build valgrind zip unzip lsb-release wget software-properties-common gnupg
wget https://apt.llvm.org/llvm.sh
sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14
sudo apt-get purge --auto-remove llvm python3-lldb-15 llvm-15
chmod +x llvm.sh
sudo ./llvm.sh $CLANGVERSION
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- uses: actions/cache@v3
- uses: actions/cache@v4
id: cache-corpus
with:
path: out/
@@ -125,7 +125,7 @@ jobs:
done
- name: Save the corpus as a github artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: corpus
path: corpus.tar
@@ -148,7 +148,7 @@ jobs:
run: tar cf valgrind.tar valgrind-*.txt
- name: Save valgrind output as a github artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: always()
with:
name: valgrindresults
@@ -156,7 +156,7 @@ jobs:
if-no-files-found: ignore
- name: Archive any crashes as an artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: always()
with:
name: crashes
+50
View File
@@ -0,0 +1,50 @@
name: LoongArch64-CI
on: [push, pull_request]
jobs:
loongarch64:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- { toolchain-version: 2023.08.08 }
steps:
- uses: actions/checkout@v4
- name: Install build requirements
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends cmake
- uses: actions/cache/restore@v4
id: restore-cache
with:
path: /opt/cross-tools
key: loongarch64-${{ matrix.platform.toolchain-version }}
- name: Download LoongArch64 gcc+glibc toolchain
if: ${{ !steps.restore-cache.outputs.cache-hit }}
run: |
url="https://github.com/loongson/build-tools/releases/download/${{ matrix.platform.toolchain-version }}/x86_64-cross-tools-loongarch64-gcc-libc.tar.xz"
wget "$url" -O /tmp/toolchain.tar.xz
mkdir -p /opt
tar -C /opt -x -f /tmp/toolchain.tar.xz
- uses: actions/cache/save@v3
if: ${{ !steps.restore-cache.outputs.cache-hit }}
with:
path: /opt/cross-tools
key: loongarch64-${{ matrix.platform.toolchain-version }}
- name: setup Loongarch64 build environment
run: |
echo "/opt/cross-tools/bin" >> $GITHUB_PATH
echo "CC=loongarch64-unknown-linux-gnu-gcc" >> $GITHUB_ENV
echo "CXX=loongarch64-unknown-linux-gnu-g++" >> $GITHUB_ENV
- name: configure
run: cmake -B build -DCMAKE_SYSTEM_PROCESSOR=loongarch64 -DARCH=lonngarch64 -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_C_COMPILER=loongarch64-unknown-linux-gnu-gcc -DCMAKE_CXX_COMPILER=loongarch64-unknown-linux-gnu-g++
- name: build
run: cmake --build build
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+4 -1
View File
@@ -20,12 +20,15 @@ jobs:
- msystem: "MINGW64"
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: Debug
- msystem: "MINGW64"
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: RelWithDebInfo
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+4 -1
View File
@@ -22,12 +22,15 @@ jobs:
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: Debug
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: RelWithDebInfo
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
-71
View File
@@ -1,71 +0,0 @@
name: short fuzz on the power arch
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
armv7_job:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
# The host should always be Linux
runs-on: ubuntu-20.04
name: Build on ubuntu-20.04 ppc64le
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2.0.5
name: Run commands
id: runcmd
env:
DEBIAN_FRONTEND: noninteractive
with:
arch: ppc64le
distro: buster
# Not required, but speeds up builds by storing container images in
# a GitHub package registry.
githubToken: ${{ github.token }}
run: |
export CLANGSUFFIX="-7"
apt-get -qq update
apt-get install -q -y clang-7 libfuzzer-7-dev git wget zip ninja-build gnupg software-properties-common
wget -q -O - "https://raw.githubusercontent.com/simdjson/debian-ppa/master/key.gpg" | apt-key add -
apt-add-repository "deb https://raw.githubusercontent.com/simdjson/debian-ppa/master simdjson main"
apt-get -qq update
apt-get purge cmake cmake-data
apt-get -t simdjson -y install cmake
mkdir -p build ; cd build
cmake .. -GNinja \
-DCMAKE_CXX_COMPILER=clang++$CLANGSUFFIX \
-DCMAKE_C_COMPILER=clang$CLANGSUFFIX \
-DBUILD_SHARED_LIBS=OFF \
-DSIMDJSON_DEVELOPER_MODE=ON \
-DSIMDJSON_ENABLE_FUZZING=On \
-DSIMDJSON_COMPETITION=OFF \
-DSIMDJSON_GOOGLE_BENCHMARKS=OFF \
-DSIMDJSON_DISABLE_DEPRECATED_API=On \
-DSIMDJSON_FUZZ_LDFLAGS=-lFuzzer \
-DCMAKE_CXX_FLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=" \
-DCMAKE_C_FLAGS="-fsanitize=fuzzer-no-link" \
-DCMAKE_BUILD_TYPE=Release \
-DSIMDJSON_FUZZ_LINKMAIN=Off
cd ..
builddir=build
cmake --build $builddir
wget -O corpus.tar.gz https://readonly:readonly@www.pauldreik.se/fuzzdata/index.php?project=simdjson
tar xf corpus.tar.gz
fuzzernames=$(cmake --build $builddir --target print_all_fuzzernames |tail -n1)
for fuzzer in $fuzzernames ; do
exe=$builddir/fuzz/$fuzzer
shortname=$(echo $fuzzer |cut -f2- -d_)
echo found fuzzer $shortname with executable $exe
mkdir -p out/$shortname
others=$(find out -type d -not -name $shortname -not -name out -not -name cmin)
$exe -max_total_time=20 -max_len=4000 out/$shortname $others
echo "*************************************************************************"
done
echo "all is good, no errors found in any of these fuzzers: $fuzzernames"
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v3
name: Test
id: runcmd
with:
@@ -24,6 +24,6 @@ jobs:
apt-get update -q -y
apt-get install -y cmake make g++
run: |
cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake -DCMAKE_BUILD_TYPE=Release -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
cmake --build build -j=2
ctest --output-on-failure --test-dir build
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v3
name: Test
id: runcmd
with:
@@ -24,6 +24,6 @@ jobs:
apt-get update -q -y
apt-get install -y cmake make g++
run: |
cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake -DCMAKE_BUILD_TYPE=Release -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
cmake --build build -j=2
ctest --output-on-failure --test-dir build
+29
View File
@@ -0,0 +1,29 @@
name: Ubuntu rvv VLEN=1024 (clang 18)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
sudo apt-get update -q -y
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-18
- name: Build
run: |
CXX=clang++-18 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv_zvbb" \
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build/ -j$(nproc)
- name: Test VLEN=1024
run: |
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
export QEMU_CPU="rv64,v=on,zvbb=on,vlen=1024,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
+29
View File
@@ -0,0 +1,29 @@
name: Ubuntu rvv VLEN=128 (clang 17)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
sudo apt-get update -q -y
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-17
- name: Build
run: |
CXX=clang++-17 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" \
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build/ -j$(nproc)
- name: Test VLEN=128
run: |
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
export QEMU_CPU="rv64,v=on,vlen=128,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
+29
View File
@@ -0,0 +1,29 @@
name: Ubuntu rvv VLEN=256 (gcc 14)
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install packages
run: |
sudo apt-get update -q -y
sudo apt-get install -y cmake make g++-14-riscv64-linux-gnu qemu-user-static
- name: Build
run: |
CXX=riscv64-linux-gnu-g++-14 CXXFLAGS=-march=rv64gcv \
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build/ -j$(nproc)
- name: Test VLEN=256
run: |
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
export QEMU_CPU="rv64,v=on,zvbb=on,vlen=256,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v3
name: Test
id: runcmd
with:
@@ -24,6 +24,6 @@ jobs:
apt-get update -q -y
apt-get install -y cmake make g++
run: |
cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake -DCMAKE_BUILD_TYPE=Release -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
cmake --build build -j=2
ctest --output-on-failure --test-dir build
-38
View File
@@ -1,38 +0,0 @@
name: Ubuntu 20.04 CI (GCC 8)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
env:
CXX: g++-8
CC: gcc-8
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Install GCC 8
run: sudo apt-get install -y g++-8
- name: Use cmake
run: |
mkdir builddebug &&
cd builddebug &&
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
-47
View File
@@ -1,47 +0,0 @@
name: Ubuntu 20.04 CI (GCC 9)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake to build just the library
run: |
mkdir buildjustlib &&
cd buildjustlib &&
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DSIMDJSON_DEVELOPER_MODE=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp &&
c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson &&
cd ../tests/installation_tests/find &&
mkdir buildjustlib &&
cd buildjustlib &&
cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../buildjustlib/destination .. &&
cmake --build .
- name: Use cmake
run: |
mkdir builddebug &&
cd builddebug &&
cmake -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cd .. &&
mkdir build &&
cd build &&
cmake -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+49 -17
View File
@@ -10,24 +10,56 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
- name: Configure Debug Build
run: |
mkdir builddebug &&
cd builddebug &&
CXX=g++-12 cmake -DSIMDJSON_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cd .. &&
mkdir build &&
cd build &&
CXX=g++-12 cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j &&
cmake --install . &&
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp && c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson && ./linkandrun jsonexamples/twitter.json &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
CXX=g++-12 cmake -DSIMDJSON_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Debug -DSIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -B builddebug
- name: Compile Debug Build
run: |
cmake --build builddebug
- name: Test Debug Build
run: |
ctest --output-on-failure -LE explicitonly -j --test-dir builddebug
- name: Configure Release Build
run: |
CXX=g++-12 cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_GOOGLE_BENCHMARKS=ON -DSIMDJSON_DEVELOPER_MODE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX:PATH=destination -B build
- name: Compile Release Build
run: |
cmake --build build
- name: Test Release Build
run: |
ctest --output-on-failure -LE explicitonly -j --test-dir build
- name: Install Release Build
run: |
cmake --install build
- name: Generate Example Code
run: |
echo -e '#include <simdjson.h>\nint main(int argc,char**argv) {simdjson::dom::parser parser;simdjson::dom::element tweets = parser.load(argv[1]); }' > tmp.cpp
- name: Compile Example Code
run: |
c++ -Idestination/include -Ldestination/lib -std=c++17 -Wl,-rpath,destination/lib -o linkandrun tmp.cpp -lsimdjson
- name: Run Example Code
run: |
./linkandrun jsonexamples/twitter.json
- name: Configure Find Tests
run: |
cd tests/installation_tests/find && \
cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../destination -B build
- name: Compile Find Tests
run: |
cd tests/installation_tests/find && cmake --build build
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
-23
View File
@@ -1,23 +0,0 @@
name: Ubuntu 22.04 CI (GCC 13)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
- name: Use cmake
run: |
mkdir build &&
cd build &&
CXX=g++-13 cmake -DSIMDJSON_DEVELOPER_MODE=ON .. &&
cmake --build . &&
ctest --output-on-failure -LE explicitonly -j
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+3 -3
View File
@@ -1,4 +1,4 @@
name: Ubuntu 22.04 CI (GCC 11) with Thread Sanitizer
name: Ubuntu 22.04 CI (GCC 12) with Thread Sanitizer
on: [push, pull_request]
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
@@ -18,7 +18,7 @@ jobs:
run: |
mkdir build &&
cd build &&
cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
CXX=g++-12 cmake -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE_THREADS=ON .. &&
cmake --build . --target document_stream_tests --target ondemand_document_stream_tests --target parse_many_test &&
ctest --output-on-failure -R parse_many_test &&
ctest --output-on-failure -R document_stream_tests
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
@@ -13,10 +13,10 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+22
View File
@@ -0,0 +1,22 @@
name: Ubuntu 24.04 CI (CXX 20)
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-24.04
strategy:
matrix:
cxx: [g++-13, clang++-16]
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Prepare
run: cmake -DSIMDJSON_CXX_STANDARD=20 -DSIMDJSON_DEVELOPER_MODE=ON -B build
env:
CXX: ${{matrix.cxx}}
- name: Build
run: cmake --build build -j=2
- name: Test
run: ctest --output-on-failure --test-dir build
@@ -7,10 +7,10 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
@@ -7,10 +7,10 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
@@ -7,10 +7,10 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
@@ -25,10 +25,10 @@ jobs:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+25
View File
@@ -0,0 +1,25 @@
name: Ubuntu 24.04 CI
on: [push, pull_request]
jobs:
ubuntu-build:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
runs-on: ubuntu-24.04
strategy:
matrix:
shared: [ON, OFF]
cxx: [g++-13, clang++-16]
sanitizer: [ON, OFF]
build_type: [RelWithDebInfo, Debug, Release]
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Prepare
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_SANITIZE=${{matrix.sanitizer}} -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
env:
CXX: ${{matrix.cxx}}
- name: Build
run: cmake --build build -j=2
- name: Test
run: ctest --output-on-failure --test-dir build
+3 -3
View File
@@ -10,12 +10,12 @@ jobs:
fail-fast: false
matrix:
include:
- {arch: ARM}
- {arch: ARM64}
- {arch: ARM64EC}
steps:
- name: checkout
uses: actions/checkout@v4
- name: Use cmake
run: |
cmake -A ${{ matrix.arch }} -DCMAKE_CROSSCOMPILING=1 -DSIMDJSON_DEVELOPER_MODE=ON -D SIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_EXCEPTIONS=OFF -B build &&
cmake --build build --verbose
cmake -A ${{ matrix.arch }} -DCMAKE_SYSTEM_VERSION="10.0.22621.0" -DCMAKE_CROSSCOMPILING=1 -DSIMDJSON_DEVELOPER_MODE=ON -D SIMDJSON_GOOGLE_BENCHMARKS=OFF -DSIMDJSON_EXCEPTIONS=OFF -B build &&
cmake --build build --verbose
+30
View File
@@ -0,0 +1,30 @@
name: VS17-CI-SANITIZE
on: [push, pull_request]
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs17
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo}
steps:
- name: checkout
uses: actions/checkout@v4
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSANITIZE=ON -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
- name: Build
run: cmake --build build --config ${{matrix.build_type}} --verbose
- name: Run tests
run: |
cd build
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
+11 -15
View File
@@ -13,10 +13,12 @@ jobs:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON}
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF}
- {gen: Visual Studio 17 2022, arch: x64, shared: ON}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF}
- {gen: Visual Studio 17 2022, arch: Win32, shared: ON, build_type: Release}
- {gen: Visual Studio 17 2022, arch: Win32, shared: OFF, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, shared: ON, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Debug}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, shared: OFF, build_type: RelWithDebInfo}
steps:
- name: checkout
uses: actions/checkout@v4
@@ -24,21 +26,15 @@ jobs:
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run Release tests
run: cmake --build build --config ${{matrix.build_type}} --verbose
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Run Debug tests
run: |
cd build
ctest -C Debug -LE explicitonly --output-on-failure
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
cmake --install build --config ${{matrix.build_type}}
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release
cmake --build build_install_test --config ${{matrix.build_type}}
+37
View File
@@ -0,0 +1,37 @@
name: VS17-CLANG-CI
on: [push, pull_request]
jobs:
ci:
if: >-
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
! contains(toJSON(github.event.commits.*.message), '[skip github]')
name: windows-vs17
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: x64, build_type: Debug}
- {gen: Visual Studio 17 2022, arch: x64, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
steps:
- name: checkout
uses: actions/checkout@v4
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
- name: Build
run: cmake --build build --config ${{matrix.build_type}} --verbose
- name: Run tests
run: |
cd build
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config ${{matrix.build_type}}
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config ${{matrix.build_type}}
+9 -13
View File
@@ -13,29 +13,25 @@ jobs:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 17 2022, arch: x64}
- {gen: Visual Studio 17 2022, arch: x64, build_type: Debug}
- {gen: Visual Studio 17 2022, arch: x64, build_type: Release}
- {gen: Visual Studio 17 2022, arch: x64, build_type: RelWithDebInfo}
steps:
- name: checkout
uses: actions/checkout@v4
- name: Configure
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DSIMDJSON_DEVELOPER_MODE=ON -DSIMDJSON_COMPETITION=OFF -B build
- name: Build Debug
run: cmake --build build --config Debug --verbose
- name: Build Release
run: cmake --build build --config Release --verbose
- name: Run Release tests
- name: Build
run: cmake --build build --config ${{matrix.build_type}} --verbose
- name: Run tests
run: |
cd build
ctest -C Release -LE explicitonly --output-on-failure
- name: Run Debug tests
run: |
cd build
ctest -C Debug -LE explicitonly --output-on-failure
ctest -C ${{matrix.build_type}} -LE explicitonly --output-on-failure
- name: Install
run: |
cmake --install build --config Release
cmake --install build --config ${{matrix.build_type}}
- name: Test Installation
run: |
cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build_install_test tests/installation_tests/find
cmake --build build_install_test --config Release
cmake --build build_install_test --config ${{matrix.build_type}}
+1 -1
View File
@@ -8,7 +8,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: dependencies/.cache
key: ${{ hashFiles('dependencies/CMakeLists.txt') }}
+2 -2
View File
@@ -38,7 +38,7 @@ cmake-build-release/
.history/
# Visual Studio artifacts
/VS/
/.vs/
# C/C++ build outputs
.build/
@@ -106,4 +106,4 @@ objs
!.vscode/extensions.json
# clangd
.cache
.cache
+21 -1
View File
@@ -3,6 +3,9 @@
{"column": 95 },
{"column": 120 }
],
"cmake.configureArgs": [
"-DSIMDJSON_DEVELOPER_MODE=ON"
],
"files.trimTrailingWhitespace": true,
"files.associations": {
".clangd": "yaml",
@@ -109,6 +112,23 @@
"numbers": "cpp",
"semaphore": "cpp",
"stop_token": "cpp",
"cfenv": "cpp"
"cfenv": "cpp",
"format": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xutility": "cpp",
"coroutine": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xmemory": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp"
}
}
+69 -12
View File
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14)
project(
simdjson
# The version number is modified by tools/release.py
VERSION 3.8.0
VERSION 4.0.0
DESCRIPTION "Parsing gigabytes of JSON per second"
HOMEPAGE_URL "https://simdjson.org/"
LANGUAGES CXX C
@@ -20,10 +20,14 @@ string(
# ---- Options, variables ----
# These version numbers are modified by tools/release.py
set(SIMDJSON_LIB_VERSION "21.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "21" CACHE STRING "simdjson library soversion")
set(SIMDJSON_LIB_VERSION "27.0.0" CACHE STRING "simdjson library version")
set(SIMDJSON_LIB_SOVERSION "27" CACHE STRING "simdjson library soversion")
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson" OFF)
option(SIMDJSON_BUILD_STATIC_LIB "Build simdjson_static library along with simdjson (only makes sense if BUILD_SHARED_LIBS=ON)" OFF)
if(SIMDJSON_BUILD_STATIC_LIB AND NOT BUILD_SHARED_LIBS)
message(WARNING "SIMDJSON_BUILD_STATIC_LIB only makes sense if BUILD_SHARED_LIBS is set to ON")
message(WARNING "You might be building and installing a two identical static libraries.")
endif()
option(SIMDJSON_ENABLE_THREADS "Link with thread support" ON)
@@ -39,6 +43,20 @@ if(SIMDJSON_DISABLE_DEPRECATED_API)
)
endif()
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25.0")
option(SIMDJSON_STATIC_REFLECTION "Enables static reflection (experimental), requires C++26" OFF)
else()
set(SIMDJSON_STATIC_REFLECTION OFF CACHE BOOL "Enables static reflection (experimental)" FORCE)
message(WARNING "SIMDJSON_STATIC_REFLECTION is disabled because your CMake version is below 3.25")
endif()
if(SIMDJSON_STATIC_REFLECTION)
simdjson_add_props(
target_compile_definitions PUBLIC
SIMDJSON_STATIC_REFLECTION=1
)
endif()
option(SIMDJSON_DEVELOPMENT_CHECKS "Enable development-time aids, such as \
checks for incorrect API usage. Enabled by default in DEBUG." OFF)
if(SIMDJSON_DEVELOPMENT_CHECKS)
@@ -51,6 +69,7 @@ endif()
if(is_top_project)
option(SIMDJSON_DEVELOPER_MODE "Enable targets for developing simdjson" OFF)
option(BUILD_SHARED_LIBS "Build simdjson as a shared library" OFF)
option(SIMDJSON_SINGLEHEADER "Disable singleheader generation" ON)
endif()
include(cmake/handle-deprecations.cmake)
@@ -79,7 +98,7 @@ set_target_properties(
)
# FIXME: Use proper CMake integration for exports
if(MSVC AND BUILD_SHARED_LIBS)
if(WIN32 AND BUILD_SHARED_LIBS)
target_compile_definitions(
simdjson
PRIVATE SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY=1
@@ -93,7 +112,16 @@ simdjson_add_props(
PRIVATE "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>"
)
simdjson_add_props(target_compile_features PUBLIC cxx_std_11)
if(SIMDJSON_STATIC_REFLECTION)
# We would like to require C++26, but no compiler supports that!
# This is a hack:
simdjson_add_props(
target_compile_options PUBLIC
-freflection -fexpansion-statements -stdlib=libc++ -std=c++26
)
else()
simdjson_add_props(target_compile_features PUBLIC cxx_std_11)
endif()
# workaround for GNU GCC poor AVX load/store code generation
if(
@@ -106,6 +134,30 @@ if(
)
endif()
option(SIMDJSON_MINUS_ZERO_AS_FLOAT "Treat -0 as a floating-point value" OFF)
if(SIMDJSON_MINUS_ZERO_AS_FLOAT)
simdjson_add_props(target_compile_definitions PRIVATE SIMDJSON_MINUS_ZERO_AS_FLOAT=1)
endif(SIMDJSON_MINUS_ZERO_AS_FLOAT)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(loongarch64)$")
option(SIMDJSON_PREFER_LSX "Prefer LoongArch SX" ON)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-mlasx COMPILER_SUPPORTS_LASX)
check_cxx_compiler_flag(-mlsx COMPILER_SUPPORTS_LSX)
if(COMPILER_SUPPORTS_LASX AND NOT SIMDJSON_PREFER_LSX)
simdjson_add_props(
target_compile_options PRIVATE
-mlasx
)
elseif(COMPILER_SUPPORTS_LSX)
simdjson_add_props(
target_compile_options PRIVATE
-mlsx
)
endif()
endif()
# GCC and Clang have horrendous Debug builds when using SIMD.
# A common fix is to use '-Og' instead.
# bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412
@@ -137,11 +189,13 @@ endif()
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
install(
FILES singleheader/simdjson.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT simdjson_Development
)
if(SIMDJSON_SINGLEHEADER)
install(
FILES singleheader/simdjson.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT simdjson_Development
)
endif()
install(
TARGETS simdjson
@@ -185,6 +239,7 @@ if(SIMDJSON_BUILD_STATIC_LIB)
TARGETS simdjson_static
EXPORT simdjson_staticTargets
ARCHIVE COMPONENT simdjson_Development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(
EXPORT simdjson_staticTargets
@@ -261,6 +316,7 @@ enable_testing()
add_custom_target(all_tests)
add_subdirectory(windows)
include(cmake/CPM.cmake)
add_subdirectory(dependencies) ## This needs to be before tools because of cxxopts
add_subdirectory(tools) ## This needs to be before tests because of cxxopts
@@ -268,8 +324,9 @@ add_subdirectory(tools) ## This needs to be before tests because of cxxopts
# most of the data has been moved to https://github.com/simdjson/simdjson-data
add_subdirectory(jsonexamples)
if(SIMDJSON_SINGLEHEADER)
add_subdirectory(singleheader)
endif()
+1 -1
View File
@@ -92,7 +92,7 @@ We welcome contributions from women and less represented groups. If you need hel
Consider the following points when engaging with the project:
- We discourage arguments from authority: ideas are discusssed on their own merits and not based on who stated it.
- We discourage arguments from authority: ideas are discussed on their own merits and not based on who stated it.
- Be mindful that what you may view as an aggression is maybe merely a difference of opinion or a misunderstanding.
- Be mindful that a collection of small aggressions, even if mild in isolation, can become harmful.
+1 -1
View File
@@ -38,7 +38,7 @@ PROJECT_NAME = simdjson
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "3.8.0"
PROJECT_NUMBER = "4.0.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+49 -11
View File
@@ -20,13 +20,54 @@ If you plan to contribute to simdjson, please read our [CONTRIBUTING](https://gi
Build Quickstart
------------------------------
For non-Windows system,
```bash
mkdir build
cd build
cmake -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake --build .
cmake -B -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake --build build
ctest --test-dir build
```
It is similar for Visual Studio users, please see the CMake or Visual Studio documentation.
By default the library is built in Release mode.
Assertions and development checks
------------------------------
We do not use conventional `assert` in simdjson. Instead we use the macro
`SIMDJSON_ASSUME`:
```cpp
SIMDJSON_ASSUME(something_that_is_true());
```
Sometimes, you need to do a bit more work that a simple check.
The `SIMDJSON_DEVELOPMENT_CHECKS` macro is true only in Debug mode unless manually set.
It is acceptable to add checks that you would not do in Release mode as long as
they are guarded:
```cpp
#if SIMDJSON_DEVELOPMENT_CHECKS
// do sanity checks here
```
Working with sanitizers
------------------------------
The simdjson library must be memory-safe. We cannot allow buffer overruns.
During development, if you system supports it, we recommend configuring
the project with `-D SIMDJSON_SANITIZE=ON`.
```bash
cmake -B -D SIMDJSON_SANITIZE=ON -D SIMDJSON_DEVELOPER_MODE=ON ..
cmake --build build
ctest --test-dir build
```
Design notes
------------------------------
@@ -88,8 +129,8 @@ simdjson's source structure, from the top level, looks like this:
* simdjson/ondemand.h: the `simdjson::ondemand` namespace. Includes all public ondemand classes.
* simdjson/builtin.h: the `simdjson::builtin` namespace. Aliased to the most universal implementation available.
* simdjson/builtin/ondemand.h: the `simdjson::builtin::ondemand` namespace.
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere/ondemand.h: the `simdjson::<implementation>::ondemand` namespace. on demand compiled for the specific implementation.
* simdjson/generic/ondemand/*.h: individual on demand classes, generically written.
* simdjson/arm64|fallback|haswell|icelake|ppc64|westmere/ondemand.h: the `simdjson::<implementation>::ondemand` namespace. On-Demand compiled for the specific implementation.
* simdjson/generic/ondemand/*.h: individual On-Demand classes, generically written.
* simdjson/generic/ondemand/dependencies.h: dependencies on common, non-implementation-specific simdjson classes. This will be included before including amalgamated.h.
* simdjson/generic/ondemand/amalgamated.h: all generic ondemand classes for an implementation.
* **src:** The source files for non-inlined functionality (e.g. the architecture-specific parser
@@ -99,16 +140,13 @@ simdjson's source structure, from the top level, looks like this:
* *.cpp: other misc. implementations, such as `simdjson::implementation` and the minifier.
* arm64|fallback|haswell|icelake|ppc64|westmere.cpp: Architecture-specific parser implementations.
* generic/*.h: `simdjson::<implementation>` namespace. Generic implementation of the parser, particularly the `dom_parser_implementation`.
* generic/stage1/*.h: `simdjson::<implementation>::stage1` namespace. Generic implementation of the simd-heavy tokenizer/indexer pass of the simdjson parser. Used for the On Demand interface
* generic/stage1/*.h: `simdjson::<implementation>::stage1` namespace. Generic implementation of the simd-heavy tokenizer/indexer pass of the simdjson parser. Used for the On-Demand interface
* generic/stage2/*.h: `simdjson::<implementation>::stage2` namespace. Generic implementation of the tape creator, which consumes the index from stage 1 and actually parses numbers and string and such. Used for the DOM interface.
Other important files and directories:
* **.drone.yml:** Definitions for Drone CI.
* **.appveyor.yml:** Definitions for Appveyor CI (Windows).
* **.circleci:** Definitions for Circle CI.
* **.github/workflows:** Definitions for GitHub Actions (CI).
* **singleheader:** Contains generated `simdjson.h` and `simdjson.cpp` that we release. The files `singleheader/simdjson.h` and `singleheader/simdjson.cpp` should never be edited by hand.
* **singleheader/amalgamate.py:** Generates `singleheader/simdjson.h` and `singleheader/simdjson.cpp` for release (python script).
* **singleheader/amalgamate.py:** Generates `singleheader/simdjson.h` and `singleheader/simdjson.cpp` for release (python script). If you add a new implementation (e.g., rvv), you need to edit this file (IMPLEMENTATIONS).
* **benchmark:** This is where we do benchmarking. Benchmarking is core to every change we make; the
cardinal rule is don't regress performance without knowing exactly why, and what you're trading
for it. Many of our benchmarks are microbenchmarks. We are effectively doing controlled scientific experiments for the purpose of understanding what affects our performance. So we simplify as much as possible. We try to avoid irrelevant factors such as page faults, interrupts, unnecessary system calls. We recommend checking the performance as follows:
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2018-2023 The simdjson authors
Copyright 2018-2025 The simdjson authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+18
View File
@@ -0,0 +1,18 @@
Copyright 2018-2025 The simdjson authors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+23 -9
View File
@@ -1,7 +1,5 @@
[![][license img]][license] [![][licensemit img]][licensemit]
[![Ubuntu 20.04 CI](https://github.com/simdjson/simdjson/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)](https://simdjson.org/plots.html)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/simdjson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:simdjson)
[![][license img]][license]
[![Doxygen Documentation](https://img.shields.io/badge/docs-doxygen-green.svg)](https://simdjson.github.io/simdjson/)
@@ -31,6 +29,7 @@ Table of Contents
* [Documentation](#documentation)
* [Godbolt](#godbolt)
* [Performance results](#performance-results)
* [Packages](#packages)
* [Bindings and Ports of simdjson](#bindings-and-ports-of-simdjson)
* [About simdjson](#about-simdjson)
* [Funding](#funding)
@@ -46,6 +45,7 @@ Real-world usage
- [Meta Velox](https://velox-lib.io)
- [Google Pax](https://github.com/google/paxml)
- [milvus](https://github.com/milvus-io/milvus)
- [QuestDB](https://questdb.io/blog/questdb-release-8-0-3/)
- [Clang Build Analyzer](https://github.com/aras-p/ClangBuildAnalyzer)
- [Shopify HeapProfiler](https://github.com/Shopify/heap-profiler)
- [StarRocks](https://github.com/StarRocks/starrocks)
@@ -59,6 +59,10 @@ Real-world usage
- [vast](https://github.com/tenzir/vast)
- [ada-url](https://github.com/ada-url/ada)
- [fastgron](https://github.com/adamritter/fastgron)
- [WasmEdge](https://wasmedge.org)
- [RonDB](https://github.com/logicalclocks/rondb)
- [GreptimeDB](https://github.com/GreptimeTeam/greptimedb)
If you are planning to use simdjson in a product, please work from one of our releases.
@@ -103,6 +107,7 @@ Documentation
Usage documentation is available:
* [Basics](doc/basics.md) is an overview of how to use simdjson and its APIs.
* [Builder](doc/builder.md) is an overview of how to efficiently write JSON strings using simdjson.
* [Performance](doc/performance.md) shows some more advanced scenarios and how to tune for them.
* [Implementation Selection](doc/implementation-selection.md) describes runtime CPU detection and
how you can work with it.
@@ -141,6 +146,9 @@ speed for [synthetic files over various sizes generated with a script](https://g
For NDJSON files, we can exceed 3 GB/s with [our multithreaded parsing functions](https://github.com/simdjson/simdjson/blob/master/doc/parse_many.md).
Packages
------------------------------
[![Packaging status](https://repology.org/badge/vertical-allrepos/simdjson.svg)](https://repology.org/project/simdjson/versions)
Bindings and Ports of simdjson
@@ -165,7 +173,8 @@ We distinguish between "bindings" (which just wrap the C++ code) and a port to a
- [simdjsone](https://github.com/saleyn/simdjsone): erlang bindings.
- [lua-simdjson](https://github.com/FourierTransformer/lua-simdjson): lua bindings.
- [hermes-json](https://hackage.haskell.org/package/hermes-json): haskell bindings.
- [simdjzon](https://github.com/travisstaloch/simdjzon): zig port.
- [zimdjson](https://github.com/EzequielRamis/zimdjson): Zig port.
- [simdjzon](https://github.com/travisstaloch/simdjzon): Zig port.
- [JSON-Simd](https://github.com/rawleyfowler/JSON-simd): Raku bindings.
- [JSON::SIMD](https://metacpan.org/pod/JSON::SIMD): Perl bindings; fully-featured JSON module that uses simdjson for decoding.
- [gemmaJSON](https://github.com/sainttttt/gemmaJSON): Nim JSON parser based on simdjson bindings.
@@ -178,9 +187,9 @@ The simdjson library takes advantage of modern microarchitectures, parallelizing
instructions, reducing branch misprediction, and reducing data dependency to take advantage of each
CPU's multiple execution cores.
Our default front-end is called On Demand, and we wrote a paper about it:
Our default front-end is called On-Demand, and we wrote a paper about it:
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience (to appear)
- John Keiser, Daniel Lemire, [On-Demand JSON: A Better Way to Parse Documents?](http://arxiv.org/abs/2312.17149), Software: Practice and Experience 54 (6), 2024.
Some people [enjoy reading the first (2019) simdjson paper](https://arxiv.org/abs/1902.08318): A description of the design
and implementation of simdjson is in our research article:
@@ -199,12 +208,17 @@ For the video inclined, <br />
Funding
-------
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grant
number RGPIN-2017-03910.
The work is supported by the Natural Sciences and Engineering Research Council of Canada under grants
RGPIN-2017-03910 and RGPIN-2024-03787.
[license]: LICENSE
[license img]: https://img.shields.io/badge/License-Apache%202-blue.svg
[licensemit]: LICENSE-MIT
[licensemit img]: https://img.shields.io/badge/License-MIT-blue.svg
Contributing to simdjson
------------------------
@@ -214,7 +228,7 @@ Head over to [CONTRIBUTING.md](CONTRIBUTING.md) for information on contributing
License
-------
This code is made available under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html).
This code is made available under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html) as well as under the MIT License. As a user, you can pick the license you prefer.
Under Windows, we build some tools using the windows/dirent_portable.h file (which is outside our library code): it is under the liberal (business-friendly) MIT license.
+16
View File
@@ -4,6 +4,9 @@ add_subdirectory(dom)
include_directories( . linux )
link_libraries(simdjson-windows-headers test-data)
link_libraries(simdjson)
if(SIMDJSON_STATIC_REFLECTION)
add_compile_definitions(SIMDJSON_STATIC_REFLECTION=1)
endif(SIMDJSON_STATIC_REFLECTION)
add_executable(benchfeatures benchfeatures.cpp)
add_executable(get_corpus_benchmark get_corpus_benchmark.cpp)
@@ -32,3 +35,16 @@ if (TARGET benchmark::benchmark)
endif()
endif()
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-std=c++20" SIMDJSON_COMPILER_SUPPORTS_CXX20)
if(SIMDJSON_STATIC_REFLECTION)
add_subdirectory(static_reflect)
else()
if(SIMDJSON_EXCEPTIONS AND SIMDJSON_COMPILER_SUPPORTS_CXX20)
add_subdirectory(from)
add_subdirectory(car_builder)
endif()
endif(SIMDJSON_STATIC_REFLECTION)
+618
View File
@@ -0,0 +1,618 @@
#!/bin/bash
# JSON Parsing Compilation Benchmark: Reflection Usage vs Manual Parsing
# Compares compilation times when ACTUALLY USING reflection for parsing vs manual parsing
# This measures the compile-time cost of reflection-based automatic deserialization
set -e
echo "=== simdjson Reflection Usage Compilation Benchmark ==="
echo "Measuring compilation impact of ACTUALLY USING reflection for parsing"
echo "Starting at: $(date)"
echo
echo "╔════════════════════════════════════════════════════════════════════════════╗"
echo "║ METHODOLOGY ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ FAIR COMPARISON STRATEGY: ║"
echo "║ This benchmark compares two DIFFERENT approaches to parsing the same JSON: ║"
echo "║ ║"
echo "║ • MANUAL PARSING: Traditional simdjson with explicit .get() calls ║"
echo "║ - Uses doc[\"field\"].get(variable) for each field ║"
echo "║ - No reflection involved ║"
echo "║ ║"
echo "║ • REFLECTION PARSING: Automatic deserialization with reflection ║"
echo "║ - Uses doc.get<MyStruct>() for automatic field mapping ║"
echo "║ - Relies on compile-time reflection to generate parsing code ║"
echo "║ ║"
echo "║ WHAT WE'RE MEASURING: ║"
echo "║ • Compile-time cost of reflection-based automatic deserialization ║"
echo "║ • Template instantiation overhead for reflection parsing ║"
echo "║ • Code generation complexity from using reflection features ║"
echo "║ ║"
echo "║ TEST SCENARIOS: ║"
echo "║ ║"
echo "║ 1. SIMPLE STRUCT: Basic fields (string, int, bool) ║"
echo "║ - Measures baseline reflection overhead ║"
echo "║ ║"
echo "║ 2. NESTED STRUCT: Multiple levels of nested objects ║"
echo "║ - Measures reflection complexity scaling ║"
echo "║ ║"
echo "║ 3. COMPLEX STRUCT: Arrays, optional fields, mixed types ║"
echo "║ - Measures real-world reflection usage impact ║"
echo "║ ║"
echo "║ WHY THIS IS MEANINGFUL: ║"
echo "║ • Shows actual cost of using reflection features ║"
echo "║ • Measures compile-time code generation overhead ║"
echo "║ • Helps developers understand reflection's compilation impact ║"
echo "║ • Compares equivalent functionality implemented two different ways ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════════════════════╝"
echo
# Configuration
ITERATIONS=10
JOBS=4
# ───────────────────────────── BOX-PRINT HELPER ────────────────────────────
BOX_WIDTH=74 # characters between the pipes
print_box_line() { # usage: print_box_line "text"
printf "║ %-*s ║\n" "${BOX_WIDTH}" "$1"
}
# Function to test if a compiler supports reflection with debug output
test_reflection_support() {
local compiler="$1"
echo " → Testing compiler: $compiler"
if [ ! -x "$compiler" ] && ! command -v "$compiler" >/dev/null 2>&1; then
echo " → Compiler not found or not executable"
return 1
fi
# Check compiler version first
echo " → Compiler version: $("$compiler" --version 2>/dev/null | head -n1 || echo "version check failed")"
# Simple test: check if compiler accepts reflection flags
local test_file=$(mktemp /tmp/reflection_test_XXXXXX.cpp)
cat > "$test_file" << 'EOF'
int main() {
return 0;
}
EOF
echo " → Testing basic reflection flags..."
local test_exe=$(mktemp /tmp/reflection_test_XXXXXX)
local basic_result=$("$compiler" -freflection -fexpansion-statements -std=c++26 "$test_file" -o "$test_exe" 2>&1)
local basic_exit_code=$?
if [ $basic_exit_code -ne 0 ]; then
echo " → Basic flags FAILED with exit code $basic_exit_code"
echo " → Error output: $basic_result"
rm -f "$test_file" "$test_exe"
return 1
fi
echo " → Basic flags: OK"
rm -f "$test_exe"
# Test reflection syntax
echo " → Testing reflection syntax..."
cat > "$test_file" << 'EOF'
struct Test {
int x;
};
int main() {
auto refl = ^^Test;
return 0;
}
EOF
local syntax_result=$("$compiler" -freflection -fexpansion-statements -std=c++26 "$test_file" -o "$test_exe" 2>&1)
local syntax_exit_code=$?
if [ $syntax_exit_code -eq 0 ]; then
echo " → Reflection syntax: OK"
echo " → ✓ REFLECTION SUPPORT CONFIRMED"
rm -f "$test_file" "$test_exe"
return 0
else
echo " → Reflection syntax FAILED with exit code $syntax_exit_code"
echo " → Error output: $syntax_result"
rm -f "$test_file" "$test_exe"
return 1
fi
}
# Find a compiler with reflection support
echo "Searching for clang++ with reflection support..."
REFLECTION_CXX=""
REFLECTION_CC=""
# List of potential clang++ locations to check
POTENTIAL_COMPILERS=(
"/usr/local/bin/clang++"
"/opt/clang/bin/clang++"
"/usr/bin/clang++"
"clang++"
)
# If CXX is already set, test it first
if [ -n "$CXX" ]; then
echo "Testing user-specified compiler: $CXX"
if test_reflection_support "$CXX"; then
REFLECTION_CXX="$CXX"
echo "✓ User-specified compiler supports reflection: $CXX"
else
echo "✗ User-specified compiler does not support reflection: $CXX"
echo "Will search for alternative..."
fi
fi
# If we don't have a working compiler yet, search for one
if [ -z "$REFLECTION_CXX" ]; then
for compiler in "${POTENTIAL_COMPILERS[@]}"; do
echo "Testing: $compiler"
if test_reflection_support "$compiler"; then
REFLECTION_CXX="$compiler"
echo "✓ Found reflection-enabled compiler: $compiler"
break
else
echo "✗ No reflection support: $compiler"
fi
done
fi
# Check if we found a working compiler
if [ -z "$REFLECTION_CXX" ]; then
echo
echo "╔════════════════════════════════════════════════════════════════════════════╗"
echo "║ ERROR ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ No clang++ compiler with reflection support found! ║"
echo "║ ║"
echo "║ This benchmark requires a compiler that supports C++26 reflection. ║"
echo "║ ║"
echo "║ Options: ║"
echo "║ 1. Use the Docker container: ./p2996/run_docker.sh ║"
echo "║ 2. Build clang with reflection from: https://github.com/bloomberg/clang-p2996 ║"
echo "║ 3. Set CXX environment variable to point to reflection-enabled clang++ ║"
echo "║ ║"
echo "║ Example: CXX=/path/to/reflection-clang++ ./benchmark_script.sh ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════════════════════╝"
exit 1
fi
# Set the compilers
export CXX="$REFLECTION_CXX"
# Find corresponding C compiler
if [ -n "$CC" ]; then
REFLECTION_CC="$CC"
elif [ "$REFLECTION_CXX" = "/usr/local/bin/clang++" ]; then
REFLECTION_CC="/usr/local/bin/clang"
elif [ "$REFLECTION_CXX" = "/opt/clang/bin/clang++" ]; then
REFLECTION_CC="/opt/clang/bin/clang"
elif [ "$REFLECTION_CXX" = "/usr/bin/clang++" ]; then
REFLECTION_CC="/usr/bin/clang"
else
REFLECTION_CC="clang"
fi
export CC="$REFLECTION_CC"
echo
echo "Using reflection-enabled compiler: $($CXX --version | head -n1)"
echo "Using C compiler: $($CC --version | head -n1)"
echo
# Function to create manual parsing test
create_manual_parsing_test() {
local test_name="$1"
local struct_complexity="$2"
cat > "${test_name}_manual.cpp" << 'EOF'
#include <simdjson.h>
#include <iostream>
#include <string>
#include <vector>
#include <optional>
// Test structures
struct Person {
std::string name;
int age;
bool active;
};
struct Address {
std::string street;
std::string city;
int zipcode;
};
struct Employee {
Person person;
Address address;
std::vector<std::string> skills;
std::optional<std::string> department;
double salary;
};
// Manual parsing functions
bool parse_person_manual(simdjson::ondemand::value& val, Person& person) {
auto obj = val.get_object();
if (obj.error()) return false;
for (auto field : obj) {
std::string_view key = field.unescaped_key();
if (key == "name") {
std::string_view name_val;
if (field.value().get(name_val)) return false;
person.name = name_val;
} else if (key == "age") {
if (field.value().get(person.age)) return false;
} else if (key == "active") {
if (field.value().get(person.active)) return false;
}
}
return true;
}
bool parse_address_manual(simdjson::ondemand::value& val, Address& address) {
auto obj = val.get_object();
if (obj.error()) return false;
for (auto field : obj) {
std::string_view key = field.unescaped_key();
if (key == "street") {
std::string_view street_val;
if (field.value().get(street_val)) return false;
address.street = street_val;
} else if (key == "city") {
std::string_view city_val;
if (field.value().get(city_val)) return false;
address.city = city_val;
} else if (key == "zipcode") {
if (field.value().get(address.zipcode)) return false;
}
}
return true;
}
bool parse_employee_manual(simdjson::ondemand::document& doc, Employee& employee) {
auto obj = doc.get_object();
if (obj.error()) return false;
for (auto field : obj) {
std::string_view key = field.unescaped_key();
if (key == "person") {
auto person_val = field.value();
if (!parse_person_manual(person_val, employee.person)) return false;
} else if (key == "address") {
auto addr_val = field.value();
if (!parse_address_manual(addr_val, employee.address)) return false;
} else if (key == "skills") {
auto skills_array = field.value().get_array();
if (skills_array.error()) return false;
for (auto skill : skills_array) {
std::string_view skill_val;
if (skill.get(skill_val)) return false;
employee.skills.emplace_back(skill_val);
}
} else if (key == "department") {
std::string_view dept_val;
if (!field.value().get(dept_val)) {
employee.department = dept_val;
}
} else if (key == "salary") {
if (field.value().get(employee.salary)) return false;
}
}
return true;
}
int main() {
simdjson::ondemand::parser parser;
std::string json_str = R"({
"person": {
"name": "John Doe",
"age": 30,
"active": true
},
"address": {
"street": "123 Main St",
"city": "Anytown",
"zipcode": 12345
},
"skills": ["C++", "JSON", "Programming"],
"department": "Engineering",
"salary": 85000.50
})";
simdjson::ondemand::document doc;
auto error = parser.iterate(simdjson::pad(json_str)).get(doc);
if (error) {
std::cerr << "Parse error" << std::endl;
return 1;
}
Employee employee;
if (!parse_employee_manual(doc, employee)) {
std::cerr << "Manual parsing failed" << std::endl;
return 1;
}
std::cout << "Manual parsing successful: " << employee.person.name
<< ", age " << employee.person.age << std::endl;
return 0;
}
EOF
}
# Function to create reflection parsing test
create_reflection_parsing_test() {
local test_name="$1"
local struct_complexity="$2"
cat > "${test_name}_reflection.cpp" << 'EOF'
#include <simdjson.h>
#include <iostream>
#include <string>
#include <vector>
#include <optional>
// Test structures (same as manual version)
struct Person {
std::string name;
int age;
bool active;
};
struct Address {
std::string street;
std::string city;
int zipcode;
};
struct Employee {
Person person;
Address address;
std::vector<std::string> skills;
std::optional<std::string> department;
double salary;
};
int main() {
simdjson::ondemand::parser parser;
std::string json_str = R"({
"person": {
"name": "John Doe",
"age": 30,
"active": true
},
"address": {
"street": "123 Main St",
"city": "Anytown",
"zipcode": 12345
},
"skills": ["C++", "JSON", "Programming"],
"department": "Engineering",
"salary": 85000.50
})";
simdjson::ondemand::document doc;
auto error = parser.iterate(simdjson::pad(json_str)).get(doc);
if (error) {
std::cerr << "Parse error" << std::endl;
return 1;
}
// Use reflection-based automatic deserialization
Employee employee;
auto result = doc.get<Employee>();
if (result.error()) {
std::cerr << "Reflection parsing failed" << std::endl;
return 1;
}
employee = result.value();
std::cout << "Reflection parsing successful: " << employee.person.name
<< ", age " << employee.person.age << std::endl;
return 0;
}
EOF
}
# Function to time compilation of parsing approach
time_parsing_compilation() {
local description="$1"
local test_file="$2"
local use_reflection="$3"
local iteration="$4"
echo "[$iteration] $description"
# Clean build
rm -rf build_parsing_test
mkdir build_parsing_test
cd build_parsing_test
# Copy test file
cp "../$test_file" .
echo " Configuring..."
if [ "$use_reflection" = "true" ]; then
cmake -DCMAKE_CXX_COMPILER="$CXX" \
-DSIMDJSON_DEVELOPER_MODE=ON \
-DSIMDJSON_STATIC_REFLECTION=ON \
-DBUILD_SHARED_LIBS=OFF \
../.. >/dev/null 2>&1
else
cmake -DCMAKE_CXX_COMPILER="$CXX" \
-DSIMDJSON_DEVELOPER_MODE=ON \
-DSIMDJSON_STATIC_REFLECTION=OFF \
-DBUILD_SHARED_LIBS=OFF \
../.. >/dev/null 2>&1
fi
echo " Building simdjson..."
cmake --build . --target simdjson >/dev/null 2>&1
echo " Compiling parsing test..."
# Time just the test compilation
start_time=$(date +%s.%N)
"$CXX" -std=c++17 -I../../include "$test_file" -L. -lsimdjson -o parsing_test >/dev/null 2>&1
end_time=$(date +%s.%N)
# Calculate time duration
time_taken=$(echo "$end_time $start_time" | awk '{printf "%.3f", $1 - $2}')
echo " Completed in: ${time_taken}s"
cd ..
rm -rf build_parsing_test
echo "$time_taken"
}
# Create test files
echo "Creating test files..."
create_manual_parsing_test "complex" "complex"
create_reflection_parsing_test "complex" "complex"
# Arrays to store times
times_manual=""
times_reflection=""
echo
echo "=== MANUAL PARSING COMPILATION ==="
echo "Testing traditional simdjson parsing with explicit .get() calls"
echo
for i in $(seq 1 $ITERATIONS); do
time_result=$(time_parsing_compilation "Compiling manual parsing test" "complex_manual.cpp" "false" "$i" | tail -n1)
times_manual="$times_manual $time_result"
done
echo
echo "=== REFLECTION PARSING COMPILATION ==="
echo "Testing automatic deserialization with doc.get<Struct>()"
echo
for i in $(seq 1 $ITERATIONS); do
time_result=$(time_parsing_compilation "Compiling reflection parsing test" "complex_reflection.cpp" "true" "$i" | tail -n1)
times_reflection="$times_reflection $time_result"
done
echo
echo "╔════════════════════════════════════════════════════════════════════════════╗"
echo "║ REFLECTION USAGE COMPILATION RESULTS ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ MANUAL PARSING (explicit .get() calls): ║"
count=1
for t in $times_manual; do
if [[ "$t" =~ ^[0-9]+\.?[0-9]*$ ]]; then
line=$(printf "Run %2d: %7.3f seconds" "$count" "$t")
print_box_line "$line"
count=$((count + 1))
fi
done
echo "║ ║"
echo "║ REFLECTION PARSING (automatic doc.get<Struct>()): ║"
count=1
for t in $times_reflection; do
if [[ "$t" =~ ^[0-9]+\.?[0-9]*$ ]]; then
line=$(printf "Run %2d: %7.3f seconds" "$count" "$t")
print_box_line "$line"
count=$((count + 1))
fi
done
echo "╚════════════════════════════════════════════════════════════════════════════╝"
echo
echo "╔════════════════════════════════════════════════════════════════════════════╗"
echo "║ ANALYSIS SUMMARY ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
# Calculate averages and percentages - filter to only numeric values first
manual_numbers=""
reflection_numbers=""
for t in $times_manual; do
if [[ "$t" =~ ^[0-9]+\.?[0-9]*$ ]]; then
manual_numbers="$manual_numbers $t"
fi
done
for t in $times_reflection; do
if [[ "$t" =~ ^[0-9]+\.?[0-9]*$ ]]; then
reflection_numbers="$reflection_numbers $t"
fi
done
if [ -n "$manual_numbers" ] && [ -n "$reflection_numbers" ]; then
manual_avg=$(echo "$manual_numbers" | awk '{sum=0; for(i=1;i<=NF;i++) sum+=$i; print sum/NF}')
reflection_avg=$(echo "$reflection_numbers" | awk '{sum=0; for(i=1;i<=NF;i++) sum+=$i; print sum/NF}')
overhead=$(echo "$reflection_avg $manual_avg" | awk '{printf "%.3f", $1 - $2}')
if [ $(echo "$manual_avg > 0" | awk '{print ($1 > 0)}') -eq 1 ]; then
percent=$(echo "$reflection_avg $manual_avg" | awk '{printf "%.1f", ($1 - $2) / $2 * 100}')
else
percent="0"
fi
print_box_line "MANUAL PARSING RESULTS:"
print_box_line "$(printf "Average compilation time: %.3fs" "$manual_avg")"
print_box_line ""
print_box_line "REFLECTION PARSING RESULTS:"
print_box_line "$(printf "Average compilation time: %.3fs" "$reflection_avg")"
print_box_line ""
print_box_line "REFLECTION OVERHEAD:"
print_box_line "$(printf "Additional time: %.3fs (%+.1f%%)" "$overhead" "$percent")"
print_box_line ""
else
echo "║ ERROR: Could not extract valid timing data ║"
echo "║ Manual times: $times_manual"
echo "║ Reflection times: $times_reflection"
echo "║ ║"
fi
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ INTERPRETATION ║"
echo "╠════════════════════════════════════════════════════════════════════════════╣"
echo "║ ║"
echo "║ WHAT THESE RESULTS SHOW: ║"
echo "║ ║"
echo "║ • COMPILE-TIME COST: How much longer reflection parsing takes to compile ║"
echo "║ - Higher % = more expensive template instantiation and codegen ║"
echo "║ ║"
echo "║ • CODE GENERATION OVERHEAD: Reflection creates parsing code at compile ║"
echo "║ time, which requires more template processing than manual parsing ║"
echo "║ ║"
echo "║ • DEVELOPER TRADE-OFF: Reflection provides automatic deserialization ║"
echo "║ but at the cost of increased compilation time ║"
echo "║ ║"
echo "║ EVALUATION: ║"
echo "║ • Low overhead (0-20%): Reflection is compile-time efficient ║"
echo "║ • Medium overhead (20-50%): Noticeable but potentially acceptable ║"
echo "║ • High overhead (50%+): Significant compilation cost for reflection ║"
echo "║ ║"
echo "║ REAL-WORLD IMPACT: ║"
echo "║ • Small projects: Absolute time matters more than percentage ║"
echo "║ • Large projects: Percentage overhead compounds across many files ║"
echo "║ • CI/CD pipelines: Longer builds affect development velocity ║"
echo "║ ║"
echo "╚════════════════════════════════════════════════════════════════════════════╝"
# Clean up test files
rm -f complex_manual.cpp complex_reflection.cpp
echo
echo "Completed at: $(date)"
+14
View File
@@ -0,0 +1,14 @@
# Executable
add_executable(benchmark_car_builder benchmark_car_builder.cpp)
# Compile for C++20.
target_compile_features(benchmark_car_builder PRIVATE cxx_std_20)
# Check if -march=native is supported
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-march=native" SIMDJSON_SUPPORTS_MARCH_NATIVE)
if(SIMDJSON_SUPPORTS_MARCH_NATIVE)
target_compile_options(benchmark_car_builder PRIVATE -march=native)
endif()
target_include_directories(benchmark_car_builder PRIVATE ${CMAKE_CURRENT_LIST_DIR}/..)
@@ -0,0 +1,127 @@
#include "event_counter.h"
#include <random>
#include <vector>
#include <simdjson.h>
event_collector collector;
struct Car {
std::string make;
std::string model;
int64_t year; // We deliberately do not include the tire pressure.
};
std::vector<Car> generate_random_cars(size_t count) {
static const std::vector<std::string> makes = {"Toyota", "Honda", "Ford",
"BMW", "Mazda"};
static const std::vector<std::string> models = {"Camry", "Civic", "Focus",
"320i", "3"};
static thread_local std::mt19937 rng{std::random_device{}()};
std::uniform_int_distribution<int> make_dist(0, makes.size() - 1);
std::uniform_int_distribution<int> model_dist(0, models.size() - 1);
std::uniform_int_distribution<int64_t> year_dist(2000, 2025);
std::uniform_real_distribution<double> pressure_dist(30.0, 45.0);
std::vector<Car> cars;
cars.reserve(count);
for (size_t i = 0; i < count; ++i) {
Car car;
car.make = makes[make_dist(rng)];
car.model = models[model_dist(rng)];
car.year = year_dist(rng);
cars.push_back(std::move(car));
}
return cars;
}
std::string_view serialize(simdjson::builder::string_builder &sb,
const std::vector<Car> &cars) {
sb.clear();
sb.start_array();
for (const auto &car : cars) {
sb.start_object();
sb.append_key_value("make", car.make);
sb.append_comma();
sb.append_key_value("model", car.model);
sb.append_comma();
sb.append_key_value("year", car.year);
sb.end_object();
}
sb.end_array();
std::string_view result;
if (sb.view().get(result)) {
return ""; // unexpected (error)
}
return result;
}
double pretty_print(const std::string &name, size_t num_chars,
std::pair<event_aggregate, size_t> result) {
const auto &agg = result.first;
size_t N = result.second;
num_chars *= N;
printf("%-40s : %8.2f ns %8.2f GB/s", name.c_str(),
agg.elapsed_ns() / num_chars, num_chars / agg.elapsed_ns());
if (collector.has_events()) {
printf(" %8.2f GHz %8.2f cycles/char %8.2f ins./char %8.2f i/c",
agg.cycles() / agg.elapsed_ns(), agg.cycles() / num_chars,
agg.instructions() / num_chars, agg.instructions() / agg.cycles());
}
printf("\n");
return num_chars / agg.elapsed_ns();
}
template <class function_type>
std::pair<event_aggregate, size_t>
bench(const function_type &&function, size_t min_repeat = 100,
size_t min_time_ns = 40'000'000, size_t max_repeat = 10000000) {
size_t N = min_repeat;
if (N == 0) {
N = 1;
}
event_aggregate warm_aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
function();
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
warm_aggregate << allocate_count;
if ((i + 1 == N) && (warm_aggregate.total_elapsed_ns() < min_time_ns) &&
(N < max_repeat)) {
N *= 10;
}
}
event_aggregate aggregate{};
for (size_t i = 0; i < 10; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (size_t i = 0; i < N; i++) {
function();
}
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
aggregate << allocate_count;
}
return {aggregate, N};
}
void run_benchmarks() {
std::vector<Car> source = generate_random_cars(100000);
simdjson::builder::string_builder sb;
size_t volume = serialize(sb, source).size();
pretty_print("string_builder", volume, bench([&source, &sb]() -> size_t {
return serialize(sb, source).size();
}));
}
int main() {
for (size_t trial = 0; trial < 3; trial++) {
printf("Trial %zu:\n", trial + 1);
run_benchmarks();
printf("\n");
}
return EXIT_SUCCESS;
}
+2 -3
View File
@@ -25,12 +25,11 @@ int main(int argc, char *argv[]) {
exit(1);
}
const char *filename = argv[1];
auto v = simdjson::padded_string::load(filename);
if (v.error()) {
simdjson::padded_string p;
if (simdjson::padded_string::load(filename).get(p)) {
std::cerr << "Could not load the file " << filename << std::endl;
return EXIT_FAILURE;
}
const simdjson::padded_string& p = v.value_unsafe();
if (test_baseline) {
std::wclog << "Baseline: Getline + normal parse... " << std::endl;
std::cout << "Gigabytes/second\t"
+1
View File
@@ -122,6 +122,7 @@ struct event_aggregate {
}
double elapsed_sec() const { return total.elapsed_sec() / iterations; }
double total_elapsed_ns() const { return total.elapsed_ns(); }
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
double cycles() const { return total.cycles() / iterations; }
double instructions() const { return total.instructions() / iterations; }
+1 -1
View File
@@ -13,7 +13,7 @@ struct nlohmann_json {
auto root = nlohmann::json::parse(json.data(), json.data() + json.size());
for (auto tweet : root["statuses"]) {
if (tweet["id"] == find_id) {
result = tweet["text"];
result = to_string(tweet["text"]);
return true;
}
}
+15
View File
@@ -0,0 +1,15 @@
# Executable
add_executable(from_benchmark from_benchmark.cpp)
# Compile for C++20.
if(CMAKE_CXX_STANDARD LESS 20)
target_compile_features(from_benchmark PRIVATE cxx_std_20)
endif()
# Check if -march=native is supported
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-march=native" SIMDJSON_SUPPORTS_MARCH_NATIVE)
if(SIMDJSON_SUPPORTS_MARCH_NATIVE)
target_compile_options(from_benchmark PRIVATE -march=native)
endif()
target_include_directories(from_benchmark PRIVATE ${CMAKE_CURRENT_LIST_DIR}/..)
+77
View File
@@ -0,0 +1,77 @@
#ifndef BENCHMARK_HELPERS_H
#define BENCHMARK_HELPERS_H
#include "event_counter.h"
#include <atomic>
event_collector collector;
template <class function_type>
std::pair<event_aggregate, size_t>
bench(const function_type &&function, size_t min_repeat = 10,
size_t min_time_ns = 40'000'000, size_t max_repeat = 10000000) {
size_t N = min_repeat;
if (N == 0) {
N = 1;
}
event_aggregate warm_aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
function();
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
warm_aggregate << allocate_count;
if ((i + 1 == N) && (warm_aggregate.total_elapsed_ns() < min_time_ns) &&
(N < max_repeat)) {
N *= 10;
}
}
event_aggregate aggregate{};
for (size_t i = 0; i < 10; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (size_t i = 0; i < N; i++) {
function();
}
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
aggregate << allocate_count;
}
return {aggregate, N};
}
double pretty_print(const std::string &name, size_t num_chars,
std::pair<event_aggregate, size_t> result) {
const auto &agg = result.first;
size_t N = result.second;
num_chars *= N;
printf("%-40s : %8.2f ns %8.2f GB/s", name.c_str(),
agg.elapsed_ns() / num_chars, num_chars / agg.elapsed_ns());
if (collector.has_events()) {
printf(" %8.2f GHz %8.2f cycles/char %8.2f ins./char %8.2f i/c",
agg.cycles() / agg.elapsed_ns(), agg.cycles() / num_chars,
agg.instructions() / num_chars, agg.instructions() / agg.cycles());
}
printf("\n");
return num_chars / agg.elapsed_ns();
}
double pretty_print_array(const std::string &name, size_t num_chars, size_t num_elements,
std::pair<event_aggregate, size_t> result) {
const auto &agg = result.first;
size_t N = result.second;
num_chars *= N;
printf("%-40s : %8.2f ns/char %8.2f ns/value %8.2f GB/s", name.c_str(),
agg.elapsed_ns() / num_chars, agg.elapsed_ns() / num_elements, num_chars / agg.elapsed_ns());
if (collector.has_events()) {
printf(" %8.2f GHz %8.2f cycles/char %8.2f ins./char %8.2f ins./value %8.2f i/c",
agg.cycles() / agg.elapsed_ns(), agg.cycles() / num_chars,
agg.instructions() / num_chars, agg.instructions() / num_elements, agg.instructions() / agg.cycles());
}
printf("\n");
return num_chars / agg.elapsed_ns();
}
#endif
+146
View File
@@ -0,0 +1,146 @@
#ifndef CAR_HELPERS_H
#define CAR_HELPERS_H
#include <iomanip>
#include <random>
#include <sstream>
#include <vector>
#include <simdjson.h>
struct Car {
std::string make;
std::string model;
int64_t year; // We deliberately do not include the tire pressure.
};
using namespace simdjson;
std::string random_car_json() {
static const std::vector<std::string> makes = {"Toyota", "Honda", "Ford",
"BMW", "Mazda"};
static const std::vector<std::string> models = {"Camry", "Civic", "Focus",
"320i", "3"};
static thread_local std::mt19937 rng{std::random_device{}()};
std::uniform_int_distribution<int> make_dist(0, makes.size() - 1);
std::uniform_int_distribution<int> model_dist(0, models.size() - 1);
std::uniform_int_distribution<int> year_dist(2000, 2025);
std::uniform_real_distribution<double> pressure_dist(30.0, 45.0);
std::ostringstream oss;
oss << R"({ "make": ")" << makes[make_dist(rng)] << R"(", "model": ")"
<< models[model_dist(rng)] << R"(", "year": )" << year_dist(rng)
<< R"(, "tire_pressure": [ )" << std::fixed << std::setprecision(1)
<< pressure_dist(rng) << ", " << pressure_dist(rng) << R"( ] })";
return oss.str();
}
std::string generate_car_json_stream(size_t N) {
std::ostringstream oss;
for (size_t i = 0; i < N; ++i) {
oss << random_car_json();
oss << "\n";
}
return oss.str();
}
std::string generate_car_json_array(size_t N) {
std::ostringstream oss;
oss << "[\n";
for (size_t i = 0; i < N; ++i) {
oss << random_car_json();
if (i < N - 1) {
oss << ",";
}
oss << "\n";
}
oss << "]";
return oss.str();
}
// We want to maximize C++ portability, but this is not needed with static
// reflection (C++26).
template <>
simdjson_inline simdjson_result<Car>
simdjson::ondemand::document::get() & noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
return car;
}
template <>
simdjson_inline simdjson_result<Car> simdjson::ondemand::value::get() noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
return car;
}
template <>
simdjson_inline simdjson_result<Car>
simdjson::ondemand::document_reference::get() & noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
return car;
}
template <>
simdjson_inline simdjson_result<Car>
simdjson::ondemand::document_reference::get() && noexcept {
ondemand::object obj;
auto error = get_object().get(obj);
if (error) {
return error;
}
Car car;
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get_int64().get(car.year))) {
return error;
}
return car;
}
#endif
+132
View File
@@ -0,0 +1,132 @@
// We are going to assume C++20.
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <simdjson.h>
#include <string>
#include "benchmark_helpers.h"
#include "car_helpers.h"
using namespace simdjson;
void run_benchmarks() {
printf("Running benchmarks on tiny input...\n");
simdjson::padded_string json =
R"({ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9 ] })"_padded;
volatile size_t dummy = 0;
ondemand::parser parser;
// Classic On-Demand
pretty_print("simdjson classic", json.size(),
bench([&json, &dummy, &parser]() {
ondemand::document doc = parser.iterate(json);
Car car = doc.get<Car>();
dummy = dummy + car.year;
}));
// Benchmark simdjson::from() without parser
pretty_print("simdjson::from<Car>() (no parser)", json.size(),
bench([&json, &dummy]() {
Car car = simdjson::from(json);
dummy = dummy + car.year;
}));
// Benchmark simdjson::from() with parser
pretty_print("simdjson::from<Car>() (with parser)", json.size(),
bench([&json, &dummy, &parser]() {
Car car = simdjson::from(parser, json);
dummy = dummy + car.year;
}));
}
void run_array_benchmarks() {
printf("Running benchmarks on large array...\n");
size_t N = 1'000'000;
simdjson::padded_string json = generate_car_json_array(N);
volatile size_t dummy = 0;
ondemand::parser parser;
// Classic On-Demand
pretty_print_array("simdjson classic", json.size(), N,
bench([&json, &dummy, &parser]() {
dummy = 0;
ondemand::document doc = parser.iterate(json);
ondemand::array array = doc.get_array();
for (auto value : array) {
Car car = value.get<Car>();
dummy = dummy + car.year;
}
}));
// from with array
pretty_print_array("simdjson::from(json).array()", json.size(), N,
bench([&json, &dummy, &parser]() {
dummy = 0;
for (auto value : simdjson::from(json).array()) {
Car car = value.get<Car>();
dummy = dummy + car.year;
}
}));
#if SIMDJSON_SUPPORTS_RANGES_FROM
// Benchmark simdjson::from() without parser (EXPERIMENTAL)
pretty_print_array("simdjson::from<Car>() (experimental, no parser)",
json.size(), N, bench([&json, &dummy]() {
dummy = 0;
for (Car car :
simdjson::from(json) | simdjson::as<Car>()) {
dummy = dummy + car.year;
}
}));
// Benchmark simdjson::from() with parser (EXPERIMENTAL)
pretty_print_array("simdjson::from<Car>() (experimental, with parser)",
json.size(), N, bench([&json, &dummy, &parser]() {
dummy = 0;
for (Car car : simdjson::from(parser, json) |
simdjson::as<Car>()) {
dummy = dummy + car.year;
}
}));
#endif // SIMDJSON_SUPPORTS_RANGES_FROM
}
void run_stream_benchmarks() {
printf("Running stream benchmarks...\n");
simdjson::padded_string json = generate_car_json_stream(1'000'000);
volatile size_t dummy = 0;
ondemand::parser parser;
// Classic On-Demand
pretty_print("simdjson classic", json.size(),
bench([&json, &dummy, &parser]() {
ondemand::document_stream stream = parser.iterate_many(json);
for (auto doc : stream) {
Car car = doc.get<Car>();
dummy = dummy + car.year;
}
}));
// from_many
pretty_print("simdjson with thread local", json.size(),
bench([&json, &dummy, &parser]() {
ondemand::document_stream stream =
ondemand::parser::get_parser().iterate_many(json);
for (auto doc : stream) {
Car car = doc.get<Car>();
dummy = dummy + car.year;
}
}));
}
int main() {
for (size_t trial = 0; trial < 3; trial++) {
printf("Trial %zu:\n", trial + 1);
run_array_benchmarks();
run_stream_benchmarks();
run_benchmarks();
printf("\n");
}
return EXIT_SUCCESS;
}
+5 -1
View File
@@ -9,7 +9,11 @@ simdjson_never_inline
double bench(std::string filename, simdjson::padded_string& p) {
std::chrono::time_point<std::chrono::steady_clock> start_clock =
std::chrono::steady_clock::now();
simdjson::padded_string::load(filename).value_unsafe().swap(p);
auto error = simdjson::padded_string::load(filename).get(p);
if(error) {
std::cerr << simdjson::error_message(error) << std::endl;
std::abort();
}
std::chrono::time_point<std::chrono::steady_clock> end_clock =
std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed = end_clock - start_clock;
@@ -23,7 +23,29 @@ struct simdjson_ondemand {
};
BENCHMARK_TEMPLATE(large_random, simdjson_ondemand)->UseManualTime();
#if SIMDJSON_STATIC_REFLECTION
struct simdjson_ondemand_static_reflect {
static constexpr diff_flags DiffFlags = diff_flags::NONE;
ondemand::parser parser{};
bool run(simdjson::padded_string &json, std::vector<point> &result) {
auto doc = parser.iterate(json);
if(auto e = doc.get_array().get<std::vector<point>>(result); e) { return false; }
// We can also do it like so:
//for (ondemand::object coord : doc) {
// result.emplace_back(coord.get<point>());
//}
// It seems that doing the reflection is slower than doing the manual lookup.
// E.g., it is faster if we do result.emplace_back(coord["x"], coord["y"], coord["z"]);
return true;
}
};
BENCHMARK_TEMPLATE(large_random, simdjson_ondemand_static_reflect)->UseManualTime();
#endif
} // namespace large_random
#endif // SIMDJSON_EXCEPTIONS
+1 -1
View File
@@ -13,7 +13,7 @@ class OnDemand {
public:
OnDemand() {
if(!displayed_implementation) {
std::cout << "On Demand implementation: " << builtin_implementation()->name() << std::endl;
std::cout << "On-Demand implementation: " << builtin_implementation()->name() << std::endl;
displayed_implementation = true;
}
}
+44
View File
@@ -0,0 +1,44 @@
# Include reflect-cpp
CPMAddPackage(
NAME reflect-cpp
GITHUB_REPOSITORY getml/reflect-cpp
GIT_TAG v0.17.0
EXCLUDE_FROM_ALL YES
)
if(NOT WIN32)
# We want the check whether Rust is available before trying to build a crate.
CPMAddPackage(
NAME corrosion
GITHUB_REPOSITORY corrosion-rs/corrosion
VERSION 0.4.4
DOWNLOAD_ONLY ON
OPTIONS "Rust_FIND_QUIETLY OFF"
)
include("${corrosion_SOURCE_DIR}/cmake/FindRust.cmake")
endif()
if(RUST_FOUND)
message(STATUS "Rust found: " ${Rust_VERSION} )
add_subdirectory("${corrosion_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/_deps/corrosion" EXCLUDE_FROM_ALL)
# Important: we want to build in release mode!
corrosion_import_crate(MANIFEST_PATH "serde-benchmark/Cargo.toml" NO_LINKER_OVERRIDE PROFILE release)
else()
message(STATUS "Rust/Cargo is unavailable." )
message(STATUS "We will not benchmark serde-benchmark." )
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(STATUS "Under macOS, you may be able to install rust with")
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Under Linux, you may be able to install rust with a command such as")
message(STATUS "apt-get install cargo" )
message(STATUS "or" )
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
endif()
endif()
# Add the benchmark executable targets
add_subdirectory(twitter_benchmark)
add_subdirectory(citm_catalog_benchmark)
@@ -0,0 +1,52 @@
#ifndef BENCHMARK_HELPER_HPP
#define BENCHMARK_HELPER_HPP
#include "event_counter.h"
#include <atomic>
inline event_collector &get_collector() {
static event_collector collector;
return collector;
}
template <class function_type>
event_aggregate bench(const function_type &function, size_t min_repeat = 10,
size_t min_time_ns = 1000000000,
size_t max_repeat = 100000) {
event_collector &collector = get_collector();
event_aggregate aggregate{};
size_t N = min_repeat;
if (N == 0) {
N = 1;
}
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
function();
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
aggregate << allocate_count;
if ((i + 1 == N) && (aggregate.total_elapsed_ns() < min_time_ns) &&
(N < max_repeat)) {
N *= 10;
}
}
return aggregate;
}
// Source of the 2 functions below:
// https://github.com/simdutf/simdutf/blob/master/benchmarks/base64/benchmark_base64.cpp
inline void pretty_print(size_t strings, size_t bytes, std::string name,
event_aggregate agg) {
event_collector &collector = get_collector();
printf("%-60s : ", name.c_str());
printf(" %5.2f MB/s ", bytes * 1000 / agg.elapsed_ns());
printf(" %5.2f Ms/s ", strings * 1000 / agg.elapsed_ns());
if (collector.has_events()) {
printf(" %5.2f GHz ", agg.cycles() / agg.elapsed_ns());
printf(" %5.2f c/b ", agg.cycles() / bytes);
printf(" %5.2f i/b ", agg.instructions() / bytes);
printf(" %5.2f i/c ", agg.instructions() / agg.cycles());
}
printf("\n");
}
#endif
@@ -0,0 +1,14 @@
add_executable(benchmark_serialization_citm_catalog benchmark_serialization_citm_catalog.cpp)
# Link with Rust benchmarking code if available
if(TARGET serde-benchmark)
message(STATUS "serde-benchmark target was created. Linking CITM catalog benchmark with serde-benchmark.")
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE serde-benchmark)
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
endif()
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE simdjson::simdjson nlohmann_json)
target_link_libraries(benchmark_serialization_citm_catalog PRIVATE reflectcpp)
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
target_compile_definitions(benchmark_serialization_citm_catalog PRIVATE JSON_FILE="${BENCH_CITM_JSON}")
@@ -0,0 +1,177 @@
#include <cassert>
#include <cstdlib>
#include <ctime>
#include <format>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
#include <simdjson.h>
#include <string>
#include "citm_catalog_data.h"
#include "nlohmann_citm_catalog_data.h"
#include "../benchmark_utils/benchmark_helper.h"
#if SIMDJSON_BENCH_CPP_REFLECT
#include <rfl.hpp>
#include <rfl/json.hpp>
void bench_reflect_cpp(CitmCatalog &data) {
std::string output = rfl::json::write(data);
size_t output_volume = output.size();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_reflect_cpp",
bench([&data, &measured_volume, &output_volume]() {
std::string output = rfl::json::write(data);
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
#endif // SIMDJSON_BENCH_CPP_REFLECT
#ifdef SIMDJSON_RUST_VERSION
#include "../serde-benchmark/serde_benchmark.h"
void bench_rust(serde_benchmark::CitmCatalog *data) {
const char * output = serde_benchmark::str_from_citm(data);
size_t output_volume = strlen(output);
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_rust",
bench([&data, &measured_volume, &output_volume]() {
const char * output = serde_benchmark::str_from_citm(data);
measured_volume = strlen(output);
if (measured_volume != output_volume) {
printf("mismatch\n");
}
serde_benchmark::free_str(const_cast<char*>(output));
}));
serde_benchmark::free_str(const_cast<char*>(output));
}
#endif // SIMDJSON_RUST_VERSION
void bench_nlohmann(CitmCatalog &data) {
std::string output = nlohmann_serialize(data);
size_t output_volume = output.size();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_nlohmann",
bench([&data, &measured_volume, &output_volume]() {
std::string output = nlohmann_serialize(data);
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
void bench_simdjson_static_reflection(CitmCatalog &data) {
simdjson::builder::string_builder sb;
simdjson::builder::append(sb, data);
std::string_view p;
if(sb.view().get(p)) {
std::cerr << "Error!" << std::endl;
}
size_t output_volume = p.size();
sb.clear();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
bench([&data, &measured_volume, &output_volume, &sb]() {
sb.clear();
simdjson::builder::append(sb, data);
std::string_view p;
if(sb.view().get(p)) {
std::cerr << "Error!" << std::endl;
}
measured_volume = sb.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
std::string read_file(const std::string &file_path, size_t read_size = 65536) {
std::ifstream stream(file_path, std::ios::binary);
if(!stream) {
std::cerr << "Could not open file '" << file_path << "'" << std::endl;
exit(EXIT_FAILURE);
}
stream.exceptions(std::ios_base::badbit);
std::string out;
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
out.append(buf, 0, size_t(stream.gcount()));
}
out.append(buf, 0, size_t(stream.gcount()));
return out;
}
// Function to check if benchmark name contains filter substring
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
return filter.empty() || benchmark_name.find(filter) != std::string::npos;
}
int main(int argc, char* argv[]) {
std::string filter;
// Parse command-line arguments
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--filter") == 0) {
if (i + 1 < argc) {
filter = argv[++i];
} else {
std::cerr << "Error: -f/--filter requires an argument" << std::endl;
return EXIT_FAILURE;
}
}
}
// Testing correctness of round-trip (serialization + deserialization)
std::string json_str = read_file(JSON_FILE);
// Loading up the data into a structure.
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
if(parser.iterate(simdjson::pad(json_str)).get(doc)) {
std::cerr << "Error loading the document!" << std::endl;
return EXIT_FAILURE;
}
CitmCatalog my_struct;
if(doc.get<CitmCatalog>().get(my_struct)) {
std::cerr << "Error loading CitmCatalog!" << std::endl;
return EXIT_FAILURE;
}
// Benchmarking the serialization
if (matches_filter("nlohmann", filter)) {
bench_nlohmann(my_struct);
}
if (matches_filter("simdjson_static_reflection", filter)) {
bench_simdjson_static_reflection(my_struct);
}
#ifdef SIMDJSON_RUST_VERSION
if (matches_filter("rust", filter)) {
printf("# WARNING: The Rust benchmark may not be directly comparable since it does not use an equivalent data structure.\n");
// Create a Rust-compatible CitmCatalog structure from the JSON string
serde_benchmark::CitmCatalog* rust_data =
serde_benchmark::citm_from_str(json_str.c_str(), json_str.size());
if (rust_data == nullptr) {
printf("# Failed to initialize Rust data structure\n");
} else {
bench_rust(rust_data);
serde_benchmark::free_citm(rust_data);
}
}
#endif
#if SIMDJSON_BENCH_CPP_REFLECT
if (matches_filter("reflect_cpp", filter)) {
bench_reflect_cpp(my_struct);
}
#endif
return EXIT_SUCCESS;
}
@@ -0,0 +1,82 @@
#ifndef CITM_CATALOG_DATA_H
#define CITM_CATALOG_DATA_H
#include <string>
#include <vector>
#include <map>
struct Area {
int64_t id;
std::string name;
int64_t parent;
std::vector<int64_t> childAreas;
bool operator==(const Area &other) const = default;
};
struct AudienceSubCategory {
int64_t id;
std::string name;
int64_t parent;
bool operator==(const AudienceSubCategory &other) const = default;
};
struct Event {
int64_t id;
std::string name;
std::string description;
int64_t subTopic;
int64_t topic;
std::vector<int64_t> audience;
bool operator==(const Event &other) const = default;
};
struct Performance {
int64_t id;
std::string name;
int64_t event;
std::string start;
int64_t venueCode;
bool operator==(const Performance &other) const = default;
};
struct SeatCategory {
int64_t id;
std::string name;
std::vector<int64_t> areas;
bool operator==(const SeatCategory &other) const = default;
};
struct SubTopic {
int64_t id;
std::string name;
int64_t parent;
bool operator==(const SubTopic &other) const = default;
};
struct Topic {
int64_t id;
std::string name;
bool operator==(const Topic &other) const = default;
};
struct Venue {
int64_t id;
std::string name;
int64_t address;
bool operator==(const Venue &other) const = default;
};
struct CitmCatalog {
std::map<std::string, Area> areas;
std::map<std::string, AudienceSubCategory> audienceSubCategory;
std::map<std::string, Event> events;
std::map<std::string, Performance> performances;
std::map<std::string, SeatCategory> seatCategory;
std::map<std::string, SubTopic> subTopic;
std::map<std::string, Topic> topic;
std::map<std::string, Venue> venue;
bool operator==(const CitmCatalog &other) const = default;
};
#endif
@@ -0,0 +1,171 @@
// nlohmann_citm_catalog_data.h
#ifndef NLOHMANN_CITM_CATALOG_DATA_H
#define NLOHMANN_CITM_CATALOG_DATA_H
#include "citm_catalog_data.h"
#include <nlohmann/json.hpp>
#include <string>
using json = nlohmann::json;
// ---- Area ----
inline void to_json(json &j, const Area &a) {
j = json{
{"id", a.id},
{"name", a.name},
{"parent", a.parent},
{"childAreas", a.childAreas}
};
}
inline void from_json(const json &j, Area &a) {
j.at("id").get_to(a.id);
j.at("name").get_to(a.name);
j.at("parent").get_to(a.parent);
j.at("childAreas").get_to(a.childAreas);
}
// ---- AudienceSubCategory ----
inline void to_json(json &j, const AudienceSubCategory &asc) {
j = json{
{"id", asc.id},
{"name", asc.name},
{"parent", asc.parent}
};
}
inline void from_json(const json &j, AudienceSubCategory &asc) {
j.at("id").get_to(asc.id);
j.at("name").get_to(asc.name);
j.at("parent").get_to(asc.parent);
}
// ---- Event ----
inline void to_json(json &j, const Event &e) {
j = json{
{"id", e.id},
{"name", e.name},
{"description", e.description},
{"subTopic", e.subTopic},
{"topic", e.topic},
{"audience", e.audience}
};
}
inline void from_json(const json &j, Event &e) {
j.at("id").get_to(e.id);
j.at("name").get_to(e.name);
j.at("description").get_to(e.description);
j.at("subTopic").get_to(e.subTopic);
j.at("topic").get_to(e.topic);
j.at("audience").get_to(e.audience);
}
// ---- Performance ----
inline void to_json(json &j, const Performance &p) {
j = json{
{"id", p.id},
{"name", p.name},
{"event", p.event},
{"start", p.start},
{"venueCode", p.venueCode}
};
}
inline void from_json(const json &j, Performance &p) {
j.at("id").get_to(p.id);
j.at("name").get_to(p.name);
j.at("event").get_to(p.event);
j.at("start").get_to(p.start);
j.at("venueCode").get_to(p.venueCode);
}
// ---- SeatCategory ----
inline void to_json(json &j, const SeatCategory &sc) {
j = json{
{"id", sc.id},
{"name", sc.name},
{"areas", sc.areas}
};
}
inline void from_json(const json &j, SeatCategory &sc) {
j.at("id").get_to(sc.id);
j.at("name").get_to(sc.name);
j.at("areas").get_to(sc.areas);
}
// ---- SubTopic ----
inline void to_json(json &j, const SubTopic &st) {
j = json{
{"id", st.id},
{"name", st.name},
{"parent", st.parent}
};
}
inline void from_json(const json &j, SubTopic &st) {
j.at("id").get_to(st.id);
j.at("name").get_to(st.name);
j.at("parent").get_to(st.parent);
}
// ---- Topic ----
inline void to_json(json &j, const Topic &t) {
j = json{
{"id", t.id},
{"name", t.name}
};
}
inline void from_json(const json &j, Topic &t) {
j.at("id").get_to(t.id);
j.at("name").get_to(t.name);
}
// ---- Venue ----
inline void to_json(json &j, const Venue &v) {
j = json{
{"id", v.id},
{"name", v.name},
{"address", v.address}
};
}
inline void from_json(const json &j, Venue &v) {
j.at("id").get_to(v.id);
j.at("name").get_to(v.name);
j.at("address").get_to(v.address);
}
// ---- CitmCatalog ----
inline void to_json(json &j, const CitmCatalog &c) {
j = json{
{"areas", c.areas},
{"audienceSubCategory", c.audienceSubCategory},
{"events", c.events},
{"performances", c.performances},
{"seatCategory", c.seatCategory},
{"subTopic", c.subTopic},
{"topic", c.topic},
{"venue", c.venue}
};
}
inline void from_json(const json &j, CitmCatalog &c) {
j.at("areas").get_to(c.areas);
j.at("audienceSubCategory").get_to(c.audienceSubCategory);
j.at("events").get_to(c.events);
j.at("performances").get_to(c.performances);
j.at("seatCategory").get_to(c.seatCategory);
j.at("subTopic").get_to(c.subTopic);
j.at("topic").get_to(c.topic);
j.at("venue").get_to(c.venue);
}
// Optional convenience functions for benchmarking
inline std::string nlohmann_serialize(const CitmCatalog &catalog) {
json j = catalog;
return j.dump();
}
inline bool nlohmann_deserialize(const std::string &json_in, CitmCatalog &catalog) {
try {
catalog = json::parse(json_in);
return false; // success
} catch(...) {
return true; // failure
}
}
#endif // NLOHMANN_CITM_CATALOG_DATA_H
+103
View File
@@ -0,0 +1,103 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "libc"
version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "proc-macro2"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.209"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-benchmark"
version = "0.1.0"
dependencies = [
"libc",
"serde",
"serde_json",
]
[[package]]
name = "serde_derive"
version = "1.0.209"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
@@ -0,0 +1,17 @@
[package]
name = "serde-benchmark"
version = "0.1.0"
[lib]
path = "lib.rs"
crate-type = ["cdylib"]
[dependencies]
serde = { version = "1.0", features = ["derive"] }
libc = "0.2"
serde_json = "1.0"
[profile.release]
opt-level = 3
debug = false
lto = true
@@ -0,0 +1,18 @@
## Rust Serde FFI
This folder includes FFI bindings for rust/serde.
### Links
- https://github.com/eqrion/cbindgen/blob/master/docs.md
- https://gist.github.com/zbraniecki/b251714d77ffebbc73c03447f2b2c69f
- https://michael-f-bryan.github.io/rust-ffi-guide/setting_up.html
### Building
- Generating cbindgen output
- Install dependencies with `brew install cbindgen` or `apt-get install cbindgen` or `cargo install cbindgen` or the equivalent: we used `cargo install --version 0.23.0 cbindgen`.
- Go to the directory where this README.md file is located
- Generate with `cbindgen --config cbindgen.toml --crate serde-benchmark --output serde_benchmark.h`
- Building
- Run with `cargo build --release`
@@ -0,0 +1,12 @@
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
include_version = true
braces = "SameLine"
line_length = 100
tab_width = 2
language = "C++"
namespaces = ["serde_benchmark"]
include_guard = "serde_benchmark_ffi_h"
[parse]
parse_deps = true
include = ["serde_json", "serde"]
@@ -0,0 +1,482 @@
extern crate serde;
extern crate serde_json;
extern crate libc;
use libc::{c_char, size_t};
use serde::{Serialize, Deserialize};
use std::{collections::HashMap, ffi::CString, ptr, slice};
use serde::de::{self, Deserializer};
/******************************************************/
/******************************************************/
/**
* Warning: the C++ code may not generate the same JSON.
*/
/******************************************************/
/******************************************************/
// This has no equivalent in C++:
#[derive(Serialize, Deserialize)]
pub struct Metadata {
result_type: String,
iso_language_code: String,
}
#[derive(Serialize, Deserialize)]
pub struct User {
id: i64,
id_str: String,
name: String,
screen_name: String,
location: String,
description: String,
// C++ does not have those:
// url: Option<String>,
//protected: bool,
//listed_count: i64,
//created_at: String,
//favourites_count: i64,
//utc_offset: Option<i64>,
//time_zone: Option<String>,
//geo_enabled: bool,
verified: bool,
followers_count: i64,
friends_count: i64,
statuses_count: i64,
// C++ does not have those:
//lang: String,
//profile_background_color: String,
//profile_background_image_url: String,
//profile_background_image_url_https: String,
//profile_background_tile: bool,
//profile_image_url: String,
//profile_image_url_https: String,
//profile_banner_url: Option<String>,
//profile_link_color: String,
//profile_sidebar_border_color: String,
//profile_sidebar_fill_color: String,
//profile_text_color: String,
//profile_use_background_image: bool,
//default_profile: bool,
//default_profile_image: bool,
//following: bool,
//follow_request_sent: bool,
//notifications: bool,
}
#[derive(Serialize, Deserialize)]
pub struct Hashtag {
text: String,
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
// int64_t indices_start;
// int64_t indices_end;
}
#[derive(Serialize, Deserialize)]
pub struct Url {
url: String,
expanded_url: String,
display_url: String,
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
// int64_t indices_start;
// int64_t indices_end;
}
#[derive(Serialize, Deserialize)]
pub struct UserMention {
id: i64,
name: String,
screen_name: String,
// Not in the C++ equivalent:
//id_str: String,
//indices: Vec<i64>,
// C++ has those but D. Lemire does not know what they are, they don't appear in the JSON:
// int64_t indices_start;
// int64_t indices_end;
}
#[derive(Serialize, Deserialize)]
pub struct Entities {
hashtags: Vec<Hashtag>,
urls: Vec<Url>,
user_mentions: Vec<UserMention>,
}
#[derive(Serialize, Deserialize)]
pub struct Status {
created_at: String,
id: i64,
text: String,
user: User,
entities: Entities,
retweet_count: i64,
favorite_count: i64,
favorited: bool,
retweeted: bool,
// None of these are in the C++ equivalent:
/*
metadata: Metadata,
id_str: String,
source: String,
truncated: bool,
in_reply_to_status_id: Option<i64>,
in_reply_to_status_id_str: Option<String>,
in_reply_to_user_id: Option<i64>,
in_reply_to_user_id_str: Option<String>,
in_reply_to_screen_name: Option<String>,
geo: Option<String>,
coordinates: Option<String>,
place: Option<String>,
contributors: Option<String>,
lang: String,
*/
}
#[derive(Serialize, Deserialize)]
pub struct TwitterData {
statuses: Vec<Status>,
}
#[no_mangle]
pub unsafe extern "C" fn twitter_from_str(raw_input: *const c_char, raw_input_length: size_t) -> *mut TwitterData {
let input = std::str::from_utf8_unchecked(slice::from_raw_parts(raw_input as *const u8, raw_input_length));
match serde_json::from_str(&input) {
Ok(result) => Box::into_raw(Box::new(result)),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn str_from_twitter(raw: *mut TwitterData) -> *const c_char {
let twitter_thing = { &*raw };
let serialized = serde_json::to_string(&twitter_thing).unwrap();
return std::ffi::CString::new(serialized.as_str()).unwrap().into_raw()
}
#[no_mangle]
pub unsafe extern "C" fn free_twitter(raw: *mut TwitterData) {
if raw.is_null() {
return;
}
drop(Box::from_raw(raw))
}
#[no_mangle]
pub unsafe extern fn free_string(ptr: *const c_char) {
let _ = std::ffi::CString::from_raw(ptr as *mut _);
}
// Functions associated with the CitmCatalog benchmark
#[derive(Serialize, Deserialize)]
pub struct Area {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub parent: i64,
#[serde(rename = "childAreas")]
pub child_areas: Vec<i64>,
}
#[derive(Serialize, Deserialize)]
pub struct AudienceSubCategory {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub parent: i64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Event {
#[serde(default)]
pub description: Option<String>,
pub id: i64,
#[serde(default)]
pub logo: Option<String>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub subTopicIds: Vec<i64>,
#[serde(default)]
pub subjectCode: Option<String>,
#[serde(default)]
pub subtitle: Option<String>,
#[serde(default)]
pub topicIds: Vec<i64>,
// Add a catch-all for any other fields
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Performance {
#[serde(default)]
pub id: i64,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub event: i64,
// This is the key fix - accept any JSON value type for timestamps
// This allows both string dates and integer timestamps (line 3511)
#[serde(default)]
pub start: serde_json::Value,
#[serde(rename = "venueCode")]
pub venue_code: String,
// Add a catch-all for any other fields
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize)]
pub struct SeatCategory {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub areas: Vec<i64>,
}
#[derive(Serialize, Deserialize)]
pub struct SubTopic {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub parent: i64,
}
#[derive(Serialize, Deserialize)]
pub struct Topic {
pub id: i64,
pub name: Option<String>, // Changed to Option
}
#[derive(Serialize, Deserialize)]
pub struct Venue {
pub id: i64,
pub name: Option<String>, // Changed to Option
pub address: i64,
}
// Custom deserializers
fn deserialize_string_to_area<'de, D>(deserializer: D) -> Result<HashMap<String, Area>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), Area {
id: id_num,
name: Some(name),
parent: 0,
child_areas: Vec::new(),
});
}
Ok(result)
}
fn deserialize_string_to_audience_subcategory<'de, D>(deserializer: D) -> Result<HashMap<String, AudienceSubCategory>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), AudienceSubCategory {
id: id_num,
name: Some(name),
parent: 0,
});
}
Ok(result)
}
fn deserialize_string_to_seat_category<'de, D>(deserializer: D) -> Result<HashMap<String, SeatCategory>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), SeatCategory {
id: id_num,
name: Some(name),
areas: Vec::new(),
});
}
Ok(result)
}
fn deserialize_string_to_subtopic<'de, D>(deserializer: D) -> Result<HashMap<String, SubTopic>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), SubTopic {
id: id_num,
name: Some(name),
parent: 0,
});
}
Ok(result)
}
fn deserialize_string_to_topic<'de, D>(deserializer: D) -> Result<HashMap<String, Topic>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
let id_num = id.parse::<i64>().unwrap_or(0);
result.insert(id.clone(), Topic {
id: id_num,
name: Some(name),
});
}
Ok(result)
}
fn deserialize_string_to_venue<'de, D>(deserializer: D) -> Result<HashMap<String, Venue>, D::Error>
where D: Deserializer<'de> {
let string_map: HashMap<String, String> = HashMap::deserialize(deserializer)?;
let mut result = HashMap::new();
for (id, name) in string_map {
result.insert(id.clone(), Venue {
id: 0,
name: Some(name),
address: 0,
});
}
Ok(result)
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CitmCatalog {
#[serde(rename = "areaNames")]
pub area_names: HashMap<String, String>,
#[serde(rename = "audienceSubCategoryNames")]
pub audience_subcategory_names: HashMap<String, String>,
#[serde(default)]
#[serde(rename = "blockNames")]
pub block_names: HashMap<String, String>,
pub events: HashMap<String, Event>,
#[serde(default)]
pub performances: Vec<Performance>,
#[serde(rename = "seatCategoryNames")]
pub seat_category_names: HashMap<String, String>,
#[serde(rename = "subTopicNames")]
pub subtopic_names: HashMap<String, String>,
#[serde(default)]
#[serde(rename = "subjectNames")]
pub subject_names: HashMap<String, String>,
#[serde(rename = "topicNames")]
pub topic_names: HashMap<String, String>,
#[serde(rename = "topicSubTopics")]
pub topic_subtopics: HashMap<String, Vec<i64>>,
#[serde(rename = "venueNames")]
pub venue_names: HashMap<String, String>,
// Catch-all for other fields
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
#[no_mangle]
pub unsafe extern "C" fn citm_from_str(
raw_input: *const c_char,
raw_input_length: usize
) -> *mut CitmCatalog {
if raw_input.is_null() {
eprintln!("Error: Input pointer is null");
return ptr::null_mut();
}
// Convert the raw pointer + length into a Rust slice
let bytes = slice::from_raw_parts(raw_input as *const u8, raw_input_length);
let input_str = match std::str::from_utf8(bytes) {
Ok(s) => s,
Err(e) => {
eprintln!("Error: Invalid UTF-8 string: {}", e);
return ptr::null_mut();
}
};
// Try deserializing the input string into CitmCatalog
match serde_json::from_str::<CitmCatalog>(input_str) {
Ok(catalog) => Box::into_raw(Box::new(catalog)),
Err(e) => {
eprintln!("Error deserializing JSON: {}", e);
eprintln!("JSON snippet (first 200 chars): {:.200}...", input_str);
ptr::null_mut()
}
}
}
/// Serializes a CitmCatalog into a JSON string (UTF-8).
#[no_mangle]
pub unsafe extern "C" fn str_from_citm(raw_catalog: *mut CitmCatalog) -> *mut c_char {
if raw_catalog.is_null() {
eprintln!("Error: Catalog pointer is null");
return ptr::null_mut();
}
// Fix: Actually serialize the catalog
let catalog = &*raw_catalog;
match serde_json::to_string(catalog) {
Ok(serialized) => {
match CString::new(serialized) {
Ok(cstr) => cstr.into_raw(),
Err(e) => {
eprintln!("Error creating CString: {}", e);
ptr::null_mut()
}
}
},
Err(e) => {
eprintln!("Error serializing catalog to JSON: {}", e);
ptr::null_mut()
}
}
}
/// Frees the CitmCatalog pointer.
#[no_mangle]
pub unsafe extern "C" fn free_citm(raw_catalog: *mut CitmCatalog) {
if !raw_catalog.is_null() {
drop(Box::from_raw(raw_catalog));
}
}
#[no_mangle]
pub extern "C" fn free_str(ptr: *mut c_char) {
if !ptr.is_null() {
unsafe {
// Convert back into a CString, which automatically frees the memory
let _ = CString::from_raw(ptr);
}
}
}
@@ -0,0 +1,45 @@
#ifndef serde_benchmark_ffi_h
#define serde_benchmark_ffi_h
/* Generated with cbindgen:0.28.0 */
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
namespace serde_benchmark {
struct CitmCatalog;
struct TwitterData;
extern "C" {
TwitterData *twitter_from_str(const char *raw_input, size_t raw_input_length);
const char *str_from_twitter(TwitterData *raw);
void free_twitter(TwitterData *raw);
void free_string(const char *ptr);
/// Creates a CitmCatalog from a JSON string (UTF-8 encoded).
CitmCatalog *citm_from_str(const char *raw_input, uintptr_t raw_input_length);
/// Serializes a CitmCatalog into a JSON string (UTF-8).
char *str_from_citm(CitmCatalog *raw_catalog);
/// Frees the CitmCatalog pointer.
void free_citm(CitmCatalog *raw_catalog);
void free_str(char *ptr);
} // extern "C"
} // namespace serde_benchmark
#endif // serde_benchmark_ffi_h
@@ -0,0 +1,14 @@
# Add executable targets
add_executable(benchmark_serialization_twitter benchmark_serialization_twitter.cpp)
if(TARGET serde-benchmark)
message(STATUS "serde-benchmark target was created. Linking benchmarks and serde-benchmark.")
target_link_libraries(benchmark_serialization_twitter PRIVATE serde-benchmark)
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_RUST_VERSION="${Rust_VERSION}")
endif()
target_link_libraries(benchmark_serialization_twitter PRIVATE simdjson::simdjson nlohmann_json)
target_link_libraries(benchmark_serialization_twitter PRIVATE reflectcpp)
target_compile_definitions(benchmark_serialization_twitter PRIVATE SIMDJSON_BENCH_CPP_REFLECT=1)
target_compile_definitions(benchmark_serialization_twitter PRIVATE JSON_FILE="${EXAMPLE_JSON}")
@@ -0,0 +1,168 @@
#include <cassert>
#include <cstdlib>
#include <ctime>
#include <format>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
#include <simdjson.h>
#include <string>
#include "twitter_data.h"
#include "nlohmann_twitter_data.h"
#include "../benchmark_utils/benchmark_helper.h"
#if SIMDJSON_BENCH_CPP_REFLECT
#include <rfl.hpp>
#include <rfl/json.hpp>
void bench_reflect_cpp(TwitterData &data) {
std::string output = rfl::json::write(data);
size_t output_volume = output.size();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_reflect_cpp",
bench([&data, &measured_volume, &output_volume]() {
std::string output = rfl::json::write(data);
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
#endif // SIMDJSON_BENCH_CPP_REFLECT
#ifdef SIMDJSON_RUST_VERSION
#include "../serde-benchmark/serde_benchmark.h"
void bench_rust(serde_benchmark::TwitterData *data) {
const char * output = serde_benchmark::str_from_twitter(data);
size_t output_volume = strlen(output);
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_rust",
bench([&data, &measured_volume, &output_volume]() {
const char * output = serde_benchmark::str_from_twitter(data);
serde_benchmark::free_string(output);
}));
}
#endif
template <class T> void bench_simdjson_static_reflection(T &data) {
simdjson::builder::string_builder sb;
simdjson::builder::append(sb, data);
std::string_view p;
if(sb.view().get(p)) {
std::cerr << "Error!" << std::endl;
}
size_t output_volume = p.size();
sb.clear();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(sizeof(data), output_volume, "bench_simdjson_static_reflection",
bench([&data, &measured_volume, &output_volume, &sb]() {
sb.clear();
simdjson::builder::append(sb, data);
std::string_view p;
if(sb.view().get(p)) {
std::cerr << "Error!" << std::endl;
}
measured_volume = sb.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
void bench_nlohmann(TwitterData &data) {
std::string output = nlohmann_serialize(data);
size_t output_volume = output.size();
printf("# output volume: %zu bytes\n", output_volume);
volatile size_t measured_volume = 0;
pretty_print(1, output_volume, "bench_nlohmann",
bench([&data, &measured_volume, &output_volume]() {
std::string output = nlohmann_serialize(data);
measured_volume = output.size();
if (measured_volume != output_volume) {
printf("mismatch\n");
}
}));
}
size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
((std::string *)userp)->append((char *)contents, size * nmemb);
return size * nmemb;
}
std::string read_file(std::string filename) {
printf("# Reading file %s\n", filename.c_str());
constexpr size_t read_size = 4096;
auto stream = std::ifstream(filename.c_str());
stream.exceptions(std::ios_base::badbit);
std::string out;
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
out.append(buf, 0, size_t(stream.gcount()));
}
out.append(buf, 0, size_t(stream.gcount()));
return out;
}
// Function to check if benchmark name contains filter substring
bool matches_filter(const std::string& benchmark_name, const std::string& filter) {
return filter.empty() || benchmark_name.find(filter) != std::string::npos;
}
int main(int argc, char* argv[]) {
std::string filter;
// Parse command-line arguments
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--filter") == 0) {
if (i + 1 < argc) {
filter = argv[++i];
} else {
std::cerr << "Error: -f/--filter requires an argument" << std::endl;
return EXIT_FAILURE;
}
}
}
// Testing correctness of round-trip (serialization + deserialization)
std::string json_str = read_file(JSON_FILE);
// Loading up the data into a structure.
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
if(parser.iterate(simdjson::pad(json_str)).get(doc)) {
std::cerr << "Error loading the document!" << std::endl;
return EXIT_FAILURE;
}
TwitterData my_struct;
if(doc.get<TwitterData>().get(my_struct)) {
std::cerr << "Error loading TwitterData!" << std::endl;
return EXIT_FAILURE;
}
// Benchmarking the serialization
if (matches_filter("nlohmann", filter)) {
bench_nlohmann(my_struct);
}
if (matches_filter("simdjson_static_reflection", filter)) {
bench_simdjson_static_reflection(my_struct);
}
#ifdef SIMDJSON_RUST_VERSION
if (matches_filter("rust", filter)) {
printf("# WARNING: The Rust benchmark may not be directly comparable since it does not use an equivalent data structure.\n");
serde_benchmark::TwitterData * td = serde_benchmark::twitter_from_str(json_str.c_str(), json_str.size());
bench_rust(td);
serde_benchmark::free_twitter(td);
}
#endif
#if SIMDJSON_BENCH_CPP_REFLECT
if (matches_filter("reflect_cpp", filter)) {
bench_reflect_cpp(my_struct);
}
#endif
return EXIT_SUCCESS;
}
@@ -0,0 +1,116 @@
#ifndef NLOHMANN_TWITTER_DATA_H
#define NLOHMANN_TWITTER_DATA_H
#include "twitter_data.h"
#include <nlohmann/json.hpp>
void to_json(nlohmann::json &j, const User &u) {
j = nlohmann::json{{"id", u.id},
{"name", u.name},
{"screen_name", u.screen_name},
{"location", u.location},
{"description", u.description},
{"verified", u.verified},
{"followers_count", u.followers_count},
{"friends_count", u.friends_count},
{"statuses_count", u.statuses_count}};
}
void to_json(nlohmann::json &j, const Hashtag &h) {
j = nlohmann::json{{"text", h.text},
{"indices_start", h.indices_start},
{"indices_end", h.indices_end}};
}
void to_json(nlohmann::json &j, const Url &u) {
j = nlohmann::json{{"url", u.url},
{"expanded_url", u.expanded_url},
{"display_url", u.display_url},
{"indices_start", u.indices_start},
{"indices_end", u.indices_end}};
}
void to_json(nlohmann::json &j, const UserMention &um) {
j = nlohmann::json{{"id", um.id},
{"name", um.name},
{"screen_name", um.screen_name},
{"indices_start", um.indices_start},
{"indices_end", um.indices_end}};
}
void to_json(nlohmann::json &j, const Entities &e) {
j = nlohmann::json{{"hashtags", e.hashtags},
{"urls", e.urls},
{"user_mentions", e.user_mentions}};
}
void to_json(nlohmann::json &j, const Status &s) {
j = nlohmann::json{{"created_at", s.created_at},
{"id", s.id},
{"text", s.text},
{"user", s.user},
{"entities", s.entities},
{"retweet_count", s.retweet_count},
{"favorite_count", s.favorite_count},
{"favorited", s.favorited},
{"retweeted", s.retweeted}};
}
std::string nlohmann_serialize(const std::vector<Hashtag>& v) {
nlohmann::json a = nlohmann::json::array();
for(const Hashtag & h : v) {
a.push_back(nlohmann::json{{"text", h.text},
{"indices_start", h.indices_start},
{"indices_end", h.indices_end}});
}
return a.dump();
}
std::string nlohmann_serialize(const std::vector<Url>& v) {
nlohmann::json a = nlohmann::json::array();
for(const Url & u : v) {
a.push_back(nlohmann::json{{"url", u.url},
{"expanded_url", u.expanded_url},
{"display_url", u.display_url},
{"indices_start", u.indices_start},
{"indices_end", u.indices_end}});
}
return a.dump();
}
std::string nlohmann_serialize(const std::vector<UserMention>& v) {
nlohmann::json a = nlohmann::json::array();
for(const UserMention & um : v) {
a.push_back(nlohmann::json{{"id", um.id},
{"name", um.name},
{"screen_name", um.screen_name},
{"indices_start", um.indices_start},
{"indices_end", um.indices_end}});
}
return a.dump();
}
std::string nlohmann_serialize(const std::vector<Status>& v) {
nlohmann::json a = nlohmann::json::array();
for(const Status & s : v) {
a.push_back(nlohmann::json{{"created_at", s.created_at},
{"id", s.id},
{"text", s.text},
{"user", s.user},
{"entities", s.entities},
{"retweet_count", s.retweet_count},
{"favorite_count", s.favorite_count},
{"favorited", s.favorited},
{"retweeted", s.retweeted}});
}
return a.dump();
}
void to_json(nlohmann::json &j, const TwitterData &t) {
j = nlohmann::json{{"statuses", t.statuses}};
}
std::string nlohmann_serialize(const TwitterData &data) {
return nlohmann_serialize(data.statuses);
}
#endif // NLOHMANN_TWITTER_DATA_H
@@ -0,0 +1,71 @@
#ifndef TWITTER_DATA_H
#define TWITTER_DATA_H
#include <string>
#include <vector>
struct User {
int64_t id;
std::string id_str;
std::string name;
std::string screen_name;
std::string location;
std::string description;
bool verified;
int64_t followers_count;
int64_t friends_count;
int64_t statuses_count;
bool operator<=>(const User &other) const = default;
};
struct Hashtag {
std::string text;
int64_t indices_start;
int64_t indices_end;
bool operator<=>(const Hashtag &other) const = default;
};
struct Url {
std::string url;
std::string expanded_url;
std::string display_url;
int64_t indices_start;
int64_t indices_end;
bool operator<=>(const Url &other) const = default;
};
struct UserMention {
int64_t id;
std::string name;
std::string screen_name;
int64_t indices_start;
int64_t indices_end;
bool operator<=>(const UserMention &other) const = default;
};
struct Entities {
std::vector<Hashtag> hashtags;
std::vector<Url> urls;
std::vector<UserMention> user_mentions;
bool operator==(const Entities &other) const = default;
};
struct Status {
std::string created_at;
int64_t id;
std::string text;
User user;
Entities entities;
int64_t retweet_count;
int64_t favorite_count;
bool favorited;
bool retweeted;
bool operator==(const Status &other) const = default;
};
struct TwitterData {
std::vector<Status> statuses;
bool operator==(const TwitterData &other) const = default;
};
#endif
+2 -2
View File
@@ -26,8 +26,8 @@ struct nlohmann_json {
}
}
result.text = top_tweet["text"];
result.screen_name = top_tweet["user"]["screen_name"];
result.text = to_string(top_tweet["text"]);
result.screen_name = to_string(top_tweet["user"]["screen_name"]);
return result.retweet_count != -1;
}
};
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: MIT
#
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
set(CPM_DOWNLOAD_VERSION 0.40.2)
set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d")
if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
else()
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
endif()
# Expand relative path. This is important if the provided path contains a tilde (~)
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
file(DOWNLOAD
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
)
include(${CPM_DOWNLOAD_LOCATION})
+15 -7
View File
@@ -2,7 +2,10 @@
# Flags used by exes and by the simdjson library (project-wide flags)
#
add_library(simdjson-internal-flags INTERFACE)
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
# We default to ON for all targets, so that we can use the library in shared libraries.
set_target_properties(simdjson-internal-flags PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
option(SIMDJSON_CHECK_EOF "Check for the end of the input buffer. The setting is unnecessary since we require padding of the inputs. You should expect tests to fail with this option turned on." OFF)
if(SIMDJSON_CHECK_EOF)
@@ -29,6 +32,9 @@ undefined behavior.")
link_libraries(
-fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all
)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(-fsanitize=address)
link_libraries(-fsanitize=address)
else()
message(
STATUS
@@ -52,7 +58,6 @@ endif()
option(SIMDJSON_SANITIZE_MEMORY "Sanitize memory" OFF)
if(SIMDJSON_SANITIZE_MEMORY)
message(STATUS "Setting the memory sanitizer.")
add_compile_options(
@@ -110,8 +115,14 @@ endif()
# We compile tools, tests, etc. with C++ 17. Override yourself if you need on a
# target.
set(SIMDJSON_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for simdjson")
set(CMAKE_CXX_STANDARD ${SIMDJSON_CXX_STANDARD})
if(SIMDJSON_STATIC_REFLECTION)
# This is temporary.
set(SIMDJSON_CXX_STANDARD 26 CACHE STRING "the C++ standard to use for simdjson")
#set(CMAKE_CXX_STANDARD ${SIMDJSON_CXX_STANDARD})
else()
set(SIMDJSON_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for simdjson")
set(CMAKE_CXX_STANDARD ${SIMDJSON_CXX_STANDARD})
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
@@ -158,9 +169,6 @@ We recommend Visual Studio 2019 or better on a 64-bit system.")
add_compile_options(/Zi)
endif()
else()
if(NOT WIN32)
target_compile_options(simdjson-internal-flags INTERFACE -fPIC)
endif()
target_compile_options(
simdjson-internal-flags INTERFACE
-Werror -Wall -Wextra -Weffc++ -Wsign-compare -Wshadow -Wwrite-strings
@@ -0,0 +1,4 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR riscv64)
set(CMAKE_CROSSCOMPILING_EMULATOR "qemu-riscv64-static")
+73 -42
View File
@@ -1,5 +1,4 @@
include(CMakeDependentOption)
include(import.cmake)
option(SIMDJSON_ALLOW_DOWNLOADS
"Allow dependencies to be downloaded during configure time"
@@ -8,20 +7,24 @@ option(SIMDJSON_ALLOW_DOWNLOADS
cmake_dependent_option(SIMDJSON_COMPETITION "Compile competitive benchmarks" ON
SIMDJSON_ALLOW_DOWNLOADS OFF)
cmake_dependent_option(SIMDJSON_GOOGLE_BENCHMARKS "compile the Google Benchmark benchmarks" ON
SIMDJSON_ALLOW_DOWNLOADS OFF)
"SIMDJSON_ALLOW_DOWNLOADS OR MINGW" OFF)
if(SIMDJSON_GOOGLE_BENCHMARKS)
set_off(BENCHMARK_ENABLE_TESTING)
set_off(BENCHMARK_ENABLE_INSTALL)
set_off(BENCHMARK_ENABLE_WERROR)
import_dependency(google_benchmarks google/benchmark v1.7.1)
add_dependency(google_benchmarks)
CPMAddPackage(
NAME google_benchmarks
URL https://github.com/google/benchmark/archive/refs/tags/v1.9.4.zip
OPTIONS
"BENCHMARK_ENABLE_TESTING OFF"
"BENCHMARK_ENABLE_INSTALL OFF"
"BENCHMARK_ENABLE_WERROR OFF"
)
endif()
# The bulk of our benchmarking and testing data has been moved simdjson/simdjson-data
import_dependency(simdjson-data simdjson/simdjson-data a5b13babe65c1bba7186b41b43d4cbdc20a5c470)
add_dependency(simdjson-data)
CPMAddPackage(
NAME simdjson-data
URL https://github.com/simdjson/simdjson-data/archive/351949906abde446f0314bf79606fb5d884f5be7.zip
)
option(SIMDJSON_USE_BOOST_JSON "Try to include BOOST_JSON, this may break your binaries under some systems." OFF)
# This prevents variables declared with set() from unnecessarily escaping and
# should not be called more than once
@@ -38,20 +41,30 @@ function(competition_scope_)
int main() {}
]] SIMDJSON_FOUND_STRING_VIEW)
if(SIMDJSON_FOUND_STRING_VIEW AND SIMDJSON_USE_BOOST_JSON)
import_dependency(boostjson boostorg/json ee8d72d)
CPMAddPackage(
NAME boostjson
URL https://github.com/boostorg/json/archive/ee8d72d8502b409b5561200299cad30ccdb91415.zip
)
add_library(boostjson STATIC "${boostjson_SOURCE_DIR}/src/src.cpp")
target_compile_definitions(boostjson PUBLIC BOOST_JSON_STANDALONE)
target_include_directories(boostjson SYSTEM PUBLIC
"${boostjson_SOURCE_DIR}/include")
target_compile_definitions(boostjson INTERFACE SIMDJSON_COMPETITION_BOOSTJSON)
endif()
import_dependency(cjson DaveGamble/cJSON c69134d)
CPMAddPackage(
NAME cjson
URL https://github.com/DaveGamble/cJSON/archive/c69134d01746dcf551dd7724b4edb12f922eb0d1.zip
DOWNLOAD_ONLY YES
)
add_library(cjson STATIC "${cjson_SOURCE_DIR}/cJSON.c")
target_include_directories(cjson SYSTEM PUBLIC "${cjson_SOURCE_DIR}")
target_compile_definitions(cjson INTERFACE SIMDJSON_COMPETITION_CJSON)
import_dependency(fastjson mikeando/fastjson 485f994)
CPMAddPackage(
NAME fastjson
URL https://github.com/mikeando/fastjson/archive/485f994a61a64ac73fa6a40d4d639b99b463563b.zip
DOWNLOAD_ONLY YES
)
add_library(fastjson STATIC
"${fastjson_SOURCE_DIR}/src/fastjson.cpp"
"${fastjson_SOURCE_DIR}/src/fastjson2.cpp"
@@ -60,38 +73,42 @@ int main() {}
"${fastjson_SOURCE_DIR}/include")
target_compile_definitions(fastjson INTERFACE SIMDJSON_COMPETITION_FASTJSON)
import_dependency(gason vivkin/gason 7aee524)
CPMAddPackage(
NAME gason
URL https://github.com/vivkin/gason/archive/7aee524189da1c1ecd19f67981e3d903dae25470.zip
DOWNLOAD_ONLY YES
)
add_library(gason STATIC "${gason_SOURCE_DIR}/src/gason.cpp")
target_include_directories(gason SYSTEM PUBLIC "${gason_SOURCE_DIR}/src")
target_compile_definitions(gason INTERFACE SIMDJSON_COMPETITION_GASON)
import_dependency(jsmn zserge/jsmn 18e9fe4)
CPMAddPackage(
NAME jsmn
URL https://github.com/zserge/jsmn/archive/18e9fe42cbfe21d65076f5c77ae2be379ad1270f.zip
DOWNLOAD_ONLY YES
)
add_library(jsmn STATIC "${jsmn_SOURCE_DIR}/jsmn.c")
target_include_directories(jsmn SYSTEM PUBLIC "${jsmn_SOURCE_DIR}")
target_compile_definitions(jsmn INTERFACE SIMDJSON_COMPETITION_JSMN)
message(STATUS "Importing json (nlohmann/json@v3.10.5)")
set(nlohmann_json_SOURCE_DIR "${dep_root}/json")
if(NOT EXISTS "${nlohmann_json_SOURCE_DIR}")
file(DOWNLOAD
"https://github.com/nlohmann/json/releases/download/v3.10.5/json.hpp"
"${nlohmann_json_SOURCE_DIR}/nlohmann/json.hpp")
endif()
add_library(nlohmann_json INTERFACE)
target_include_directories(nlohmann_json SYSTEM INTERFACE "${nlohmann_json_SOURCE_DIR}")
target_compile_definitions(nlohmann_json INTERFACE SIMDJSON_COMPETITION_NLOHMANN_JSON)
CPMAddPackage(
NAME nlohmann_json
URL https://github.com/nlohmann/json/archive/refs/tags/v3.12.0.zip
)
set_property(TARGET nlohmann_json APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS SIMDJSON_COMPETITION_NLOHMANN_JSON)
import_dependency(json11 dropbox/json11 ec4e452)
add_library(json11 STATIC "${json11_SOURCE_DIR}/json11.cpp")
target_include_directories(json11 SYSTEM PUBLIC "${json11_SOURCE_DIR}")
target_compile_definitions(json11 INTERFACE SIMDJSON_COMPETITION_JSON11)
set(jsoncpp_SOURCE_DIR "${simdjson_SOURCE_DIR}/dependencies/jsoncppdist")
add_library(jsoncpp STATIC "${jsoncpp_SOURCE_DIR}/jsoncpp.cpp")
target_include_directories(jsoncpp SYSTEM PUBLIC "${jsoncpp_SOURCE_DIR}")
target_compile_definitions(jsoncpp INTERFACE SIMDJSON_COMPETITION_JSONCPP)
import_dependency(rapidjson Tencent/rapidjson f54b0e4)
CPMAddPackage(
NAME rapidjson
URL https://github.com/Tencent/rapidjson/archive/805d7ed5dfe97a39b8b0816fd5eeed8731dc4936.zip
DOWNLOAD_ONLY YES
)
add_library(rapidjson INTERFACE)
target_compile_definitions(rapidjson INTERFACE RAPIDJSON_HAS_STDSTRING)
include (TestBigEndian)
@@ -110,14 +127,22 @@ int main() {}
target_compile_definitions(rapidjson INTERFACE SIMDJSON_COMPETITION_RAPIDJSON)
endif()
import_dependency(sajson chadaustin/sajson 2dcfd35)
CPMAddPackage(
NAME sajson
URL https://github.com/chadaustin/sajson/archive/2dcfd350586375f9910f74821d4f07d67ae455ba.zip
DOWNLOAD_ONLY YES
)
add_library(sajson INTERFACE)
target_compile_definitions(sajson INTERFACE SAJSON_UNSORTED_OBJECT_KEYS)
target_include_directories(sajson SYSTEM INTERFACE
"${sajson_SOURCE_DIR}/include")
target_compile_definitions(sajson INTERFACE SIMDJSON_COMPETITION_SAJSON)
import_dependency(ujson4c esnme/ujson4c e14f3fd)
CPMAddPackage(
NAME ujson4c
URL https://github.com/esnme/ujson4c/archive/e14f3fd5207fe30d1bdea723f260609e69d1abfa.zip
DOWNLOAD_ONLY YES
)
add_library(ujson4c STATIC
"${ujson4c_SOURCE_DIR}/src/ujdecode.c"
"${ujson4c_SOURCE_DIR}/3rdparty/ultrajsondec.c")
@@ -126,7 +151,11 @@ int main() {}
"${ujson4c_SOURCE_DIR}/3rdparty")
target_compile_definitions(ujson4c INTERFACE SIMDJSON_COMPETITION_UJSON4C)
import_dependency(yyjson ibireme/yyjson c385651)
CPMAddPackage(
NAME yyjson
URL https://github.com/ibireme/yyjson/archive/c3856514de0a67d7b66939bf3ed491a2d6e61277.zip
DOWNLOAD_ONLY YES
)
add_library(yyjson STATIC "${yyjson_SOURCE_DIR}/src/yyjson.c")
target_include_directories(yyjson SYSTEM PUBLIC "${yyjson_SOURCE_DIR}/src")
target_compile_definitions(yyjson INTERFACE SIMDJSON_COMPETITION_YYJSON)
@@ -140,7 +169,7 @@ int main() {}
endif()
add_library(competition-all INTERFACE)
target_link_libraries(competition-all INTERFACE competition-core jsoncpp json11 fastjson gason ujson4c)
target_link_libraries(competition-all INTERFACE competition-core jsoncpp fastjson gason ujson4c)
endfunction()
if(SIMDJSON_COMPETITION)
@@ -151,10 +180,12 @@ cmake_dependent_option(SIMDJSON_CXXOPTS "Download cxxopts (necessary for tools)"
SIMDJSON_ALLOW_DOWNLOADS OFF)
if(SIMDJSON_CXXOPTS)
set_off(CXXOPTS_BUILD_EXAMPLES)
set_off(CXXOPTS_BUILD_TESTS)
set_off(CXXOPTS_ENABLE_INSTALL)
import_dependency(cxxopts jarro2783/cxxopts 794c975)
add_dependency(cxxopts)
CPMAddPackage(
NAME cxxopts
URL https://github.com/jarro2783/cxxopts/archive/59656709c0c58fcd0ed18b38e02938dbe05284c5.zip
OPTIONS
"CXXOPTS_BUILD_EXAMPLES OFF"
"CXXOPTS_BUILD_TESTS OFF"
"CXXOPTS_ENABLE_INSTALL OFF"
)
endif()
-48
View File
@@ -1,48 +0,0 @@
set(dep_root "${simdjson_SOURCE_DIR}/dependencies/.cache")
if(DEFINED ENV{simdjson_DEPENDENCY_CACHE_DIR})
set(dep_root "$ENV{simdjson_DEPENDENCY_CACHE_DIR}")
endif()
function(import_dependency NAME GITHUB_REPO COMMIT)
message(STATUS "Importing ${NAME} (${GITHUB_REPO}@${COMMIT})")
set(target "${dep_root}/${NAME}")
# If the folder exists in the cache, then we assume that everything is as
# should be and do nothing
if(EXISTS "${target}")
set("${NAME}_SOURCE_DIR" "${target}" PARENT_SCOPE)
return()
endif()
set(zip_url "https://github.com/${GITHUB_REPO}/archive/${COMMIT}.zip")
set(archive "${dep_root}/archive.zip")
set(dest "${dep_root}/_extract")
file(DOWNLOAD "${zip_url}" "${archive}")
file(MAKE_DIRECTORY "${dest}")
execute_process(
WORKING_DIRECTORY "${dest}"
COMMAND "${CMAKE_COMMAND}" -E tar xf "${archive}")
file(REMOVE "${archive}")
# GitHub archives only ever have one folder component at the root, so this
# will always match that single folder
file(GLOB dir LIST_DIRECTORIES YES "${dest}/*")
file(RENAME "${dir}" "${target}")
set("${NAME}_SOURCE_DIR" "${target}" PARENT_SCOPE)
endfunction()
# Delegates to the dependency
macro(add_dependency NAME)
if(NOT DEFINED "${NAME}_SOURCE_DIR")
message(FATAL_ERROR "Missing ${NAME}_SOURCE_DIR variable")
endif()
add_subdirectory("${${NAME}_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/_deps/${NAME}" EXCLUDE_FROM_ALL)
endmacro()
function(set_off NAME)
set("${NAME}" OFF CACHE INTERNAL "")
endfunction()
+897 -234
View File
File diff suppressed because it is too large Load Diff
+256
View File
@@ -0,0 +1,256 @@
Builder
==========
Sometimes you want to generate JSON string outputs efficiently.
The simdjson library provides high-performance low-level facilities.
When using these low-level functionalities, you are responsible to
define the structure of your JSON document. Our more advanced interface
automates the process using C++26 static reflection: you get both high
speed and high convenience.
- [Builder](#builder)
* [Overview: string_builder](#overview--string-builder)
* [Example: string_builder](#example--string-builder)
* [C++26 static reflection](#c--26-static-reflection)
+ [Without `string_buffer` instance](#without--string-buffer--instance)
+ [Without `string_buffer` instance but with explicit error handling](#without--string-buffer--instance-but-with-explicit-error-handling)
Overview: string_builder
---------------------------
The string_builder class is a low-level utility for constructing JSON strings representing documents. It is optimized for performance, potentially leveraging kernel-specific features like SIMD instructions for tasks such as string escaping. This class supports atomic types (e.g., booleans, numbers, strings) but does not handle composed types directly (like arrays or objects).
Note that JSON strings are always encoded as UTF-8.
An `string_builder` is created with an initial buffer capacity (e.g., 1kB). The memory
is reallocated when needed.
The efficiency of `string_builder` stems from its internal use of a resizable array or buffer. When you append data, it adds the characters to this buffer, resizing it only when necessary, typically in a way that minimizes reallocations. This approach contrasts with regular string concatenation, where each operation creates a new string, copying all previous content, leading to quadratic time complexity for repeated concatenations.
It has the following methods to add content to the string:
- `append(number_type v)`: Appends a number (including booleans) to the JSON buffer. Booleans are converted to the strings "false" or "true". Numbers are formatted according to the JSON standard, with floating-point numbers using the shortest representation that accurately reflects the value.
- `append(char c)`: Appends a single character to the JSON buffer.
- `append_null()`: Appends the string "null" to the JSON buffer.
- `clear()`: Clears the contents of the JSON buffer, resetting the position to 0 while retaining the allocated capacity.
- `escape_and_append(std::string_view input)`: Appends a string view to the JSON buffer after escaping special characters (e.g., quotes, backslashes) as required by JSON.
- `escape_and_append_with_quotes(std::string_view input)` Appends a string view surrounded by double quotes (e.g., "input") to the JSON buffer after escaping special characters.
Parameters:
- `escape_and_append_with_quotes(char input)`: Appends a single character surrounded by double quotes (e.g., "c") to the JSON buffer after escaping it if necessary.
- `append_raw(const char *c)`: Appends a null-terminated C string directly to the JSON buffer without escaping.
- `append_raw(std::string_view input)`: Appends a string view directly to the JSON buffer without escaping.
- `append_raw(const char *str, size_t len)`: Appends a specified number of characters from a C string directly to the JSON
After writting the content, if you have reasons to believe that the content might violate UTF-8 conventions, you can check it as follows:
- `validate_unicode()`: Checks if the content in the JSON buffer is valid UTF-8. Returns: true if the content is valid UTF-8, false otherwise.
You might need to do unicode validation if you have strings in your data structures containing
malformed UTF-8. Note that we do not automatically call `validate_unicode()`.
Once you are satisfied, you can recover the string as follows:
- `operator std::string()`: Converts the JSON buffer to an std::string. (Might throw if an error occurred.)
- `operator std::string_view()`: Converts the JSON buffer to an std::string_view. (Might throw if an error occurred.)
- `view()`: Returns a view of the written JSON buffer as a `simdjson_result<std::string_view>`.
The later method (`view()`) is recommended. For performance reasons, we expect you to explicitly call `validate_unicode()` as needed (e.g., prior to calling `view()`).
Example: string_builder
---------------------------
```C++
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
void serialize_car(const Car& car, simdjson::builder::string_builder& builder) {
// start of JSON
builder.start_object();
// "make"
builder.append_key_value("make", car.make);
builder.append_comma();
// "model"
builder.append_key_value("model", car.model);
builder.append_comma();
// "year"
builder.append_key_value("year", car.year);
builder.append_comma();
// "tire_pressure"
builder.escape_and_append_with_quotes("tire_pressure");
builder.append_colon();
builder.start_array();
// vector tire_pressure
for (size_t i = 0; i < car.tire_pressure.size(); ++i) {
builder.append(car.tire_pressure[i]);
if (i < car.tire_pressure.size() - 1) {
builder.append_comma();
}
}
builder.end_array();
builder.end_object();
}
bool car_test() {
simdjson::builder::string_builder sb;
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
serialize_car(c, sb);
std::string_view p{sb};
// p holds the JSON:
// "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}"
return true;
}
```
The `string_builder` constructor takes an optional parameter which specifies the initial
memory allocation in byte. If you know approximately the size of your JSON output, you can
pass this value as a parameter (e.g., `simdjson::builder::string_builder sb{1233213}`).
The `string_builder` might throw an exception in case of error when you cast it result to `std::string_view`. If you wish to avoid exceptions, you can use the following programming pattern:
```cpp
std::string_view p;
if(sb.view().get(p)) {
return false; // there was an error
}
```
In all cases, the `std::string_view` instance depends the corresponding `string_builder` instance.
### C++20
If you have C++20, you can simplify the code, as the `std::vector<double>` is automatically
supported.
```cpp
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
simdjson::builder::string_builder sb;
sb.start_object();
sb.append_key_value("make", c.make);
sb.append_comma();
sb.append_key_value("model", c.model);
sb.append_comma();
sb.append_key_value("year", c.year);
sb.append_comma();
sb.append_key_value("tire_pressure", c.tire_pressure);
sb.end_object();
std::string_view p = sb.view();
```
With C++20, you can similarly handle standard containers transparently.
For example, you can serialize `std::map<std::string,T>` types.
```cpp
std::map<std::string,double> c = {{"key1", 1}, {"key2", 1}};
simdjson::builder::string_builder sb;
sb.append(c);
std::string_view p = sb.view();
```
You can also serialize `std::vector<T>` types.
```cpp
std::vector<std::vector<double>> c = {{1.0, 2.0}, {3.0, 4.0}};
simdjson::builder::string_builder sb;
sb.append(c);
std::string_view p = sb.view();
```
You can also skip the creation for the `string_builder` instance in such simple cases.
```cpp
std::vector<std::vector<double>> c = {{1.0, 2.0}, {3.0, 4.0}};
std::string json = simdjson::to_json(c);
```
We do recommend that you create and reuse the `string_builder` instance for performance
reasons.
C++26 static reflection
------------------------
Static reflection (or compile-time reflection) in C++26 introduces a powerful compile-time mechanism that allows a program to inspect and manipulate its own structure, such as types, variables, functions, and other program elements, during compilation. Unlike runtime reflection in languages like Java or Python, C++26s static reflection operates entirely at compile time, aligning with C++s emphasis on zero-overhead abstractions and high performance. It means
that you can delegate much of the work to the library.
If you have a compiler with support C++26 static reflection, you can compile
your code with the `SIMDJSON_STATIC_REFLECTION` macro set:
```cpp
#define SIMDJSON_STATIC_REFLECTION 1
//...
#include "simdjson.h"
```
And then you can append your data structures to a `string_builder` instance
automatically. In most cases, it should work automatically:
```cpp
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
bool car_test() {
simdjson::builder::string_builder sb;
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
sb << c;
std::string_view p{sb};
// p holds the JSON:
// "{\"make\":\"Toyota\",\"model\":\"Corolla\",\"year\":2017,\"tire_pressure\":[30.0,30.2,30.513,30.79]}"
return true;
}
```
### Without `string_buffer` instance
In some instances, you might want to create a string directly from your own data type.
You can create a string directly, without an explicit `string_builder` instance
with the `simdjson::to_json` template function.
(Under the hood a `string_builder` instance may still be created.)
```cpp
struct Car {
std::string make;
std::string model;
int64_t year;
std::vector<double> tire_pressure;
};
void f() {
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
std::string json = simdjson::to_json(c);
}
```
If you know the output size, in bytes, of your JSON string, you may
pass it as a second parameter (e.g., `simdjson::to_json(c, 31123)`).
### Without `string_buffer` instance but with explicit error handling
If prefer a version without exceptions and explicit error handling, you can use the following
pattern:
```cpp
std::string json;
if(simdjson::to(c).get(json)) {
// there was an error
} else {
// json contain the serialized JSON
}
```
+174 -5
View File
@@ -1,13 +1,18 @@
The Document-Object-Model (DOM) front-end
==========
An overview of what you need to know to use simdjson, with examples.
An overview of what you need to know to use simdjson to parse JSON documents with
our DOM API, with examples. [Our documentation regarding the generation (serialization) of JSON documents is in a
separate document](https://github.com/simdjson/simdjson/blob/master/doc/builder.md).
* [DOM vs On Demand](#dom-vs-on-demand)
* [DOM vs On-Demand](#dom-vs-on-demand)
* [The Basics: Loading and Parsing JSON Documents](#the-basics-loading-and-parsing-json-documents-using-the-dom-front-end)
* [Using the Parsed JSON](#using-the-parsed-json)
* [C++17 Support](#c17-support)
* [C++20 Support](#c20-support)
* [JSON Pointer](#json-pointer)
* [JSONPath](#jsonpath)
* [Error Handling](#error-handling)
* [Error Handling Example](#error-handling-example)
* [Exceptions](#exceptions)
@@ -18,7 +23,7 @@ An overview of what you need to know to use simdjson, with examples.
* [Padding and Temporary Copies](#padding-and-temporary-copies)
* [Performance Tips](#performance-tips)
DOM vs On Demand
DOM vs On-Demand
----------------------------------------------
The simdjson library offers two distinct approaches on how to access a JSON document. We support
@@ -26,6 +31,10 @@ a conventional Document-Object-Model (DOM) front-end. In such a scenario, the JS
entirely parsed, validated and materialized in memory as the first step. The programmer may
then access the parsed data using this in-memory model.
On-Demand is a different model where you parse just what you need, directly into your own
data structure. The On-Demand approach, when well tuned, can provide superior performance.
[We refer you to the On-Demand documentation for further details](https://github.com/simdjson/simdjson/blob/master/doc/basics.md).
The Basics: Loading and Parsing JSON Documents using the DOM front-end
----------------------------------------------
@@ -59,6 +68,26 @@ std::string data = "my data";
simdjson::padded_string my_padded_data(data); // copies to a padded buffer
```
You can then parse the JSON document from the `simdjson::padded_string` instance:
```cpp
simdjson::dom::parser parser;
simdjson::dom::element doc = parser.parse(my_padded_data);
```
Whenever you pass an `std::string` reference to `parser::parse`,
the parser will access the bytes beyond the end of
the string but before the end of the allocated memory (`std::string::capacity()`).
If you are using a sanitizer that checks for reading uninitialized bytes or `std::string`'s
container-overflow checks, you may encounter sanitizer warnings.
You can safely ignore these warnings. Or you can call `simdjson::pad(std::string&)` to pad the
string with `SIMDJSON_PADDING` spaces: this function returns a `simdjson::padding_string_view` which can be be passed to the parser's iterator function:
```c++
std::string json = "[1]";
dom::element doc = parser.parse(simdjson::pad(json));
```
The parsed document resulting from the `parser.load` and `parser.parse` calls depends on the `parser` instance. Thus the `parser` instance must remain in scope. Furthermore, you must have at most one parsed document in play per `parser` instance.
You cannot copy a `parser` instance, you may only move it.
@@ -69,8 +98,14 @@ During the`load` or `parse` calls, neither the input file nor the input string a
For best performance, a `parser` instance should be reused over several files: otherwise you will needlessly reallocate memory, an expensive process. It is also possible to avoid entirely memory allocations during parsing when using simdjson. [See our performance notes for details](performance.md).
If you need a lower-level interface, you may call the function `parser.parse(const char * p, size_t l)` on a pointer `p` while specifying the
length of your input `l` in bytes. To see how to get the very best performance from a low-level approach, you way want to read our [performance notes](https://github.com/simdjson/simdjson/blob/master/doc/performance.md#padding-and-temporary-copies) on this topic (see the Padding and Temporary Copies section).
length of your input `l` in bytes.
*Windows-specific*: Windows users who need to read files with
non-ANSI characters in the name should set their code page to
UTF-8 (65001). This should be the default with Windows 11 and better.
Further, they may use the AreFileApisANSI function to determine whether
the filename is interpreted using the ANSI or the system default OEM
codepage, and they may call SetFileApisToOEM accordingly.
Using the Parsed JSON
---------------------
@@ -92,6 +127,12 @@ Once you have an element, you can navigate it with idiomatic C++ iterators, oper
std::cout << "I parsed " << value << " from " << numberstring.data() << std::endl;
```
The strings contain unescaped valid UTF-8 strings: no unmatched surrogate is allowed.
Internally, numbers are stored as either 64-bit integers or 64-bit floating-point numbers.
Thus it is possible to get the full 64-bit integer range (either signed or unsigned).
By default, the string `-0` is parsed as the integer 0 as in Pytho or C++. If you set the macro
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to `1` when building simdjson, you can get that `-0` is mapped to `-0.0`
as in JavaScript. You can get the desired effect by building simdjson with cmake setting the
`SIMDJSON_MINUS_ZERO_AS_FLOAT` to on: `cmake -B build -D SIMDJSON_MINUS_ZERO_AS_FLOAT=ON`.
* **Field Access:** To get the value of the "foo" field in an object, use `object["foo"]`.
* **Array Iteration:** To iterate through an array, use `for (auto value : array) { ... }`. If you
know the type of the value, you can cast it right there, too! `for (double value : array) { ... }`
@@ -205,6 +246,23 @@ for (dom::key_value_pair field : object) {
}
```
C++20 Support
------------
simdjson library also supports some C++20 feature including `std::ranges`:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
auto justmodel = [](auto car) { return car["model"]; };
for (auto car : parser.parse(cars_json).get_array() | std::views::transform(justmodel)) {
std::cout << car << std::endl;
}
```
JSON Pointer
------------
@@ -251,8 +309,119 @@ for (dom::element car_element : cars) {
}
```
JSONPath
------------
The simdjson library supports a subset of [JSONPath](https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00) through the `at_path()` method, allowing you to reach further into the document in a single call. The subset of JSONPath that is implemented is the subset that is trivially convertible into the JSON Pointer format, using `.` to access a field and `[]` to access a specific index.
Consider the following example:
```c++
auto cars_json = R"( [
{ "make": "Toyota", "model": "Camry", "year": 2018, "tire_pressure": [ 40.1, 39.9, 37.7, 40.4 ] },
{ "make": "Kia", "model": "Soul", "year": 2012, "tire_pressure": [ 30.1, 31.0, 28.6, 28.7 ] },
{ "make": "Toyota", "model": "Tercel", "year": 1999, "tire_pressure": [ 29.8, 30.0, 30.2, 30.5 ] }
] )"_padded;
dom::parser parser;
dom::element doc;
auto error = parser.parse(cars_json).get(doc);
if(error) { /*won't happen*/ }
double p;
error = doc.at_path("[0].tire_pressure[1]").get(p);
if(error) { /*won't happen*/ }
cout << p << endl; // Prints 39.9
```
We also support the `$` prefix. When you start a JSONPath expression with $, you are indicating that the path starts from the root of the JSON document. E.g.,
```c++
auto json = R"( { "c" :{ "foo": { "a": [ 10, 20, 30 ] }}, "d": { "foo2": { "a": [ 10, 20, 30 ] }} , "e": 120 })"_padded;
dom::parser parser;
dom::element doc;
auto error = parser.parse(json).get(doc);
if(error) { /*won't happen*/ }
dom::object obj;
error = doc.get_object().get(obj);
if(error) { /*won't happen*/ }
int64_t x;
error = obj.at_path("$[3].foo.a[1]").get(x);
if(error) { /*won't happen*/ }
if(x != 20) { /*won't happen*/ }
x = obj.at_path("$.d.foo2.a.2");
if(error) { /*won't happen*/ }
```
## Using `at_path_with_wildcard` for JSONPath Queries
The `at_path_with_wildcard` function in simdjson extends the JSONPath querying capabilities by supporting wildcard expressions (`*`) in JSON paths. This allows users to retrieve multiple elements from a JSON document in a single query. For example, you can use `$.address.*` to fetch all fields within the `address` object or `$.phoneNumbers[*].numbers[*]` to retrieve all phone numbers across multiple objects in an array.
The `*` wildcard matches all elements at a specific level. For instance, `$.address.*` retrieves all key-value pairs in the `address` object, while `$.*.streetAddress` fetches all `streetAddress` fields across objects at the root level. You can combine wildcards with array indexing. For example, `$.phoneNumbers[*].numbers[1]` retrieves the second number from each `numbers` array in the `phoneNumbers` array. If no elements match the wildcard query, the function returns an empty result. For instance, querying `$.empty_object.*` or `$.empty_array.*` will yield an empty set.
### Example Usage
Here is an example demonstrating the use of `at_path_with_wildcard`:
```cpp
simdjson::padded_string json_string = R"(
{
"firstName": "John",
"lastName": "doe",
"age": 26,
"address": {
"streetAddress": "naist street",
"city": "Nara",
"postalCode": "630-0192"
},
"phoneNumbers": [
{
"type": "iPhone",
"numbers": ["0123-4567-8888", "0123-4567-8788"]
},
{
"type": "home",
"numbers": ["0123-4567-8910"]
}
]
})"_padded;
dom::parser parser;
dom::element parsed_json = parser.parse(json_string);
std::vector<dom::element> values;
// Fetch all fields in the address object
auto error = parsed_json.at_path_with_wildcard("$.address.*").get(values);
if(error) {
// do something
}
for (auto &value : values) {
std::string_view field;
error = value.get(field);
if(error) {
// do something
}
std::cout << field << std::endl;
}
// Fetch all phone numbers
error = parsed_json.at_path_with_wildcard("$.phoneNumbers[*].numbers[*]").get(values);
if(error) {
// do something
}
for (auto &value : values) {
std::string_view number;
error = value.get(number);
if(error) {
// do something
}
std::cout << number << std::endl;
}
```
This function is particularly useful for extracting data from complex JSON structures with nested arrays and objects. By leveraging wildcards, you can simplify your queries and reduce the need for multiple iterations.
Error Handling
--------------
@@ -663,5 +832,5 @@ Setting the `realloc_if_needed` parameter `false` in this manner may lead to bet
Performance Tips
---------------------
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directrive, you must set it manually (e.g., `-DNDEBUG`).
- For release builds, we recommend setting `NDEBUG` pre-processor directive when compiling the `simdjson` library. Importantly, using the optimization flags `-O2` or `-O3` under GCC and LLVM clang does not set the `NDEBUG` directive, you must set it manually (e.g., `-DNDEBUG`).
- For long streams of JSON documents, consider [`iterate_many`](iterate_many.md) and [`parse_many`](parse_many.md) for better performance.
+3 -1
View File
@@ -24,6 +24,8 @@ The current implementations are:
* westmere: SSE4.2 (2010 Westmere or later).
* arm64: 64-bit ARMv8-A NEON
* ppc64: 64-bit POWER8 and POWER9 with VSX and ALTIVEC extensions. Both big endian and little endian are implemented, depending on the compiler you are using. The library is tested on recent, little-endian, POWER systems.
* lasx: Loongson Advanced SIMD EXtension (LASX), a 256-bit vector expansion for the LoongArch architecture.
* lsx: Loongson SIMD EXtension (LSX), a 128-bit vector expansion for the LoongArch architecture.
* fallback: A generic implementation that runs on any 64-bit processor.
In many cases, you don't know where your compiled binary is going to run, so simdjson automatically
@@ -33,7 +35,7 @@ it will include 2 (ppc64 and fallback).
If you know more about where you're going to run and want to save the space, you can disable any of
these implementations at compile time with `-DSIMDJSON_IMPLEMENTATION_X=0` (where X is ICELAKE, HASWELL,
WESTMERE, ARM64, PPC64 and FALLBACK).
WESTMERE, ARM64, PPC64, LSX, LASX and FALLBACK).
The simdjson library automatically sets header flags for each implementation as it compiles; there
is no need to set architecture-specific flags yourself (e.g., `-mavx2`, `/AVX2` or
+116 -2
View File
@@ -25,6 +25,7 @@ Contents
- [Use cases](#use-cases)
- [Tracking your position](#tracking-your-position)
- [Incomplete streams](#incomplete-streams)
- [C++20 features](#c20-features)
Motivation
-----------
@@ -102,7 +103,12 @@ remove almost entirely its cost and replaces it by the overhead of a thread, whi
cheaper. Ain't that awesome!
Thread support is only active if thread supported is detected in which case the macro
SIMDJSON_THREADS_ENABLED is set. Otherwise the library runs in single-thread mode.
SIMDJSON_THREADS_ENABLED is set. You can also manually pass `SIMDJSON_THREADS_ENABLED=1` flag
to the library. Otherwise the library runs in single-thread mode.
You should be consistent. If you link against the simdjson library built for multithreading
(i.e., with `SIMDJSON_THREADS_ENABLED`), then you should build your application with multithreading
system (setting `SIMDJSON_THREADS_ENABLED=1` and linking against a thread library).
A `document_stream` instance uses at most two threads: there is a main thread and a worker thread.
@@ -124,7 +130,7 @@ If your documents are all objects or arrays, then you may even have nothing betw
E.g., `[1,2]{"32":1}` is recognized as two documents.
Some official formats **(non-exhaustive list)**:
- [Newline-Delimited JSON (NDJSON)](http://ndjson.org/)
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec/)
- [JSON lines (JSONL)](http://jsonlines.org/)
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by JsonStream!
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
@@ -288,3 +294,111 @@ string
object
array
```
C++20 features
--------------------
In C++20, the standard introduced the notion of *customization point*.
A customization point is a function or function object that can be customized for different types. It allows library authors to provide default behavior while giving users the ability to override this behavior for specific types.
A tag_invoke function serves as a mechanism for customization points. It is not directly part of the C++ standard library but is often used in libraries that implement customization points.
The tag_invoke function is typically a generic function that takes a tag type and additional arguments.
The first argument is usually a tag type (often an empty struct) that uniquely identifies the customization point (e.g., deserialization of custom types in simdjson). Users or library providers can specialize tag_invoke for their types by defining it in the appropriate namespace, often inline namespace.
You can deserialize you own data structures conveniently if your system supports C++20.
When it is the case, the macro `SIMDJSON_SUPPORTS_CONCEPTS` will be set to 1 by
the simdjson library.
Consider a custom class `Car`:
```C++
struct Car {
std::string make;
std::string model;
int year;
std::vector<float> tire_pressure;
};
```
You may support deserializing directly from a JSON value or document to your own `Car` instance
by defining a single `tag_invoke` function:
```C++
namespace simdjson {
// This tag_invoke MUST be inside simdjson namespace
template <typename simdjson_value>
auto tag_invoke(deserialize_tag, simdjson_value &val, Car& car) {
ondemand::object obj;
auto error = val.get_object().get(obj);
if (error) {
return error;
}
if ((error = obj["make"].get_string(car.make))) {
return error;
}
if ((error = obj["model"].get_string(car.model))) {
return error;
}
if ((error = obj["year"].get(car.year))) {
return error;
}
if ((error = obj["tire_pressure"].get<std::vector<float>>().get(
car.tire_pressure))) {
return error;
}
return simdjson::SUCCESS;
}
} // namespace simdjson
```
Importantly, the `tag_invoke` function must be inside the `simdjson` namespace.
Let us explain each argument of `tag_invoke` function.
- `simdjson::deserialize_tag`: it is the tag for Customization Point Object (CPO). You may often ignore this parameter. It is used to indicate that you mean to provide a deserialization function for simdjson.
- `var`: It receives automatically a `simdjson` value type (document, value, document_reference).
- The third parameter is an instance of the type that you want to support.
Please see our main documentation (`basics.md`) under
"Use `tag_invoke` for custom types (C++20)" for details about
tag_invoke functions.
Given a stream of JSON documents, you can add them to a data structure
such as a `std::vector<Car>` like so if you support exceptions:
```C++
padded_string json =
R"( { "make": "Toyota", "model": "Camry", "year": 2018,
"tire_pressure": [ 40.1, 39.9 ] }
{ "make": "Kia", "model": "Soul", "year": 2012,
"tire_pressure": [ 30.1, 31.0 ] }
{ "make": "Toyota", "model": "Tercel", "year": 1999,
"tire_pressure": [ 29.8, 30.0 ] }
)"_padded;
ondemand::parser parser;
ondemand::document_stream stream;
[[maybe_unused]] auto error = parser.iterate_many(json).get(stream);
std::vector<Car> cars;
for(auto doc : stream) {
cars.push_back((Car)doc); // an exception may be thrown
}
```
Otherwise you may use this longer version for explicit handling of errors:
```C++
std::vector<Car> cars;
for(auto doc : stream) {
Car c;
if ((error = doc.get<Car>().get(c))) {
std::cerr << simdjson::error_message(error); << std::endl;
return EXIT_FAILURE;
}
cars.push_back(c);
}
```
+31 -31
View File
@@ -9,8 +9,8 @@ Whether we parse JSON or XML, or any other serialized format, there are relative
- Another popular approach is the schema-based deserialization model.
We propose an approach that is as easy to use and often as flexible as the DOM approach, yet as fast and
efficient as the schema-based or event-based approaches. We call this new approach "On Demand". The
simdjson On Demand API offers a familiar, friendly DOM API and
efficient as the schema-based or event-based approaches. We call this new approach "On-Demand". The
simdjson On-Demand API offers a familiar, friendly DOM API and
provides the performance of just-in-time parsing on top of the simdjson superior performance.
To achieve ease of use, we mimicked the *form* of a traditional DOM API: you can iterate over
@@ -18,7 +18,7 @@ arrays, look up fields in objects, and extract native values like `double`, `uin
To achieve performance, we introduced some key limitations that make the DOM API *streaming*:
array/object iteration cannot be restarted, and string/number values can only be parsed once. If
these limitations are acceptable to you, the On Demand API could help you write maintainable
these limitations are acceptable to you, the On-Demand API could help you write maintainable
applications with a computation efficiency that is difficult to surpass.
A code example illustrates our API from a programmer's point of view:
@@ -72,24 +72,24 @@ This streaming approach means that unused fields and values are not parsed or
converted, thus saving space and time. In our example, the `"name"`, `"followers_count"`,
and `"friends_count"` keys and matching values are skipped.
Further, the On Demand API does not parse a value *at all* until you try to convert it (e.g., to `double`,
Further, the On-Demand API does not parse a value *at all* until you try to convert it (e.g., to `double`,
`int`, `string`, or `bool`). In our example, when accessing the key-value pair `"retweet_count": 82`, the parser
may not convert the pair of characters `82` to the binary integer 82. Because the programmer specifies the data
type, we avoid branch mispredictions related to data type determination and improve the performance.
We expect users of an On Demand API to work in terms of a JSON dialect, which is a set of expectations and
We expect users of an On-Demand API to work in terms of a JSON dialect, which is a set of expectations and
specifications that come in addition to the [JSON specification](https://www.rfc-editor.org/rfc/rfc8259.txt).
The On Demand approach is designed around several principles:
The On-Demand approach is designed around several principles:
* **Streaming (\*):** It avoids preparsing values, keeping the memory usage and the latency down.
* **Forward-Only:** To prevent reiteration of the same values and to keep the number of variables down (literally), only a single index is maintained and everything uses it (even if you have nested for loops). This means when you are going through an array of arrays, for example, that the inner array loop will advance the index to the next comma, and the array can just pick it up and look at it.
* **Natural Iteration:** A JSON array or object can be iterated with a normal C++ for loop. Nested arrays and objects are supported by nested for loops.
* **Use-Specific Parsing:** Parsing is always specific to the type required by the programmer. For example, if the programmer asks for an unsigned integer, we just start parsing digits. If there were no digits, we toss an error. There are even different parsers for `double`, `uint64_t` and `int64_t` values. This use-specific parsing avoids the branchiness of a generic "type switch," and makes the code more inlineable and compact.
* **Validate What You Use:** On Demand deliberately validates the values you use and the structure leading to it, but nothing else. The goal is a guarantee that the value you asked for is the correct one and is not malformed: there must be no confusion over whether you got the right value.
* **Validate What You Use:** On-Demand deliberately validates the values you use and the structure leading to it, but nothing else. The goal is a guarantee that the value you asked for is the correct one and is not malformed: there must be no confusion over whether you got the right value.
To understand why On Demand is different, it is helpful to review the major
To understand why On-Demand is different, it is helpful to review the major
approaches to parsing and parser APIs in use today.
### DOM Parsers
@@ -102,11 +102,11 @@ or indexing (`object["key"]`). In some cases, the values are even deserialized d
maps.
The DOM approach is conceptually simple and "programmer friendly". Using the
DOM tree is often easy enough that many users use the DOM as-is instead of creating
DOM tree is often easy enough that many users process the DOM as-is instead of creating
their own custom data structures.
The DOM approach was the only way to parse JSON documents up to version 0.6 of the simdjson library.
Our DOM API looks similar to our On Demand example, except
Our DOM API looks similar to our On-Demand example, except
it calls `parse` instead of `iterate`:
```c++
@@ -152,7 +152,7 @@ a tweet right now, or is this from some other place in the document
entirely? Though an event-based approach may allow superior performance, it is demanding of the programmer
who must efficiently keep track of its current state within the JSON input.
The following is event-based example of the Twitter problem we have reviewed in the DOM and On Demand
The following is event-based example of the Twitter problem we have reviewed in the DOM and On-Demand
examples. To make it short enough to use as an example at all, it has heavily redacted: it only solves
a part of the problem (does not get user.screen_name), it has bugs (it does not handle sub-objects
in a tweet at all), and it uses a theoretical, simple event-based API that minimizes ceremony.
@@ -257,7 +257,7 @@ stress the branch prediction. Though branch predictors improve with each new gen
the cost of branch mispredictions also tends to increase as pipelines expand, and the processors become
able to schedule longer streams of instructions.
On Demand parsing is tailor-made to solve this problem at the source, parsing values only after the
On-Demand parsing is tailor-made to solve this problem at the source, parsing values only after the
user declares their type by asking for a `double`, an `int`, a `string`, etc. It attempts to do so while
preserving most of the flexibility of DOM parsing.
@@ -297,7 +297,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
Since this is the first time this parser has been used, `iterate()` first allocates internal
parser buffers if this is the first time through. When reusing an existing parser, allocation
only happens if the new document is bigger than internal buffers can handle. The On Demand
only happens if the new document is bigger than internal buffers can handle. The On-Demand
API only ever allocates memory in the `iterate()` function call.
The simdjson library then preprocesses the JSON text at high speed, finding all tokens (i.e. the starting
@@ -492,7 +492,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
Because of the cast to uint64_t, simdjson knows it's parsing an unsigned integer. This lets
us use a fast parser which *only* knows how to parse digits. It validates that it is an integer
by rejecting negative numbers, strings, and other values based on the fact that they are not the
digits 0-9. This type specificity is part of why parsing with on demand is so fast: you lose all
digits 0-9. This type specificity is part of why parsing with On-Demand is so fast: you lose all
the code that has to understand those other types.
The iterator is advanced to the `}`, and depth decreased back to 3 (root > statuses > tweet).
@@ -597,7 +597,7 @@ To help visualize the algorithm, we'll walk through the example C++ given at the
This means you can very efficiently do things like read a single value from a JSON file, or take
the top N, for example. It also means the things you don't use won't be fully validated. This is
a general principle of On Demand: don't validate what you don't use. We still fully validate
a general principle of On-Demand: don't validate what you don't use. We still fully validate
values you do use, however, as well as the objects and arrays that lead to them, so that you can
be sure you get the information you need.
@@ -654,7 +654,7 @@ for(auto field : doc.get_object()) {
### Iteration Safety
The On Demand API is powerful. To compensate, we add some safeguards to ensure that it can be used without fear
The On-Demand API is powerful. To compensate, we add some safeguards to ensure that it can be used without fear
in production systems:
- If the value fails to be parsed as one type, the program can try to parse it as something else until the program succeeds. Thus
@@ -667,7 +667,7 @@ in production systems:
if it was `nullptr` but did not care what the actual value was--it will iterate. The destructor automates
the iteration.
Some care is needed when using the On Demand API in scenarios where you need to access several sibling arrays or objects because
Some care is needed when using the On-Demand API in scenarios where you need to access several sibling arrays or objects because
only one object or array can be active at any one time. Let us consider the following example:
```C++
@@ -709,36 +709,36 @@ A correct usage is given by the following example:
}
```
### Benefits of the On Demand Approach
### Benefits of the On-Demand Approach
We expect that the On Demand approach has many of the performance benefits of the schema-based approach, while providing a flexibility that is similar to that of the DOM-based approach.
We expect that the On-Demand approach has many of the performance benefits of the schema-based approach, while providing a flexibility that is similar to that of the DOM-based approach.
* Faster than DOM in some cases. Reduced memory usage.
* Straightforward, programmer-friendly interface (arrays and objects).
* Highly expressive, beyond deserialization and pointer queries: many tasks can be accomplished with little code.
### Limitations of the On Demand Approach
### Limitations of the On-Demand Approach
The On Demand approach has some limitations:
The On-Demand approach has some limitations:
* Because it operates in streaming mode, you only have access to the current element in the JSON document. Furthermore, the document is traversed in order so the code is sensitive to the order of the JSON nodes in the same manner as an event-based approach (e.g., SAX). (The one exception to this is field lookup, which is more *performant* when the order of lookups matches the order of fields in the document, but which will still work with out-of-order fields, with a performance hit.)
* The On Demand approach is less safe than DOM: we only validate the components of the JSON document that are used and it is possible to begin ingesting an invalid document only to find out later that the document is invalid. Are you fine ingesting a large JSON document that starts with well formed JSON but ends with invalid JSON content?
* The On-Demand approach is less safe than DOM: we only validate the components of the JSON document that are used and it is possible to begin ingesting an invalid document only to find out later that the document is invalid. Are you fine ingesting a large JSON document that starts with well formed JSON but ends with invalid JSON content?
There are currently additional technical limitations which we expect to resolve in future releases of the simdjson library:
* The simdjson library offers runtime dispatching which allows you to compile one binary and have it run at full speed on different processors, taking advantage of the specific features of the processor. The On Demand API has limited runtime dispatch support. Under x64 systems, to fully benefit from the On Demand API, we recommend that you compile your code for a specific processor. E.g., if your processor supports AVX2 instructions, you should compile your binary executable with AVX2 instruction support (by using your compiler's commands). If you are sufficiently technically proficient, you can implement runtime dispatching within your application, by compiling your On Demand code for different processors.
* The simdjson library offers runtime dispatching which allows you to compile one binary and have it run at full speed on different processors, taking advantage of the specific features of the processor. The On-Demand API has limited runtime dispatch support. Under x64 systems, to fully benefit from the On-Demand API, we recommend that you compile your code for a specific processor. E.g., if your processor supports AVX2 instructions, you should compile your binary executable with AVX2 instruction support (by using your compiler's commands). If you are sufficiently technically proficient, you can implement runtime dispatching within your application, by compiling your On-Demand code for different processors.
* There is an initial phase which scans the entire document quickly, irrespective of the size of the document. We plan to break this phase into distinct steps for large files in a future release as we have done with other components of our API (e.g., `parse_many`).
### Applicability of the On Demand Approach
### Applicability of the On-Demand Approach
At this time we recommend the On Demand API in the following cases:
At this time we recommend the On-Demand API in the following cases:
1. The 64-bit hardware (CPU) used to run the software is known at compile time. If you need runtime dispatching because you cannot be certain of the hardware used to run your software, you will be better served with the core simdjson API. (This only applies to x64 (AMD/Intel). On 64-bit ARM hardware, runtime dispatching is unnecessary.)
2. The used parts of JSON files do not need to be validated and the layout of the nodes follows a strict JSON dialect. If you are receiving JSON from other systems, you might be better served with core simdjson API as it fully validates the JSON inputs and allows you to navigate through the document at will.
3. Speed and efficiency are of the utmost importance. Keep in mind that the core simdjson API is highly efficient so adopting the On Demand API is not necessary for high efficiency.
3. Speed and efficiency are of the utmost importance. Keep in mind that the core simdjson API is highly efficient so adopting the On-Demand API is not necessary for high efficiency.
4. As a developer, you value a clean, flexible and maintainable API.
Good applications for the On Demand API might be:
Good applications for the On-Demand API might be:
* You are working from pre-existing large JSON files that have been vetted. You expect them to be well formed according to a known JSON dialect and to have a consistent layout. For example, you might be doing biomedical research or machine learning on top of static data dumps in JSON.
* Both the generation and the consumption of JSON data is within your system. Your team controls both the software that produces the JSON and the software the parses it, your team knows and control the hardware. Thus you can fully test your system.
@@ -746,13 +746,13 @@ Good applications for the On Demand API might be:
## Checking Your CPU Selection (x64 systems)
The On Demand API uses advanced architecture-specific code for many common processors to make JSON preprocessing and string parsing faster. By default, however, most c++ compilers will compile to the least common denominator (since the program could theoretically be run anywhere). Since On Demand is inlined into your own code, it cannot always use these advanced versions unless the compiler is told to target them.
The On-Demand API uses advanced architecture-specific code for many common processors to make JSON preprocessing and string parsing faster. By default, however, most c++ compilers will compile to the least common denominator (since the program could theoretically be run anywhere). Since On-Demand is inlined into your own code, it cannot always use these advanced versions unless the compiler is told to target them.
On relevant systems, the On Demand API provides some support for runtime dispatching: that is, it will attempt to detect, at runtime, the instructions that your processor supports and optimize the code accordingly. However, it cannot always make full use of the features of your processor.
On relevant systems, the On-Demand API provides some support for runtime dispatching: that is, it will attempt to detect, at runtime, the instructions that your processor supports and optimize the code accordingly. However, it cannot always make full use of the features of your processor.
Some users wish to run at the best possible speed. Under recent Intel and AMD processors, these users should take additional steps to verify that their code is well optimized.
Given that the On Demand API offer limited runtime dispatching, it matters that your code is compiled against a specific CPU target. You should verify that the code is compiled against the target you expect. Thankfully, the simdjson library will tell you exactly what it detects as an implementation: `icelake` (AVX512 x64 processors), `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `ppc64` (64-bit POWER), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM, most programmers will want to target `arm64` (and it should do so automatically). The `fallback` is probably only good for testing purposes, not for deployment.
Given that the On-Demand API offer limited runtime dispatching, it matters that your code is compiled against a specific CPU target. You should verify that the code is compiled against the target you expect. Thankfully, the simdjson library will tell you exactly what it detects as an implementation: `icelake` (AVX512 x64 processors), `haswell` (AVX2 x64 processors), `westmere` (SSE4 x64 processors), `arm64` (64-bit ARM), `ppc64` (64-bit POWER), `lasx` (LoongArch), `lsx` (LoongArch), `fallback` (others). Under x64 processors, many programmers will want to target `haswell` whereas under ARM, most programmers will want to target `arm64` (and it should do so automatically). The `fallback` is probably only good for testing purposes, not for deployment.
```C++
std::cout << simdjson::builtin_implementation()->name() << std::endl;
@@ -777,6 +777,6 @@ In these examples, the `-march=haswell` flags targets a haswell processor and th
Instead of specifying a specific microarchitecture, you can let your compiler do the work. The `-march=native` flags says "target the current computer," which is a reasonable default for many applications which both compile and run on the same processor.
Passing `-march=native` to the compiler may make On Demand faster by allowing it to use optimizations specific to your machine. You cannot do this, however, if you are compiling code that might be run on less advanced machines. That is, be mindful that when compiling with the `-march=native` flag, the resulting binary will run on the current system but may not run on other systems (e.g., on an old processor).
Passing `-march=native` to the compiler may make On-Demand faster by allowing it to use optimizations specific to your machine. You cannot do this, however, if you are compiling code that might be run on less advanced machines. That is, be mindful that when compiling with the `-march=native` flag, the resulting binary will run on the current system but may not run on other systems (e.g., on an old processor).
If you are compiling on an ARM or POWER system, you do not need to be concerned with CPU selection during compilation. The `-march=native` flag is useful for best performance on x64 (e.g., Intel) systems but it is generally unsupported on some platforms such as ARM (aarch64) or POWER.
+7 -2
View File
@@ -103,7 +103,12 @@ cases, remove almost entirely its cost and replaces it by the overhead of a thre
of magnitude cheaper. Ain't that awesome!
Thread support is only active if thread supported is detected in which case the macro
SIMDJSON_THREADS_ENABLED is set. Otherwise the library runs in single-thread mode.
SIMDJSON_THREADS_ENABLED is set. You can also manually pass `SIMDJSON_THREADS_ENABLED=1` flag
to the library. Otherwise the library runs in single-thread mode.
You should be consistent. If you link against the simdjson library built for multithreading
(i.e., with `SIMDJSON_THREADS_ENABLED`), then you should build your application with multithreading
system (setting `SIMDJSON_THREADS_ENABLED=1` and linking against a thread library).
A `document_stream` instance uses at most two threads: there is a main thread and a worker thread.
You should expect the main thread to be fully occupied while the worker thread is partially busy
@@ -125,7 +130,7 @@ Whitespace Characters:
- **Nothing**
Some official formats **(non-exhaustive list)**:
- [Newline-Delimited JSON (NDJSON)](http://ndjson.org/)
- [Newline-Delimited JSON (NDJSON)](https://github.com/ndjson/ndjson-spec)
- [JSON lines (JSONL)](http://jsonlines.org/)
- [Record separator-delimited JSON (RFC 7464)](https://tools.ietf.org/html/rfc7464) <- Not supported by JsonStream!
- [More on Wikipedia...](https://en.wikipedia.org/wiki/JSON_streaming)
+131 -12
View File
@@ -4,9 +4,9 @@ Performance Notes
simdjson strives to be at its fastest *without tuning*, and generally achieves this. However, there
are still some scenarios where tuning can enhance performance.
Once your code is tested, we
further encourage you to define `NDEBUG` in your Release builds to disable additional runtime
further encourage you to define `NDEBUG` in your release builds to disable additional runtime
testing and get the best performance.
* [NDEBUG directive](#ndebug-directive)
* [NDEBUG macro](#ndebug-macro)
* [Reusing the parser for maximum efficiency](#reusing-the-parser-for-maximum-efficiency)
* [Reusing string buffers](#reusing-string-buffers)
* [Server Loops: Long-Running Processes and Memory Capacity](#server-loops-long-running-processes-and-memory-capacity)
@@ -14,18 +14,30 @@ testing and get the best performance.
* [Number parsing](#number-parsing)
* [Visual Studio](#visual-studio)
* [Power Usage and Downclocking](#power-usage-and-downclocking)
* [Free Padding](#free-padding)
NDEBUG directive
NDEBUG macro
-------------
In C/C++, the `NDEBUG` pre-processor directive is not set by default. When it is not set, the simdjson library does
many additional checks that may impact negatively the performance. We recommend that, once your code
is well tested, you define `NDEBUG` directive in your Release builds. The `NDEBUG` directive should be defined
prior to including the `simdjson.h` header.
We recommend that you set `NDEBUG` macro in your release builds.
The `NDEBUG` directive is generally independent from optimization flags. For example, setting `-O3` under
GCC does not set the `NDEBUG` directive.
In C/C++, the `NDEBUG` macro is not set by default.
When it is not set, the software may do many additional checks that may impact
negatively the performance. We recommend that, once your code
is well tested, you define `NDEBUG` directive in your release builds.
The `NDEBUG` directive is generally independent from optimization flags.
For example, setting `-O3` under GCC does not set the `NDEBUG` directive.
However, tools like `CMake` automatically
set `NDEBUG` for release builds.
In the simdjson library, we check the `NDEBUG` macro as well as other
macros to make performant release builds. However, the C++ standard
does not provide a definitive approach to determine whether you are
compiling for a release build. Thus we recommend that you follow
the practice of setting the `NDEBUG` macro in release mode to make sure
that you do not get undesirable expensive checks.
Reusing the parser for maximum efficiency
-----------------------------------------
@@ -74,7 +86,7 @@ or simply
Server Loops: Long-Running Processes and Memory Capacity
---------------------------------
The On Demand approach also automatically expands its memory capacity when larger documents are parsed. However, for longer processes where very large files are processed (such as server loops), this capacity is not resized down. On Demand also lets you adjust the maximal capacity that the parser can process:
The On-Demand approach also automatically expands its memory capacity when larger documents are parsed. However, for longer processes where very large files are processed (such as server loops), this capacity is not resized down. On-Demand also lets you adjust the maximal capacity that the parser can process:
* You can set an upper bound (*max_capacity*) when construction the parser:
```C++
@@ -157,9 +169,9 @@ On Intel and AMD Windows platforms, Microsoft Visual Studio enables programmers
When compiling with Visual Studio, we recommend the flags `/Ob2 /O2` or better. We do not recommend that you compile simdjson with architecture-specific flags such as `arch:AVX2`. The simdjson library automatically selects the best execution kernel at runtime.
Recent versions of Microsoft Visual Studio on Windows provides support for the LLVM Clang compiler. You only need to install the "Clang compiler" optional component (ClangCL). You may also get a copy of the 64-bit LLVM CLang compiler for [Windows directly from LLVM](https://releases.llvm.org/download.html). The simdjson library fully supports the LLVM Clang compiler under Windows. In fact, you may get better performance out of simdjson with the LLVM Clang compiler than with the regular Visual Studio compiler. Meanwhile the [LLVM CLang compiler is binary compatible with Visual Studio](https://clang.llvm.org/docs/MSVCCompatibility.html) which means that you can combine their binaries (executables and libraries).
Recent versions of Microsoft Visual Studio on Windows provides support for the LLVM Clang compiler. You only need to install the "Clang compiler" optional component (clang-cl). You may also get a copy of the 64-bit LLVM CLang compiler for [Windows directly from LLVM](https://releases.llvm.org/download.html). The simdjson library fully supports the LLVM Clang compiler under Windows. In fact, you may get better performance out of simdjson with the LLVM Clang compiler than with the regular Visual Studio compiler. Meanwhile the [LLVM CLang compiler is binary compatible with Visual Studio](https://clang.llvm.org/docs/MSVCCompatibility.html) which means that you can combine their binaries (executables and libraries).
Under Windows, we also support the GNU GCC compiler via MSYS2. The performance of 64-bit MSYS2 under Windows is excellent (on par with Linux).
We recommend Visual Studio users prefer LLVM (clang-cl). It compiles to faster release binaries. Furthermore, it compilers faster in release mode.
Power Usage and Downclocking
@@ -179,3 +191,110 @@ The simdjson library does not generally make use of heavy 256-bit instructions.
the macro `SIMDJSON_AVX512_ALLOWED` to `0` in C++ prior to importing the headers.
You may still be worried about which SIMD instruction set is used by simdjson. Thankfully, [you can always determine and change which architecture-specific implementation is used](implementation-selection.md) by simdjson. Thus even if your CPU supports AVX2, you do not need to use AVX2. You are in control.
Free Padding
-------
For performance reasons, the simdjson library requires that the JSON input contain at least
`simdjson::SIMDJSON_PADDING` bytes at the end of the stream. The value `simdjson::SIMDJSON_PADDING` is
small (e.g., 64 bytes). On modern systems, you can safely read beyond an allocated buffers,
as long as you remain within an allocated page. Pages on modern systems span at least 4 kilobytes,
but can be significantly larger. E.g., Apple systems favour pages spanning 16 kilobytes.
In effect, it means that you can almost always read a few bytes beyond your current buffer---without
allocating extra memory. However, tools such as valgrind or memory sanitizers will flag such behavior as unsafe.
Nevertheless, you can still make sure of this capability in your code if you are an expert
programmer and you are willing to silence sanitizer warnings. The following code provides
a portable example.
The conditional compilation checks for the `_MSC_VER` macro (indicating Microsoft Visual Studio)
and includes platform-specific headers accordingly.
The `page_size()` function determines the default size of a memory page in bytes on the system.
On Windows (when `_WIN32` is defined), it uses `GetSystemInfo()` to retrieve system information and obtain the page size.
On other platforms (non-Windows), it uses `sysconf(_SC_PAGESIZE)` to get the page size.
The function returns the page size.
The `need_allocation()` function checks whether the buffer (given by `buf`) plus the specified length (`len`) is near a page boundary.
If the buffer extends beyond the current page when padded by `simdjson::SIMDJSON_PADDING`, it returns true, indicating that reallocation is needed.
Otherwise, it returns false.
The `get_padded_string_view()` creates a `padded_string_view` from the input buffer.
If reallocation is needed (unlikely case), it allocates a new padded_string and assigns it to `jsonbuffer`.
Otherwise (very likely), it creates a `padded_string_view` directly from the buffer.
The `simdjson::SIMDJSON_PADDING` ensures that there is additional padding for parsing efficiency.
The calling code just needs to provide `jsonbuffer` (an instance of `simdjson::padded_string`)
and pass `get_padded_string_view(buf, len, jsonbuffer)` to `parser.iterate`. Most of the time,
this code will not allocate new memory.
```cpp
#ifdef _WIN32
#include <windows.h>
#include <sysinfoapi.h>
#else
#include <unistd.h>
#endif
#include "simdjson.h"
#include <cstdio>
// Returns the default size of the page in bytes on this system.
long page_size() {
#ifdef _WIN32
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
long pagesize = sysInfo.dwPageSize;
#else
long pagesize = sysconf(_SC_PAGESIZE);
#endif
return pagesize;
}
// Returns true if the buffer + len + simdjson::SIMDJSON_PADDING crosses the
// page boundary.
bool need_allocation(const char *buf, size_t len) {
return ((reinterpret_cast<uintptr_t>(buf + len - 1) % page_size())
+ simdjson::SIMDJSON_PADDING > static_cast<uintptr_t>(page_size()));
}
simdjson::padded_string_view
get_padded_string_view(const char *buf, size_t len,
simdjson::padded_string &jsonbuffer) {
if (need_allocation(buf, len)) { // unlikely case
jsonbuffer = simdjson::padded_string(buf, len);
return jsonbuffer;
} else { // no reallcation needed (very likely)
return simdjson::padded_string_view(buf, len,
len + simdjson::SIMDJSON_PADDING);
}
}
int main() {
printf("page_size: %ld\n", page_size());
const char *jsonpoiner = R"(
{
"key": "value"
}
)";
size_t len = strlen(jsonpoiner);
simdjson::padded_string jsonbuffer; // only allocate if needed
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
simdjson::error_code error =
parser.iterate(get_padded_string_view(jsonpoiner, len, jsonbuffer))
.get(doc);
if (error) {
printf("error: %s\n", simdjson::error_message(error));
return EXIT_FAILURE;
}
std::string_view value;
error = doc["key"].get_string().get(value);
if (error) {
return EXIT_FAILURE;
}
printf("Value: \"%.*s\"\n", (int)value.size(), value.data());
if (value != "value") {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
```
+10 -4
View File
@@ -20,12 +20,16 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
endif()
add_quickstart_test(quickstart_noexceptions quickstart_noexceptions.cpp NO_EXCEPTIONS LABELS acceptance)
add_quickstart_test(quickstart_noexceptions11 quickstart_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11)
if(NOT SIMDJSON_STATIC_REFLECTION)
add_quickstart_test(quickstart_noexceptions11 quickstart_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11)
endif(NOT SIMDJSON_STATIC_REFLECTION)
add_quickstart_test(quickstart2_noexceptions quickstart2_noexceptions.cpp NO_EXCEPTIONS LABELS acceptance)
add_quickstart_test(quickstart2_noexceptions11 quickstart2_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11)
if(NOT SIMDJSON_STATIC_REFLECTION)
add_quickstart_test(quickstart2_noexceptions11 quickstart2_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11)
endif(NOT SIMDJSON_STATIC_REFLECTION)
# On Demand Quick Start
# On-Demand Quick Start
if (SIMDJSON_EXCEPTIONS)
add_quickstart_test(quickstart_ondemand quickstart_ondemand.cpp LABELS quickstart_ondemand acceptance)
add_quickstart_test(quickstart_ondemand11 quickstart_ondemand.cpp CXX_STANDARD c++11 LABELS quickstart_ondemand acceptance)
@@ -33,6 +37,8 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
endif()
add_quickstart_test(quickstart_ondemand_noexceptions quickstart_ondemand_noexceptions.cpp NO_EXCEPTIONS LABELS quickstart_ondemand acceptance)
add_quickstart_test(quickstart_ondemand_noexceptions11 quickstart_ondemand_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11 LABELS quickstart_ondemand)
if(NOT SIMDJSON_STATIC_REFLECTION)
add_quickstart_test(quickstart_ondemand_noexceptions11 quickstart_ondemand_noexceptions.cpp NO_EXCEPTIONS CXX_STANDARD c++11 LABELS quickstart_ondemand)
endif(NOT SIMDJSON_STATIC_REFLECTION)
endif()

Some files were not shown because too many files have changed in this diff Show More