new feature tests, etc.

This commit is contained in:
Daniel Lemire
2026-04-09 14:55:02 -04:00
parent 325db34d41
commit 9b71acf803
32 changed files with 11284 additions and 10287 deletions
+10 -4
View File
@@ -54,11 +54,17 @@ endif()
option(SIMDJSON_FEATURE_ONDEMAND_API "Enable ondemand API" ON)
option(SIMDJSON_FEATURE_DOM_API "Enable DOM API" ON)
option(SIMDJSON_FEATURE_BUILDER_API "Enable builder API" ON)
add_compile_definitions(
SIMDJSON_FEATURE_ONDEMAND_API=$<BOOL:${SIMDJSON_FEATURE_ONDEMAND_API}>
SIMDJSON_FEATURE_DOM_API=$<BOOL:${SIMDJSON_FEATURE_DOM_API}>
)
if(NOT SIMDJSON_FEATURE_ONDEMAND_API)
add_compile_definitions(SIMDJSON_FEATURE_ONDEMAND_API=0)
endif()
if(NOT SIMDJSON_FEATURE_DOM_API)
add_compile_definitions(SIMDJSON_FEATURE_DOM_API=0)
endif()
if(NOT SIMDJSON_FEATURE_BUILDER_API)
add_compile_definitions(SIMDJSON_FEATURE_BUILDER_API=0)
endif()
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.25.0")
option(SIMDJSON_STATIC_REFLECTION "Enables static reflection (experimental), requires C++26" OFF)
+5 -2
View File
@@ -34,11 +34,13 @@ By default the library is built in Release mode.
## Optional APIs (Feature Flags)
simdjson provides two APIs:
simdjson provides three APIs:
- DOM (`simdjson::dom`)
- OnDemand (`simdjson::ondemand`)
- Builder (`simdjson::builder`) — requires DOM
You can enable or disable these APIs at compile time to reduce binary size and compile time.
At least one of DOM or OnDemand must be enabled.
### Configuration
@@ -46,7 +48,8 @@ Using CMake:
```bash
cmake -DSIMDJSON_FEATURE_DOM_API=OFF \
-DSIMDJSON_FEATURE_ONDEMAND_API=ON ..
-DSIMDJSON_FEATURE_ONDEMAND_API=ON \
-DSIMDJSON_FEATURE_BUILDER_API=OFF ..
Assertions and development checks
------------------------------
+3
View File
@@ -46,6 +46,7 @@
#include "simdjson/error.h"
#include "simdjson/error-inl.h"
#include "simdjson/implementation.h"
#include "simdjson/builtin.h"
#include "simdjson/minify.h"
#include "simdjson/padded_string.h"
#include "simdjson/padded_string-inl.h"
@@ -54,7 +55,9 @@
#if SIMDJSON_FEATURE_DOM_API
#include "simdjson/dom.h"
#if SIMDJSON_FEATURE_BUILDER_API
#include "simdjson/builder.h"
#endif // SIMDJSON_FEATURE_BUILDER_API
#endif // SIMDJSON_FEATURE_DOM_API
#if SIMDJSON_FEATURE_ONDEMAND_API
+4
View File
@@ -8,6 +8,10 @@
#define SIMDJSON_FEATURE_ONDEMAND_API 1
#endif
#if !defined(SIMDJSON_FEATURE_BUILDER_API)
#define SIMDJSON_FEATURE_BUILDER_API 1
#endif
#if !SIMDJSON_FEATURE_DOM_API && !SIMDJSON_FEATURE_ONDEMAND_API
#error "simdjson: at least one of SIMDJSON_FEATURE_DOM_API or SIMDJSON_FEATURE_ONDEMAND_API must be enabled."
#endif
@@ -1,8 +1,10 @@
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
#if SIMDJSON_FEATURE_ONDEMAND_API
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_BUILDER_DEPENDENCIES_H)
#if defined(SIMDJSON_CONDITIONAL_INCLUDE) && !defined(SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H)
#error simdjson/generic/ondemand/dependencies.h must be included before simdjson/generic/ondemand/amalgamated.h!
#endif
@@ -8,6 +8,7 @@
// Internal headers needed for ondemand generics.
// All includes not under simdjson/generic/ondemand must be here!
// Otherwise, amalgamation will fail.
#include "simdjson/feature_macros.h"
#include "simdjson/dom/base.h" // for MINIMAL_DOCUMENT_CAPACITY
#include "simdjson/implementation.h"
#include "simdjson/padded_string.h"
+3 -1
View File
@@ -8,12 +8,14 @@ namespace simdjson {
* @copydoc simdjson::builtin::ondemand
*/
namespace ondemand = builtin::ondemand;
#if SIMDJSON_FEATURE_BUILDER_API
/**
* @copydoc simdjson::builtin::builder
*/
namespace builder = builtin::builder;
#endif // SIMDJSON_FEATURE_BUILDER_API
#if SIMDJSON_STATIC_REFLECTION
#if SIMDJSON_STATIC_REFLECTION && SIMDJSON_FEATURE_BUILDER_API
/**
* Create a JSON string from any user-defined type using static reflection.
* Only available when SIMDJSON_STATIC_REFLECTION is enabled.
+226
View File
@@ -0,0 +1,226 @@
#!/usr/bin/env python3
"""
Amalgamates simdjson into temporary files, then builds and runs 7 feature-flag
test programs. Reports compile time and binary size for each configuration.
"""
import os
import sys
import subprocess
import tempfile
import time
import shutil
SCRIPTPATH = os.path.dirname(os.path.abspath(__file__))
PROJECTPATH = os.path.dirname(SCRIPTPATH)
TESTSRC = os.path.join(PROJECTPATH, "tests", "feature_flag_tests")
# ── feature-flag configurations ──────────────────────────────────────────────
CONFIGS = [
{
"name": "dom_only",
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
"SIMDJSON_FEATURE_ONDEMAND_API": 0,
"SIMDJSON_FEATURE_BUILDER_API": 0},
},
{
"name": "ondemand_only",
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
"SIMDJSON_FEATURE_ONDEMAND_API": 1,
"SIMDJSON_FEATURE_BUILDER_API": 0},
},
{
"name": "dom_and_ondemand",
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
"SIMDJSON_FEATURE_ONDEMAND_API": 1,
"SIMDJSON_FEATURE_BUILDER_API": 0},
},
{
"name": "builder_and_dom",
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
"SIMDJSON_FEATURE_ONDEMAND_API": 0,
"SIMDJSON_FEATURE_BUILDER_API": 1},
},
{
"name": "builder_and_ondemand",
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
"SIMDJSON_FEATURE_ONDEMAND_API": 1,
"SIMDJSON_FEATURE_BUILDER_API": 1},
},
{
"name": "builder_dom_and_ondemand",
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
"SIMDJSON_FEATURE_ONDEMAND_API": 1,
"SIMDJSON_FEATURE_BUILDER_API": 1},
},
{
"name": "builder_only",
"flags": {"SIMDJSON_FEATURE_DOM_API": 1,
"SIMDJSON_FEATURE_ONDEMAND_API": 0,
"SIMDJSON_FEATURE_BUILDER_API": 1},
},
]
CXX = os.environ.get("CXX", "c++")
CXXSTD = os.environ.get("CXXSTD", "-std=c++17")
OPT = os.environ.get("OPT", "-O2")
def amalgamate(output_dir):
"""Run amalgamate.py, writing simdjson.h / simdjson.cpp into *output_dir*."""
env = os.environ.copy()
env["AMALGAMATE_OUTPUT_PATH"] = output_dir
cmd = [sys.executable, os.path.join(SCRIPTPATH, "amalgamate.py")]
result = subprocess.run(cmd, env=env, capture_output=True, text=True)
if result.returncode != 0:
print("amalgamate.py failed:")
print(result.stdout)
print(result.stderr)
sys.exit(1)
header = os.path.join(output_dir, "simdjson.h")
source = os.path.join(output_dir, "simdjson.cpp")
assert os.path.isfile(header), f"missing {header}"
assert os.path.isfile(source), f"missing {source}"
return header, source
def compile_and_run(name, flags, amal_dir, build_dir):
"""
1. Compile simdjson.cpp -> simdjson.o (timed, size reported)
2. Compile test .cpp -> test.o (timed, size reported)
3. Link and run the test program
Returns (lib_time, lib_size, test_time, test_size) or Nones on failure.
"""
src = os.path.join(TESTSRC, f"{name}.cpp")
simdjson_cpp = os.path.join(amal_dir, "simdjson.cpp")
simdjson_o = os.path.join(build_dir, f"{name}_simdjson.o")
test_o = os.path.join(build_dir, f"{name}.o")
binary = os.path.join(build_dir, name)
defs = [f"-D{k}={v}" for k, v in flags.items()]
# ── compile simdjson.o (timed) ────────────────────────────────────────
cmd_lib = [CXX, CXXSTD, OPT, "-c"] + defs + [
f"-I{amal_dir}", "-o", simdjson_o, simdjson_cpp,
]
t0 = time.monotonic()
result = subprocess.run(cmd_lib, capture_output=True, text=True)
lib_time = time.monotonic() - t0
if result.returncode != 0:
print(f" COMPILE FAILED (simdjson.o): {name}")
print(f" command: {' '.join(cmd_lib)}")
print(result.stderr)
return None, None, None, None
lib_size = os.path.getsize(simdjson_o)
# ── compile test source (timed) ───────────────────────────────────────
cmd_test = [CXX, CXXSTD, OPT, "-c"] + defs + [
f"-I{amal_dir}", "-o", test_o, src,
]
t0 = time.monotonic()
result = subprocess.run(cmd_test, capture_output=True, text=True)
test_time = time.monotonic() - t0
if result.returncode != 0:
print(f" COMPILE FAILED (test): {name}")
print(f" command: {' '.join(cmd_test)}")
print(result.stderr)
return None, None, None, None
test_size = os.path.getsize(test_o)
# ── link ──────────────────────────────────────────────────────────────
cmd_link = [CXX, "-o", binary, test_o, simdjson_o]
result = subprocess.run(cmd_link, capture_output=True, text=True)
if result.returncode != 0:
print(f" LINK FAILED: {name}")
print(f" command: {' '.join(cmd_link)}")
print(result.stderr)
return None, None, None, None
# ── run ───────────────────────────────────────────────────────────────
result = subprocess.run([binary], capture_output=True, text=True)
if result.returncode != 0:
print(f" RUN FAILED: {name}")
print(result.stdout)
print(result.stderr)
return lib_time, lib_size, test_time, test_size
print(f" {result.stdout.strip()}")
return lib_time, lib_size, test_time, test_size
def human_size(n):
"""Return e.g. '1.23 MB' or '456 KB'."""
if n is None:
return "N/A"
if n >= 1_000_000:
return f"{n / 1_000_000:.2f} MB"
if n >= 1_000:
return f"{n / 1_000:.1f} KB"
return f"{n} B"
def main():
tmpdir = tempfile.mkdtemp(prefix="simdjson_features_")
amal_dir = os.path.join(tmpdir, "amalgamated")
build_dir = os.path.join(tmpdir, "build")
os.makedirs(amal_dir, exist_ok=True)
os.makedirs(build_dir, exist_ok=True)
print(f"Working directory: {tmpdir}")
print(f"Compiler: {CXX} flags: {CXXSTD} {OPT}")
print()
# ── amalgamate ────────────────────────────────────────────────────────
print("Amalgamating...")
amalgamate(amal_dir)
print("Amalgamation done.\n")
# ── build & run each configuration ────────────────────────────────────
results = []
for cfg in CONFIGS:
name = cfg["name"]
flags = cfg["flags"]
enabled = [k.replace("SIMDJSON_FEATURE_", "").replace("_API", "")
for k, v in flags.items() if v]
label = " + ".join(enabled) if enabled else "none"
print(f"[{name}] ({label})")
lib_time, lib_size, test_time, test_size = \
compile_and_run(name, flags, amal_dir, build_dir)
results.append((name, label, lib_time, lib_size, test_time, test_size))
print()
# ── summary table ─────────────────────────────────────────────────────
name_w = max(len(r[0]) for r in results)
label_w = max(len(r[1]) for r in results)
hdr = (f"{'Test':<{name_w}} {'APIs':<{label_w}}"
f" {'lib time':>9} {'simdjson.o':>11}"
f" {'test time':>10} {'test .o':>11}")
print(hdr)
print("-" * len(hdr))
for name, label, lt, ls, tt, ts in results:
lt_s = f"{lt:.2f} s" if lt is not None else "FAIL"
ls_s = human_size(ls)
tt_s = f"{tt:.2f} s" if tt is not None else "FAIL"
ts_s = human_size(ts)
print(f"{name:<{name_w}} {label:<{label_w}}"
f" {lt_s:>9} {ls_s:>11}"
f" {tt_s:>10} {ts_s:>11}")
# ── cleanup ───────────────────────────────────────────────────────────
shutil.rmtree(tmpdir, ignore_errors=True)
# exit non-zero if anything failed
if any(lt is None for _, _, lt, _, _, _ in results):
print("\nSome tests FAILED.")
sys.exit(1)
print("\nAll tests passed.")
if __name__ == "__main__":
main()
+54 -3
View File
@@ -268,7 +268,21 @@ class SimdjsonFile:
print(f" Adding include: {self} includes {include}")
if self.is_conditional_include:
# If I have a dependency file, I can only include something that has a dependency file.
assert include.is_conditional_include, f"Error: Amalgamated file '{self}' is trying to include '{include}', but '{include}' is not an amalgamated file. Amalgamated files can only include other amalgamated files to maintain conditional inclusion structure. Check the inclusion rules in the script's 'rules' variable. {rules}"
if not include.is_conditional_include:
dep = self.dependency_file
dep_hint = f" and add it to the dependency file '{dep}'" if dep else ""
raise AssertionError(
f"Error: Amalgamated file '{self}' is trying to include '{include}', "
f"but '{include}' is not an amalgamated file.\n\n"
f"FIX: Wrap the #include \"{include}\" in a conditional block:\n\n"
f" #ifndef SIMDJSON_CONDITIONAL_INCLUDE\n"
f" #include \"{include}\"\n"
f" #endif // SIMDJSON_CONDITIONAL_INCLUDE\n\n"
f"This makes the include editor-only (skipped during amalgamation){dep_hint}.\n\n"
f"During amalgamation, '{include}' is already included earlier in the "
f"amalgamated output, so it does not need to be included again.\n\n"
f"{rules}"
)
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
# if include.amalgamator_file:
# assert include.amalgamator_file == self, f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
@@ -425,8 +439,10 @@ class Amalgamator:
self.implementation = "SIMDJSON_BUILTIN_IMPLEMENTATION"
assert not self.editor_only_region, f"Error: Already in an editor-only region when starting to write '{file}'. Ensure proper nesting of conditional blocks."
editor_only_start_line = None
bare_endif_lines = []
with open(file.absolute_path, 'r') as fid2:
for line in fid2:
for line_number, line in enumerate(fid2, 1):
line = line.rstrip('\n')
# Ignore #pragma once, it causes warnings if it ends up in a .cpp file
@@ -439,6 +455,7 @@ class Amalgamator:
assert self.in_conditional_include_block, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' without a prior '#define SIMDJSON_CONDITIONAL_INCLUDE'. Ensure the define comes first. Stack: {self.include_stack}. {rules}"
assert not self.editor_only_region, f"Error: File '{file}' uses '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' twice in a row. Ensure conditional blocks are properly nested and closed. {rules}"
self.editor_only_region = True
editor_only_start_line = line_number
# Handle ignored lines (and ending ignore blocks)
end_ignore = endif_conditional_re.search(line)
@@ -453,6 +470,12 @@ class Amalgamator:
file.add_editor_only_include(included_file)
if end_ignore:
self.editor_only_region = False
editor_only_start_line = None
else:
# Track bare #endif lines that might be the intended closer
stripped = line.strip()
if stripped == '#endif' or (stripped.startswith('#endif') and 'SIMDJSON_CONDITIONAL_INCLUDE' not in stripped):
bare_endif_lines.append((line_number, line.strip()))
continue
assert not end_ignore, f"Error: File '{file}' has '#endif // SIMDJSON_CONDITIONAL_INCLUDE' without a matching '#ifndef'. Ensure proper conditional block structure. {rules}"
@@ -501,7 +524,35 @@ class Amalgamator:
self.write(line)
assert not self.editor_only_region, f"Error: File '{file}' ended without closing the '#endif // SIMDJSON_CONDITIONAL_INCLUDE'. Ensure all conditional blocks are properly closed. {rules}"
if self.editor_only_region:
msg = (
f"Error: File '{file}' ended without closing the "
f"'#endif // SIMDJSON_CONDITIONAL_INCLUDE' block "
f"(opened at line {editor_only_start_line}).\n\n"
)
if bare_endif_lines:
msg += (
f"HINT: Found #endif line(s) inside the block that are missing "
f"the required comment. The amalgamation script looks for exactly:\n\n"
f" #endif // SIMDJSON_CONDITIONAL_INCLUDE\n\n"
f"but found:\n"
)
for ln, text in bare_endif_lines:
msg += f" line {ln}: {text}\n"
msg += (
f"\nFIX: Change the #endif to:\n\n"
f" #endif // SIMDJSON_CONDITIONAL_INCLUDE\n\n"
f"The '// SIMDJSON_CONDITIONAL_INCLUDE' comment is required "
f"for the amalgamation script to recognize it as the closing "
f"of the conditional block.\n"
)
else:
msg += (
f"FIX: Add '#endif // SIMDJSON_CONDITIONAL_INCLUDE' to close "
f"the '#ifndef SIMDJSON_CONDITIONAL_INCLUDE' block.\n"
)
msg += f"\n{rules}"
raise AssertionError(msg)
self.write(f"/* end file {self.file_to_str(file)} */")
+2121 -2010
View File
File diff suppressed because it is too large Load Diff
+8515 -8257
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
+5
View File
@@ -2,6 +2,7 @@
#define SIMDJSON_SRC_ARM64_CPP
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -142,6 +143,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
return arm64::stage1::generic_validate_utf8(buf,len);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<false>(*this, _doc);
}
@@ -149,6 +151,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<true>(*this, _doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
@@ -159,11 +162,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
return arm64::stringparsing::parse_wobbly_string(src, dst);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
auto error = stage1(_buf, _len, stage1_mode::regular);
if (error) { return error; }
return stage2(_doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
} // namespace arm64
} // namespace simdjson
+1 -2
View File
@@ -1,9 +1,8 @@
#ifndef SIMDJSON_SRC_FALLBACK_CPP
#define SIMDJSON_SRC_FALLBACK_CPP
#include "simdjson/feature_macros.h"
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
+1
View File
@@ -5,6 +5,7 @@
#ifndef SIMDJSON_SRC_GENERIC_DEPENDENCIES_H
#define SIMDJSON_SRC_GENERIC_DEPENDENCIES_H
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_SRC_GENERIC_DEPENDENCIES_H
+1 -2
View File
@@ -1,9 +1,8 @@
#ifndef SIMDJSON_SRC_HASWELL_CPP
#define SIMDJSON_SRC_HASWELL_CPP
#include "simdjson/feature_macros.h"
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
+1 -2
View File
@@ -1,9 +1,8 @@
#ifndef SIMDJSON_SRC_ICELAKE_CPP
#define SIMDJSON_SRC_ICELAKE_CPP
#include "simdjson/feature_macros.h"
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
+5
View File
@@ -2,6 +2,7 @@
#define SIMDJSON_SRC_LASX_CPP
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -102,6 +103,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
return lasx::stage1::generic_validate_utf8(buf,len);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<false>(*this, _doc);
}
@@ -109,6 +111,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<true>(*this, _doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
@@ -119,11 +122,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
return lasx::stringparsing::parse_wobbly_string(src, dst);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
auto error = stage1(_buf, _len, stage1_mode::regular);
if (error) { return error; }
return stage2(_doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
} // namespace lasx
} // namespace simdjson
+5
View File
@@ -2,6 +2,7 @@
#define SIMDJSON_SRC_LSX_CPP
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -106,6 +107,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
return lsx::stage1::generic_validate_utf8(buf,len);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<false>(*this, _doc);
}
@@ -113,6 +115,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<true>(*this, _doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
@@ -123,11 +126,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
return lsx::stringparsing::parse_wobbly_string(src, dst);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
auto error = stage1(_buf, _len, stage1_mode::regular);
if (error) { return error; }
return stage2(_doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
} // namespace lsx
} // namespace simdjson
+5
View File
@@ -2,6 +2,7 @@
#define SIMDJSON_SRC_PPC64_CPP
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -112,6 +113,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
return ppc64::stage1::generic_validate_utf8(buf,len);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<false>(*this, _doc);
}
@@ -119,6 +121,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<true>(*this, _doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept {
@@ -129,11 +132,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
return ppc64::stringparsing::parse_wobbly_string(src, dst);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
auto error = stage1(_buf, _len, stage1_mode::regular);
if (error) { return error; }
return stage2(_doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
} // namespace ppc64
} // namespace simdjson
+5
View File
@@ -2,6 +2,7 @@
#define SIMDJSON_SRC_RVV_VLS_CPP
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
@@ -101,6 +102,7 @@ simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t
return rvv_vls::stage1::generic_validate_utf8(buf,len);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<false>(*this, _doc);
}
@@ -108,6 +110,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document
simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept {
return stage2::tape_builder::parse_document<true>(*this, _doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
SIMDJSON_NO_SANITIZE_MEMORY
simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept {
@@ -118,11 +121,13 @@ simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(con
return rvv_vls::stringparsing::parse_wobbly_string(src, dst);
}
#if SIMDJSON_FEATURE_DOM_API
simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept {
auto error = stage1(_buf, _len, stage1_mode::regular);
if (error) { return error; }
return stage2(_doc);
}
#endif // SIMDJSON_FEATURE_DOM_API
} // namespace rvv_vls
} // namespace simdjson
+1 -2
View File
@@ -1,9 +1,8 @@
#ifndef SIMDJSON_SRC_WESTMERE_CPP
#define SIMDJSON_SRC_WESTMERE_CPP
#include "simdjson/feature_macros.h"
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#include "simdjson/feature_macros.h"
#include <base.h>
#endif // SIMDJSON_CONDITIONAL_INCLUDE
+2 -1
View File
@@ -29,4 +29,5 @@ add_cpp_test(checkimplementation LABELS other per_implementation)
add_subdirectory(compilation_failure_tests)
add_subdirectory(builder)
add_subdirectory(compile_time)
add_subdirectory(compile_time)
add_subdirectory(feature_flag_tests)
+3
View File
@@ -1,5 +1,8 @@
# All remaining tests link with simdjson proper
include_directories(..)
if(NOT SIMDJSON_FEATURE_BUILDER_API)
return()
endif()
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
if(SIMDJSON_STATIC_REFLECTION)
add_cpp_test(static_reflection_custom_tests LABELS ondemand acceptance per_implementation)
+36
View File
@@ -0,0 +1,36 @@
# Feature flag tests: compile and run against the amalgamated single-header file.
# Each test uses target_compile_definitions to set its own feature macros,
# independent of the project-wide settings.
set(SINGLEHEADER_DIR ${PROJECT_SOURCE_DIR}/singleheader)
function(add_feature_flag_test TEST_NAME)
cmake_parse_arguments(PARSE_ARGV 1 ARG "" "" "DEFS")
add_executable(${TEST_NAME} ${TEST_NAME}.cpp ${SINGLEHEADER_DIR}/simdjson.cpp)
target_include_directories(${TEST_NAME} PRIVATE ${SINGLEHEADER_DIR})
target_compile_features(${TEST_NAME} PRIVATE cxx_std_17)
# Override any inherited feature flag definitions with our own.
target_compile_definitions(${TEST_NAME} PRIVATE ${ARG_DEFS})
# Remove inherited link libraries (simdjson-internal-flags brings -Werror
# which conflicts with the amalgamated header's warning profile).
set_target_properties(${TEST_NAME} PROPERTIES LINK_LIBRARIES "")
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
set_property(TEST ${TEST_NAME} APPEND PROPERTY LABELS feature_flags acceptance)
endfunction()
add_feature_flag_test(dom_only DEFS
SIMDJSON_FEATURE_DOM_API=1 SIMDJSON_FEATURE_ONDEMAND_API=0 SIMDJSON_FEATURE_BUILDER_API=0)
# Note: OnDemand tests also enable DOM because the builtin infrastructure
# is currently included via the DOM/builtin path in the amalgamated header.
add_feature_flag_test(ondemand_only DEFS
SIMDJSON_FEATURE_DOM_API=1 SIMDJSON_FEATURE_ONDEMAND_API=1 SIMDJSON_FEATURE_BUILDER_API=0)
add_feature_flag_test(dom_and_ondemand DEFS
SIMDJSON_FEATURE_DOM_API=1 SIMDJSON_FEATURE_ONDEMAND_API=1 SIMDJSON_FEATURE_BUILDER_API=0)
add_feature_flag_test(builder_and_dom DEFS
SIMDJSON_FEATURE_DOM_API=1 SIMDJSON_FEATURE_ONDEMAND_API=0 SIMDJSON_FEATURE_BUILDER_API=1)
add_feature_flag_test(builder_and_ondemand DEFS
SIMDJSON_FEATURE_DOM_API=1 SIMDJSON_FEATURE_ONDEMAND_API=1 SIMDJSON_FEATURE_BUILDER_API=1)
add_feature_flag_test(builder_dom_and_ondemand DEFS
SIMDJSON_FEATURE_DOM_API=1 SIMDJSON_FEATURE_ONDEMAND_API=1 SIMDJSON_FEATURE_BUILDER_API=1)
add_feature_flag_test(builder_only DEFS
SIMDJSON_FEATURE_DOM_API=1 SIMDJSON_FEATURE_ONDEMAND_API=0 SIMDJSON_FEATURE_BUILDER_API=1)
@@ -0,0 +1,42 @@
// Test: Builder + DOM APIs (OnDemand disabled)
// Feature flags set via target_compile_definitions in CMakeLists.txt
#include "simdjson.h"
#include <iostream>
int main() {
auto json = R"({"key": "value"})"_padded;
// Use DOM API
simdjson::dom::parser parser;
simdjson::dom::element doc;
auto error = parser.parse(json).get(doc);
if (error) {
std::cerr << "DOM parse error: " << error << std::endl;
return EXIT_FAILURE;
}
std::string_view val;
error = doc["key"].get_string().get(val);
if (error || val != "value") {
std::cerr << "DOM key lookup failed" << std::endl;
return EXIT_FAILURE;
}
// Use Builder API
simdjson::builder::string_builder sb;
sb.start_object();
sb.append_key_value("greeting", "hello");
sb.end_object();
std::string_view result;
error = sb.view().get(result);
if (error) {
std::cerr << "Builder error: " << error << std::endl;
return EXIT_FAILURE;
}
if (result != R"({"greeting":"hello"})") {
std::cerr << "Builder output mismatch: " << result << std::endl;
return EXIT_FAILURE;
}
std::cout << "Builder+DOM test passed." << std::endl;
return EXIT_SUCCESS;
}
@@ -0,0 +1,45 @@
// Test: Builder + OnDemand APIs (DOM also enabled since Builder depends on it)
// Feature flags set via target_compile_definitions in CMakeLists.txt
#include "simdjson.h"
#include <iostream>
int main() {
// Use OnDemand API
auto json = R"({"key": "value"})"_padded;
simdjson::ondemand::parser parser;
simdjson::ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if (error) {
std::cerr << "OnDemand parse error: " << error << std::endl;
return EXIT_FAILURE;
}
std::string_view val;
error = doc["key"].get_string().get(val);
if (error || val != "value") {
std::cerr << "OnDemand key lookup failed" << std::endl;
return EXIT_FAILURE;
}
// Use Builder API
simdjson::builder::string_builder sb;
sb.start_array();
sb.append(1);
sb.append_comma();
sb.append(2);
sb.append_comma();
sb.append(3);
sb.end_array();
std::string_view result;
error = sb.view().get(result);
if (error) {
std::cerr << "Builder error: " << error << std::endl;
return EXIT_FAILURE;
}
if (result != "[1,2,3]") {
std::cerr << "Builder output mismatch: " << result << std::endl;
return EXIT_FAILURE;
}
std::cout << "Builder+OnDemand test passed." << std::endl;
return EXIT_SUCCESS;
}
@@ -0,0 +1,59 @@
// Test: All three APIs enabled (Builder + DOM + OnDemand)
// Feature flags set via target_compile_definitions in CMakeLists.txt
#include "simdjson.h"
#include <iostream>
int main() {
auto json = R"({"name": "simdjson", "version": 5})"_padded;
// Use DOM API
simdjson::dom::parser dom_parser;
simdjson::dom::element dom_doc;
auto error = dom_parser.parse(json).get(dom_doc);
if (error) {
std::cerr << "DOM parse error: " << error << std::endl;
return EXIT_FAILURE;
}
std::string_view name;
error = dom_doc["name"].get_string().get(name);
if (error || name != "simdjson") {
std::cerr << "DOM name lookup failed" << std::endl;
return EXIT_FAILURE;
}
// Use OnDemand API
simdjson::ondemand::parser od_parser;
simdjson::ondemand::document od_doc;
error = od_parser.iterate(json).get(od_doc);
if (error) {
std::cerr << "OnDemand parse error: " << error << std::endl;
return EXIT_FAILURE;
}
std::string_view od_name;
error = od_doc["name"].get_string().get(od_name);
if (error || od_name != "simdjson") {
std::cerr << "OnDemand name lookup failed" << std::endl;
return EXIT_FAILURE;
}
// Use Builder API
simdjson::builder::string_builder sb;
sb.start_object();
sb.append_key_value("name", "simdjson");
sb.append_comma();
sb.append_key_value("version", 5);
sb.end_object();
std::string_view result;
error = sb.view().get(result);
if (error) {
std::cerr << "Builder error: " << error << std::endl;
return EXIT_FAILURE;
}
if (result != R"({"name":"simdjson","version":5})") {
std::cerr << "Builder output mismatch: " << result << std::endl;
return EXIT_FAILURE;
}
std::cout << "Builder+DOM+OnDemand test passed." << std::endl;
return EXIT_SUCCESS;
}
+27
View File
@@ -0,0 +1,27 @@
// Test: Builder API only (OnDemand disabled, DOM enabled since Builder depends on it)
// Feature flags set via target_compile_definitions in CMakeLists.txt
#include "simdjson.h"
#include <iostream>
int main() {
// Use Builder API
simdjson::builder::string_builder sb;
sb.start_object();
sb.append_key_value("count", 2);
sb.append_comma();
sb.append_key_value("name", "test");
sb.end_object();
std::string_view result;
auto error = sb.view().get(result);
if (error) {
std::cerr << "Builder error: " << error << std::endl;
return EXIT_FAILURE;
}
if (result != R"({"count":2,"name":"test"})") {
std::cerr << "Builder output mismatch: " << result << std::endl;
return EXIT_FAILURE;
}
std::cout << "Builder-only test passed." << std::endl;
return EXIT_SUCCESS;
}
@@ -0,0 +1,41 @@
// Test: Both DOM and OnDemand APIs (Builder disabled)
// Feature flags set via target_compile_definitions in CMakeLists.txt
#include "simdjson.h"
#include <iostream>
int main() {
auto json = R"({"key": "value", "number": 42})"_padded;
// Use DOM API
simdjson::dom::parser dom_parser;
simdjson::dom::element dom_doc;
auto error = dom_parser.parse(json).get(dom_doc);
if (error) {
std::cerr << "DOM parse error: " << error << std::endl;
return EXIT_FAILURE;
}
std::string_view dom_val;
error = dom_doc["key"].get_string().get(dom_val);
if (error || dom_val != "value") {
std::cerr << "DOM key lookup failed" << std::endl;
return EXIT_FAILURE;
}
// Use OnDemand API
simdjson::ondemand::parser od_parser;
simdjson::ondemand::document od_doc;
error = od_parser.iterate(json).get(od_doc);
if (error) {
std::cerr << "OnDemand parse error: " << error << std::endl;
return EXIT_FAILURE;
}
std::string_view od_val;
error = od_doc["key"].get_string().get(od_val);
if (error || od_val != "value") {
std::cerr << "OnDemand key lookup failed" << std::endl;
return EXIT_FAILURE;
}
std::cout << "DOM+OnDemand test passed." << std::endl;
return EXIT_SUCCESS;
}
+29
View File
@@ -0,0 +1,29 @@
// Test: DOM API only (OnDemand and Builder disabled)
// Feature flags set via target_compile_definitions in CMakeLists.txt
#include "simdjson.h"
#include <iostream>
int main() {
simdjson::dom::parser parser;
auto json = R"({"key": "value", "number": 42})"_padded;
simdjson::dom::element doc;
auto error = parser.parse(json).get(doc);
if (error) {
std::cerr << "DOM parse error: " << error << std::endl;
return EXIT_FAILURE;
}
std::string_view val;
error = doc["key"].get_string().get(val);
if (error || val != "value") {
std::cerr << "DOM key lookup failed" << std::endl;
return EXIT_FAILURE;
}
int64_t num;
error = doc["number"].get_int64().get(num);
if (error || num != 42) {
std::cerr << "DOM number lookup failed" << std::endl;
return EXIT_FAILURE;
}
std::cout << "DOM-only test passed." << std::endl;
return EXIT_SUCCESS;
}
@@ -0,0 +1,25 @@
// Test: OnDemand API only (Builder disabled)
// Note: DOM must remain enabled because the builtin infrastructure
// (SIMDJSON_BUILTIN_IMPLEMENTATION_IS) is currently included via the DOM path.
// Feature flags set via target_compile_definitions in CMakeLists.txt
#include "simdjson.h"
#include <iostream>
int main() {
simdjson::ondemand::parser parser;
auto json = R"({"key": "value", "number": 42})"_padded;
simdjson::ondemand::document doc;
auto error = parser.iterate(json).get(doc);
if (error) {
std::cerr << "OnDemand parse error: " << error << std::endl;
return EXIT_FAILURE;
}
std::string_view val;
error = doc["key"].get_string().get(val);
if (error || val != "value") {
std::cerr << "OnDemand key lookup failed" << std::endl;
return EXIT_FAILURE;
}
std::cout << "OnDemand-only test passed." << std::endl;
return EXIT_SUCCESS;
}