mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55f698c97b | |||
| 7e2a1d7651 | |||
| 3e00a431a0 | |||
| d3626c6a37 | |||
| 686a1869c8 | |||
| 4c0f86db44 | |||
| c4058d9d22 | |||
| 6c4c934457 | |||
| fcae795042 | |||
| b7c51156b9 | |||
| 662e3d96c7 | |||
| 64009f7063 | |||
| 74fb3ecac7 | |||
| 26abf1d180 | |||
| e878dc6be3 | |||
| efa03f5733 | |||
| 8519623257 | |||
| 484a092c31 | |||
| 54af3feca2 | |||
| 8bb520adbf | |||
| ec8e6a6758 | |||
| 769528b6c0 | |||
| 9fbc577be7 | |||
| a7c95e9cc8 | |||
| 248d4eb2aa | |||
| 559493e2bf | |||
| 75acae5c46 | |||
| bd761ef573 | |||
| 76ed73f07f | |||
| 1b59b38de8 | |||
| 4794b5d936 | |||
| 114f14924c | |||
| e94825c9a8 | |||
| 333fd72f98 | |||
| d43fb6ff84 | |||
| 5eec29a6db | |||
| 228501f786 | |||
| 9d9f2427c5 | |||
| 8d53840253 | |||
| 0a82fb110f | |||
| 11f273c580 | |||
| 31662531ac |
+21
-1
@@ -49,6 +49,27 @@ objs
|
||||
|
||||
# C++ ignore from https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
|
||||
|
||||
# CMake build artifacts
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
CPackConfig.cmake
|
||||
CPackSourceConfig.cmake
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
simdjson-config*.cmake
|
||||
simdjson-props.cmake
|
||||
simdjson.pc
|
||||
|
||||
# Build directories
|
||||
deps/
|
||||
examples/build_*/
|
||||
examples/*_demo
|
||||
examples/*_benchmark
|
||||
|
||||
# Temporary files
|
||||
examples/CMakeLists_demo.txt
|
||||
examples/simple_http.h
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
@@ -88,7 +109,6 @@ objs
|
||||
# Build outputs
|
||||
/build*/
|
||||
/visual_studio/
|
||||
/originbuild/
|
||||
|
||||
# Fuzzer outputs generated by instructions in fuzz/Fuzzing.md
|
||||
/corpus.zip
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Implementation selection
|
||||
#
|
||||
set(SIMDJSON_ALL_IMPLEMENTATIONS fallback westmere haswell icelake arm64 ppc64 rvv)
|
||||
set(SIMDJSON_ALL_IMPLEMENTATIONS fallback westmere haswell icelake arm64 ppc64)
|
||||
|
||||
set(
|
||||
SIMDJSON_IMPLEMENTATION ""
|
||||
@@ -15,7 +15,7 @@ set(
|
||||
SIMDJSON_EXCLUDE_IMPLEMENTATION ""
|
||||
CACHE STRING "\
|
||||
Semicolon-separated list of implementations to exclude \
|
||||
(icelake/haswell/westmere/arm64/ppc64/rvv/fallback). By default, excludes any \
|
||||
(icelake/haswell/westmere/arm64/ppc64/fallback). By default, excludes any \
|
||||
implementations that are unsupported at compile time or cannot be selected at \
|
||||
runtime."
|
||||
)
|
||||
|
||||
@@ -1346,6 +1346,34 @@ auto tag_invoke(deserialize_tag, simdjson_value &val, std::list<Car>& car) {
|
||||
With this code, deserializing an `std::list<Car>` instance would capture only the cars
|
||||
that are not made by Toyota.
|
||||
|
||||
|
||||
|
||||
For even more convenience, you can do it directly without a parser instance like so:
|
||||
|
||||
```cpp
|
||||
Car car = simdjson::from(json);
|
||||
```
|
||||
|
||||
You can also use C++20 ranges to iterate over an array:
|
||||
|
||||
```cpp
|
||||
simdjson::padded_string json_cars =
|
||||
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;
|
||||
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 3. Using static reflection (C++26)
|
||||
|
||||
If you have a C++26 compatible compiler, you can compile
|
||||
@@ -1367,6 +1395,31 @@ simdjson::ondemand::document doc = parser.iterate(simdjson::pad(json));
|
||||
Car c = doc.get<Car>();
|
||||
```
|
||||
|
||||
Just like when using `tag_invoke` for custom types (but without the `tag_invoke` code), you can parse a class instance directly without a parser instance:
|
||||
|
||||
```cpp
|
||||
Car car = simdjson::from(json);
|
||||
```
|
||||
|
||||
Similarly, you can also use C++20 ranges to iterate over an array:
|
||||
|
||||
```cpp
|
||||
simdjson::padded_string json_cars =
|
||||
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;
|
||||
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also automatically serialize the `Car` instance to a JSON string, see
|
||||
our [Builder documentation](builder.md).
|
||||
|
||||
|
||||
+4
-4
@@ -165,7 +165,7 @@ automatically. In most cases, it should work automatically:
|
||||
|
||||
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::builder::to_json_string` function.
|
||||
with the `simdjson::to_json` template function.
|
||||
(Under the hood a `string_builder` instance may still be created.)
|
||||
|
||||
```cpp
|
||||
@@ -178,12 +178,12 @@ with the `simdjson::builder::to_json_string` function.
|
||||
|
||||
void f() {
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
std::string json = simdjson::builder::to_json_string(c);
|
||||
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::builder::to_json_string(c, 31123)`).
|
||||
pass it as a second parameter (e.g., `simdjson::to_json(c, 31123)`).
|
||||
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ pattern:
|
||||
|
||||
```cpp
|
||||
std::string json;
|
||||
if(simdjson::builder::to_json_string(c).get(json)) {
|
||||
if(simdjson::to(c).get(json)) {
|
||||
// there was an error
|
||||
} else {
|
||||
// json contain the serialized JSON
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
# 🛠️ Build Instructions for simdjson One-Liner Demo
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### For Both Examples
|
||||
- **cpr library** (for HTTP requests) - See [INSTALL_CPR.md](INSTALL_CPR.md) for installation
|
||||
- **curl library** (cpr dependency) - Usually pre-installed on most systems
|
||||
- **simdjson library** - Included in this repository
|
||||
|
||||
### For Legacy Example (github_legacy.cpp)
|
||||
- Any C++20 compatible compiler (GCC 10+, Clang 10+, MSVC 2019+)
|
||||
|
||||
### For Modern Example (github_modern.cpp)
|
||||
- Bloomberg Clang fork with C++26 reflection support
|
||||
- Get it from: https://github.com/bloomberg/clang-p2996
|
||||
|
||||
## 🔨 Compilation Commands
|
||||
|
||||
### Quick Setup (Recommended)
|
||||
|
||||
1. **Build cpr from source** (if not installed system-wide):
|
||||
```bash
|
||||
# From the examples directory
|
||||
mkdir -p ../deps && cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
```
|
||||
|
||||
2. **Compile the examples**:
|
||||
```bash
|
||||
# Legacy approach (any C++20 compiler)
|
||||
clang++ -std=c++20 \
|
||||
-I../deps/cpr/include \
|
||||
-I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. \
|
||||
github_legacy.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a \
|
||||
-lcurl -pthread \
|
||||
-o github_legacy_demo
|
||||
|
||||
# Modern approach (Bloomberg clang with reflection)
|
||||
clang++ -std=c++26 -freflection \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include \
|
||||
-I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. \
|
||||
github_modern.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a \
|
||||
-lcurl -pthread \
|
||||
-o github_modern_demo
|
||||
```
|
||||
|
||||
### Using System-Installed cpr
|
||||
|
||||
If cpr is installed system-wide (see [INSTALL_CPR.md](INSTALL_CPR.md)):
|
||||
|
||||
```bash
|
||||
# Legacy approach
|
||||
clang++ -std=c++20 \
|
||||
-I../include -I.. \
|
||||
github_legacy.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_legacy_demo
|
||||
|
||||
# Modern approach
|
||||
clang++ -std=c++26 -freflection \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../include -I.. \
|
||||
github_modern.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_modern_demo
|
||||
```
|
||||
|
||||
### Using simdjson Single Header
|
||||
|
||||
```bash
|
||||
# Legacy approach
|
||||
clang++ -std=c++20 \
|
||||
-I../singleheader \
|
||||
github_legacy.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_legacy_demo
|
||||
|
||||
# Modern approach
|
||||
clang++ -std=c++26 -freflection \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../singleheader -I../include \
|
||||
github_modern.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_modern_demo
|
||||
```
|
||||
|
||||
### Using CMake (Recommended)
|
||||
|
||||
```bash
|
||||
# Create build directory
|
||||
mkdir build && cd build
|
||||
|
||||
# Configure with CMake
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ \
|
||||
-DCMAKE_CXX_STANDARD=26 \
|
||||
-DCMAKE_CXX_FLAGS="-freflection -DSIMDJSON_STATIC_REFLECTION=1"
|
||||
|
||||
# Build both examples
|
||||
make github_legacy github_modern
|
||||
```
|
||||
|
||||
### Quick Build with simdjson Single Header
|
||||
|
||||
```bash
|
||||
# Legacy approach
|
||||
clang++ -std=c++20 \
|
||||
-I../singleheader \
|
||||
github_legacy.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_legacy_demo
|
||||
|
||||
# Modern approach
|
||||
clang++ -std=c++26 -freflection \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../singleheader \
|
||||
github_modern.cpp \
|
||||
../singleheader/simdjson.cpp \
|
||||
-lcpr -lcurl \
|
||||
-o github_modern_demo
|
||||
```
|
||||
|
||||
## 🏃 Running the Examples
|
||||
|
||||
```bash
|
||||
# Run legacy version
|
||||
./github_legacy_demo
|
||||
|
||||
# Run modern version
|
||||
./github_modern_demo
|
||||
```
|
||||
|
||||
## 🎯 Platform-Specific Notes
|
||||
|
||||
### Linux (x86_64)
|
||||
```bash
|
||||
-DSIMDJSON_IMPLEMENTATION_HASWELL=1
|
||||
```
|
||||
|
||||
### Linux (ARM64)
|
||||
```bash
|
||||
-DSIMDJSON_IMPLEMENTATION_ARM64=1
|
||||
```
|
||||
|
||||
### macOS (Apple Silicon)
|
||||
```bash
|
||||
-DSIMDJSON_IMPLEMENTATION_ARM64=1
|
||||
```
|
||||
|
||||
### macOS (Intel)
|
||||
```bash
|
||||
-DSIMDJSON_IMPLEMENTATION_HASWELL=1
|
||||
```
|
||||
|
||||
### Windows (MSVC)
|
||||
```cmd
|
||||
cl /std:c++20 /I..\include /I.. github_legacy.cpp /Fe:github_legacy_demo.exe
|
||||
```
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### "reflection feature not available"
|
||||
- Ensure you're using the Bloomberg clang fork
|
||||
- Check version: `clang++ --version` should show bloomberg/clang-p2996
|
||||
|
||||
### "SIMDJSON_STATIC_REFLECTION not working"
|
||||
- Make sure to define it before including headers:
|
||||
```cpp
|
||||
#define SIMDJSON_STATIC_REFLECTION 1
|
||||
#include <simdjson.h>
|
||||
```
|
||||
|
||||
### Linking errors
|
||||
- Use single-header approach for simplicity
|
||||
- Or ensure simdjson library is properly built and linked
|
||||
|
||||
### Performance issues
|
||||
- Add optimization flags: `-O3 -march=native`
|
||||
- Enable LTO: `-flto`
|
||||
|
||||
## 📦 Creating a Portable Demo
|
||||
|
||||
For conferences, prepare the demo environment:
|
||||
|
||||
```bash
|
||||
# Install cpr library (if not available)
|
||||
# Ubuntu/Debian:
|
||||
sudo apt-get install libcpr-dev
|
||||
|
||||
# macOS:
|
||||
brew install cpr
|
||||
|
||||
# Or build from source:
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && mkdir build && cd build
|
||||
cmake .. && make && sudo make install
|
||||
|
||||
# Create demo directory
|
||||
mkdir simdjson_oneliner_demo
|
||||
cd simdjson_oneliner_demo
|
||||
|
||||
# Copy necessary files
|
||||
cp path/to/github_legacy.cpp .
|
||||
cp path/to/github_modern.cpp .
|
||||
cp -r path/to/simdjson/include .
|
||||
cp -r path/to/simdjson/singleheader .
|
||||
|
||||
# Create build script
|
||||
cat > build_demo.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
echo "🔨 Building Legacy Example..."
|
||||
clang++ -std=c++20 -O3 -I./include -I. \
|
||||
github_legacy.cpp -lcpr -lcurl -o legacy_demo
|
||||
|
||||
echo "🔨 Building Modern Example (C++26)..."
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 -O3 \
|
||||
-I./include -I. github_modern.cpp -lcpr -lcurl -o modern_demo
|
||||
|
||||
echo "✅ Build complete!"
|
||||
echo "Run: ./legacy_demo or ./modern_demo"
|
||||
EOF
|
||||
|
||||
chmod +x build_demo.sh
|
||||
```
|
||||
|
||||
## 🎪 Conference Checklist
|
||||
|
||||
- [ ] Bloomberg clang installed on demo machine
|
||||
- [ ] Both examples compile cleanly
|
||||
- [ ] Internet connection for live API calls (or use standalone)
|
||||
- [ ] Backup: pre-recorded video of compilation and execution
|
||||
- [ ] Slides with code snippets
|
||||
- [ ] QR code for GitHub repo
|
||||
|
||||
## 🔗 Quick Links
|
||||
|
||||
- simdjson: https://github.com/simdjson/simdjson
|
||||
- Bloomberg Clang: https://github.com/bloomberg/clang-p2996
|
||||
- P2996 Reflection Proposal: https://wg21.link/p2996
|
||||
- Talk Resources: [Your GitHub repo with examples]
|
||||
@@ -1 +1,23 @@
|
||||
add_subdirectory(quickstart)
|
||||
|
||||
# Add simdjson one-liner demo examples
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/github_legacy.cpp AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/github_modern.cpp)
|
||||
# FetchContent to download cpr
|
||||
include(FetchContent)
|
||||
set(CPR_ENABLE_SSL OFF CACHE BOOL "")
|
||||
FetchContent_Declare(
|
||||
cpr
|
||||
GIT_REPOSITORY https://github.com/libcpr/cpr.git
|
||||
GIT_TAG 1.10.5
|
||||
)
|
||||
FetchContent_MakeAvailable(cpr)
|
||||
|
||||
# Build the examples
|
||||
add_executable(github_legacy github_legacy.cpp)
|
||||
target_link_libraries(github_legacy simdjson cpr::cpr)
|
||||
|
||||
add_executable(github_modern github_modern.cpp)
|
||||
target_link_libraries(github_modern simdjson cpr::cpr)
|
||||
target_compile_options(github_modern PRIVATE -std=c++26 -freflection)
|
||||
target_compile_definitions(github_modern PRIVATE SIMDJSON_STATIC_REFLECTION=1)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
# 🚀 From Boilerplate to One-Liner: The simdjson Revolution
|
||||
|
||||
## Conference Talk: JSON Parsing in Modern C++
|
||||
|
||||
### 📋 Talk Abstract
|
||||
Discover how simdjson's new API combined with C++26 reflection transforms JSON parsing from a tedious, error-prone task into a single line of code. This talk showcases the dramatic evolution from manual parsing to automatic struct deserialization.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Key Talking Points
|
||||
|
||||
### 1. **The Problem** (2 minutes)
|
||||
- JSON is everywhere: APIs, configs, data exchange
|
||||
- C++ historically makes JSON parsing verbose
|
||||
- Show other languages: `user = json.loads(data)` in Python
|
||||
- "Why can't C++ be this simple?"
|
||||
|
||||
### 2. **The Legacy Approach** (5 minutes)
|
||||
- Live demo: `github_legacy.cpp`
|
||||
- Walk through the boilerplate:
|
||||
```cpp
|
||||
// 😓 Manual field extraction
|
||||
user.login = std::string(doc["login"].get_string().value());
|
||||
user.id = doc["id"].get_int64().value();
|
||||
|
||||
// 😰 Handle optional fields
|
||||
auto company_result = doc["company"];
|
||||
if (!company_result.is_null()) {
|
||||
user.company = std::string(company_result.get_string().value());
|
||||
}
|
||||
```
|
||||
- Count the lines: ~30 lines just for parsing!
|
||||
- Error-prone: typos, missing fields, type mismatches
|
||||
|
||||
### 3. **The Magic Moment** (3 minutes)
|
||||
- "What if I told you it could be just ONE line?"
|
||||
- Show `github_modern.cpp`
|
||||
- The magic line:
|
||||
```cpp
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(json_data));
|
||||
```
|
||||
- Audience reaction: 🤯
|
||||
|
||||
### 4. **How It Works** (5 minutes)
|
||||
- C++26 static reflection
|
||||
- Compile-time struct introspection
|
||||
- simdjson generates parsing code automatically
|
||||
- Zero runtime overhead - it's all compile-time!
|
||||
|
||||
### 5. **Live Demo** (5 minutes)
|
||||
- Compile both examples
|
||||
- Run them side-by-side
|
||||
- Show identical output
|
||||
- Highlight the code difference
|
||||
- Add a new field to the struct - watch it "just work"
|
||||
|
||||
### 6. **Deserialization Performance** (3 minutes)
|
||||
- "But is deserialization fast?"
|
||||
- Live benchmark demonstration
|
||||
- Both approaches achieve ~2.7 GB/s deserialization speed on real GitHub API data
|
||||
- Reflection adds ZERO runtime overhead to deserialization
|
||||
- "You get simplicity WITHOUT sacrificing speed!"
|
||||
- Note: We're measuring JSON → struct deserialization performance
|
||||
- Note: Performance scales with larger documents (up to 3+ GB/s)
|
||||
|
||||
### 7. **The Future is Now** (2 minutes)
|
||||
- Bloomberg clang fork available today
|
||||
- C++26 coming soon
|
||||
- Start preparing your codebases
|
||||
- simdjson ready for the future
|
||||
|
||||
---
|
||||
|
||||
## 💻 Live Coding Demo Script
|
||||
|
||||
### Setup
|
||||
```bash
|
||||
# Show the two files
|
||||
ls -la github_*.cpp
|
||||
|
||||
# Show line count difference
|
||||
wc -l github_legacy.cpp github_modern.cpp
|
||||
```
|
||||
|
||||
### Demo 1: The Pain of Legacy
|
||||
```bash
|
||||
# Open github_legacy.cpp in editor
|
||||
# Highlight the parse_github_user function
|
||||
# Point out each manual field extraction
|
||||
# "Look at all this code just to parse 7 fields!"
|
||||
```
|
||||
|
||||
### Demo 2: The Modern Magic
|
||||
```bash
|
||||
# Open github_modern.cpp
|
||||
# Show the struct - "Just a plain struct!"
|
||||
# Show the one-liner
|
||||
# "That's it. That's the entire parsing code."
|
||||
```
|
||||
|
||||
### Demo 3: Compilation and Execution
|
||||
|
||||
**Option 1: Use the demo script (Recommended)**
|
||||
```bash
|
||||
# The script handles everything - building cpr, compiling, and running
|
||||
./conference_demo.sh
|
||||
|
||||
# For the extended demo with serialization:
|
||||
./conference_demo_serialization.sh
|
||||
```
|
||||
|
||||
**Option 2: Manual compilation**
|
||||
```bash
|
||||
# Build cpr first (one-time setup)
|
||||
mkdir -p ../deps && cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON \
|
||||
-DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
|
||||
# Compile legacy
|
||||
clang++ -std=c++20 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_legacy.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_demo
|
||||
|
||||
# Compile modern (with reflection)
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_modern.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_demo
|
||||
|
||||
# Run both - they fetch real GitHub data!
|
||||
./legacy_demo
|
||||
./modern_demo
|
||||
```
|
||||
|
||||
### Demo 4: Adding a New Field (Crowd Pleaser!)
|
||||
```cpp
|
||||
// Add to struct in both files:
|
||||
std::string twitter_username;
|
||||
|
||||
// Legacy: Must add parsing code
|
||||
else if (key == "twitter_username")
|
||||
user.twitter_username = field.value().get_string().value();
|
||||
|
||||
// Modern: Nothing to add! It just works!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎤 Speaker Notes
|
||||
|
||||
### Opening Hook
|
||||
"How many of you have written JSON parsing code in C++? Keep your hands up if you enjoyed it... Yeah, I thought so."
|
||||
|
||||
### Transition to Modern
|
||||
"But what if I told you that in 2024, with simdjson and C++26, parsing JSON can be as simple as Python?"
|
||||
|
||||
### After showing the one-liner
|
||||
"No, this isn't pseudocode. This is real, working C++ code. Let me prove it to you."
|
||||
|
||||
### Addressing Skeptics
|
||||
- "It's not magic, it's metaprogramming"
|
||||
- "No runtime cost - it's all compile-time"
|
||||
- "Yes, it handles errors properly"
|
||||
- "Yes, it's production-ready"
|
||||
|
||||
### Closing
|
||||
"The future of C++ is here. It's fast, it's simple, and it's beautiful. Stop writing boilerplate. Start writing the code that matters."
|
||||
|
||||
---
|
||||
|
||||
## 📊 Slide Suggestions
|
||||
|
||||
### Slide 1: Title
|
||||
**From 50 Lines to 1: The simdjson Revolution**
|
||||
*Your Name - Conference 2024*
|
||||
|
||||
### Slide 2: The Problem
|
||||
```python
|
||||
# Python
|
||||
user = json.loads(data)
|
||||
|
||||
# JavaScript
|
||||
const user = JSON.parse(data);
|
||||
|
||||
# C++ ???
|
||||
// 😭 50+ lines of boilerplate
|
||||
```
|
||||
|
||||
### Slide 3: The Solution
|
||||
```cpp
|
||||
// C++26 with simdjson
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(data));
|
||||
```
|
||||
|
||||
### Slide 4: Performance Graph
|
||||
- Bar chart showing simdjson vs other parsers
|
||||
- "Fast AND Simple"
|
||||
|
||||
### Slide 5: Timeline
|
||||
- 2018: simdjson introduced (fast but verbose)
|
||||
- 2024: New API design
|
||||
- 2024: C++26 reflection support
|
||||
- Future: It's here!
|
||||
|
||||
### Slide 6: Call to Action
|
||||
- Try it today: github.com/simdjson/simdjson
|
||||
- Bloomberg clang: github.com/bloomberg/clang-p2996
|
||||
- Join the revolution!
|
||||
|
||||
---
|
||||
|
||||
## 🔥 Audience Engagement
|
||||
|
||||
### Interactive Elements
|
||||
1. **Live Poll**: "How many lines of code for parsing JSON?"
|
||||
2. **Challenge**: "Spot the bug in this manual parsing code"
|
||||
3. **Q&A Focus**: Performance, error handling, compatibility
|
||||
|
||||
### Memorable Moments
|
||||
- The reveal of the one-liner
|
||||
- Live compilation with reflection
|
||||
- Adding a field without changing parsing code
|
||||
- Performance numbers
|
||||
|
||||
### Takeaway Message
|
||||
"C++ doesn't have to be painful. With modern tools and modern standards, C++ can be as elegant as any language - and faster than all of them."
|
||||
@@ -0,0 +1,72 @@
|
||||
# Installing CPR Library
|
||||
|
||||
The examples require the CPR library for making HTTP requests to the GitHub API.
|
||||
|
||||
## Option 1: Install via Package Manager (Recommended)
|
||||
|
||||
### Ubuntu/Debian
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install libcpr-dev
|
||||
```
|
||||
|
||||
### macOS (Homebrew)
|
||||
```bash
|
||||
brew install cpr
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
```bash
|
||||
sudo pacman -S cpr
|
||||
```
|
||||
|
||||
## Option 2: Build from Source
|
||||
|
||||
```bash
|
||||
# Clone cpr
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr
|
||||
|
||||
# Build and install
|
||||
mkdir build && cd build
|
||||
cmake .. -DCPR_USE_SYSTEM_CURL=ON
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Option 3: Using vcpkg
|
||||
|
||||
```bash
|
||||
vcpkg install cpr
|
||||
```
|
||||
|
||||
## Option 4: Using Conan
|
||||
|
||||
```bash
|
||||
conan install cpr/1.10.5@
|
||||
```
|
||||
|
||||
## Compilation
|
||||
|
||||
Once cpr is installed, compile the examples:
|
||||
|
||||
```bash
|
||||
# Legacy example
|
||||
clang++ -std=c++20 -I../include -I.. github_legacy.cpp -lcpr -lcurl -o github_legacy_demo
|
||||
|
||||
# Modern example (requires Bloomberg clang)
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../include -I.. github_modern.cpp -lcpr -lcurl -o github_modern_demo
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you get "cpr/cpr.h not found", check:
|
||||
- `pkg-config --cflags --libs cpr`
|
||||
- Add include path: `-I/usr/local/include`
|
||||
- Add library path: `-L/usr/local/lib`
|
||||
|
||||
For SSL issues, ensure OpenSSL is installed:
|
||||
- Ubuntu/Debian: `sudo apt-get install libssl-dev`
|
||||
- macOS: `brew install openssl`
|
||||
- Link OpenSSL: `-lssl -lcrypto`
|
||||
@@ -0,0 +1,74 @@
|
||||
# 🚀 simdjson One-Liner Demo: From Boilerplate to Magic
|
||||
|
||||
This demo showcases the dramatic simplification of JSON parsing in C++ using simdjson's new API combined with C++26 reflection.
|
||||
|
||||
## Files
|
||||
|
||||
- `github_legacy.cpp` - Traditional manual JSON parsing approach (~30 lines of parsing code)
|
||||
- `github_modern.cpp` - Modern C++26 reflection approach (1 line of parsing code!)
|
||||
- `CONFERENCE_TALK.md` - Complete conference presentation guide
|
||||
- `BUILD_INSTRUCTIONS.md` - Detailed compilation instructions
|
||||
- `conference_demo.sh` - Interactive demo script for presentations
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
- Bloomberg clang for C++26: https://github.com/bloomberg/clang-p2996
|
||||
- curl library (usually pre-installed)
|
||||
- cpr library (built automatically by demo script)
|
||||
|
||||
### Easy Demo
|
||||
```bash
|
||||
# Just run the demo script - it handles everything!
|
||||
./conference_demo.sh
|
||||
```
|
||||
|
||||
The script will:
|
||||
1. Build cpr if needed
|
||||
2. Compile both examples
|
||||
3. Run interactive presentation
|
||||
|
||||
### Manual Compilation
|
||||
|
||||
If you want to compile manually:
|
||||
|
||||
```bash
|
||||
# Build cpr first (if not installed)
|
||||
mkdir -p ../deps && cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
|
||||
# Compile examples
|
||||
# Legacy
|
||||
clang++ -std=c++20 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_legacy.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_demo
|
||||
|
||||
# Modern
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_modern.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_demo
|
||||
```
|
||||
|
||||
## The Magic ✨
|
||||
|
||||
**Before (Legacy):**
|
||||
```cpp
|
||||
// 30+ lines of manual parsing...
|
||||
user.login = std::string(doc["login"].get_string().value());
|
||||
user.id = doc["id"].get_int64().value();
|
||||
// ... etc for each field
|
||||
```
|
||||
|
||||
**After (Modern):**
|
||||
```cpp
|
||||
// Just ONE line!
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
```
|
||||
|
||||
Both examples fetch real data from GitHub API to demonstrate real-world usage!
|
||||
@@ -0,0 +1,53 @@
|
||||
# simdjson Serialization with C++26 Reflection
|
||||
|
||||
This directory now includes examples demonstrating **both** deserialization and serialization using C++26 reflection features.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Basic Round-Trip Demo
|
||||
```bash
|
||||
# Compile and run the simple round-trip demo
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
reflection_roundtrip_demo.cpp ../singleheader/simdjson.cpp \
|
||||
-o reflection_roundtrip_demo
|
||||
./reflection_roundtrip_demo
|
||||
```
|
||||
|
||||
### Conference Demo with Serialization
|
||||
```bash
|
||||
# Run the extended conference demo that includes serialization
|
||||
./conference_demo_serialization.sh
|
||||
```
|
||||
|
||||
## 📝 What's New?
|
||||
|
||||
The experimental `simdjson::builder::to_json_string()` function enables one-line serialization:
|
||||
|
||||
```cpp
|
||||
// Deserialize JSON → Struct
|
||||
MyStruct obj = simdjson::from(json);
|
||||
|
||||
// Serialize Struct → JSON (NEW!)
|
||||
std::string json = simdjson::builder::to_json_string(obj);
|
||||
```
|
||||
|
||||
## 🎯 Features
|
||||
|
||||
- **Zero boilerplate**: No need to write serialization code
|
||||
- **Automatic handling**: Optional fields, containers, nested structures
|
||||
- **Type-safe**: Compile-time checking with reflection
|
||||
- **Performant**: Optimized string building
|
||||
|
||||
## ⚠️ Requirements
|
||||
|
||||
- Bloomberg's clang fork (clang-p2996) with C++26 reflection support
|
||||
- Build with `-DSIMDJSON_STATIC_REFLECTION=1`
|
||||
- Include `simdjson/builder/json_builder.h`
|
||||
|
||||
## 📊 Performance
|
||||
|
||||
While serialization is generally slower than deserialization (due to string building vs parsing), the reflection-based approach is still highly optimized and significantly faster than manual string concatenation approaches.
|
||||
|
||||
## 🔧 Status
|
||||
|
||||
This serialization support is **experimental** and part of the builder API. The API may change as C++26 reflection features evolve.
|
||||
Executable
+167
@@ -0,0 +1,167 @@
|
||||
#!/bin/bash
|
||||
# Conference Demo Script - simdjson One-Liner Magic
|
||||
# Run this during your talk for a smooth demo experience
|
||||
|
||||
# Check if cpr is built
|
||||
if [ ! -f "../deps/cpr/build/lib/libcpr.a" ]; then
|
||||
echo "⚠️ CPR library not found. Building it first..."
|
||||
echo ""
|
||||
if [ ! -d "../deps/cpr" ]; then
|
||||
mkdir -p ../deps
|
||||
cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
cd ../..
|
||||
fi
|
||||
cd ../deps/cpr
|
||||
mkdir -p build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
echo "✅ CPR built successfully!"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
clear
|
||||
echo "🚀 simdjson: From Boilerplate to One-Liner"
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "Press Enter to continue..."
|
||||
read
|
||||
|
||||
# Show the legacy approach
|
||||
echo "📚 First, let's look at the LEGACY approach..."
|
||||
echo ""
|
||||
echo "Opening github_legacy.cpp..."
|
||||
sleep 1
|
||||
echo ""
|
||||
echo "Key points:"
|
||||
echo " • Manual parse_github_user() function"
|
||||
echo " • Extract each field individually"
|
||||
echo " • Handle optional fields explicitly"
|
||||
echo " • ~30 lines of parsing code"
|
||||
echo ""
|
||||
echo "Press Enter to see the code..."
|
||||
read
|
||||
|
||||
# Display key parts of legacy code
|
||||
cat github_legacy.cpp | grep -A 20 "parse_github_user" | head -25
|
||||
|
||||
echo ""
|
||||
echo "😓 That's a lot of boilerplate!"
|
||||
echo ""
|
||||
echo "Press Enter to compile and run..."
|
||||
read
|
||||
|
||||
# Compile legacy
|
||||
echo "🔨 Compiling legacy version..."
|
||||
echo "Command: clang++ -std=c++20 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes -I../include -I.. github_legacy.cpp ../singleheader/simdjson.cpp ../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_demo"
|
||||
clang++ -std=c++20 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes -I../include -I.. github_legacy.cpp ../singleheader/simdjson.cpp ../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_demo
|
||||
echo "✅ Compiled!"
|
||||
echo ""
|
||||
echo "🏃 Running legacy demo (fetching real GitHub data)..."
|
||||
echo ""
|
||||
./legacy_demo
|
||||
|
||||
echo ""
|
||||
echo "Press Enter to see the MODERN approach..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "✨ Now, let's look at the MODERN approach with C++26 reflection..."
|
||||
echo ""
|
||||
echo "Opening github_modern.cpp..."
|
||||
sleep 1
|
||||
echo ""
|
||||
echo "Key points:"
|
||||
echo " • Just declare your struct"
|
||||
echo " • ONE line of parsing code"
|
||||
echo " • C++26 reflection handles everything"
|
||||
echo " • No manual field extraction!"
|
||||
echo ""
|
||||
echo "Press Enter to see the magic..."
|
||||
read
|
||||
|
||||
# Show the struct and the one-liner
|
||||
echo "The struct (just a plain struct!):"
|
||||
echo ""
|
||||
cat github_modern.cpp | grep -A 10 "struct GitHubUser" | head -12
|
||||
echo ""
|
||||
echo "The parsing code (ONE LINE!):"
|
||||
echo ""
|
||||
echo " GitHubUser user = simdjson::from(simdjson::padded_string(response.text));"
|
||||
echo ""
|
||||
echo "🤯 That's it! That's all the parsing code!"
|
||||
echo ""
|
||||
echo "Press Enter to compile with C++26 reflection..."
|
||||
read
|
||||
|
||||
# Compile modern
|
||||
echo "🔨 Compiling modern version with Bloomberg clang..."
|
||||
echo "Command: clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes -I../include -I.. github_modern.cpp ../singleheader/simdjson.cpp ../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_demo"
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 -I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes -I../include -I.. github_modern.cpp ../singleheader/simdjson.cpp ../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_demo
|
||||
echo "✅ Compiled!"
|
||||
echo ""
|
||||
echo "🏃 Running modern demo (fetching real GitHub data)..."
|
||||
echo ""
|
||||
./modern_demo
|
||||
|
||||
echo ""
|
||||
echo "Press Enter to see side-by-side comparison..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "📊 SIDE-BY-SIDE COMPARISON"
|
||||
echo "========================="
|
||||
echo ""
|
||||
echo "Legacy Approach: Modern Approach (C++26):"
|
||||
echo "---------------- ------------------------"
|
||||
echo "❌ 30+ lines of parsing code ✅ 1 line of parsing code"
|
||||
echo "❌ Manual field extraction ✅ Automatic with reflection"
|
||||
echo "❌ Error-prone ✅ Type-safe"
|
||||
echo "❌ Hard to maintain ✅ Just update the struct"
|
||||
echo "❌ Boilerplate for each type ✅ Works for any struct"
|
||||
echo ""
|
||||
echo ""
|
||||
echo "Press Enter to run PERFORMANCE BENCHMARKS..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "⚡ DESERIALIZATION PERFORMANCE BENCHMARKS"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Let's measure the actual JSON → struct deserialization speed..."
|
||||
echo ""
|
||||
|
||||
# Compile benchmarks if not exist
|
||||
if [ ! -f "./legacy_benchmark" ] || [ ! -f "./modern_benchmark" ]; then
|
||||
echo "🔨 Compiling benchmark versions..."
|
||||
|
||||
clang++ -std=c++20 -O3 -march=native \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_legacy_benchmark.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o legacy_benchmark 2>/dev/null
|
||||
|
||||
clang++ -std=c++26 -freflection -O3 -march=native \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_modern_benchmark.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o modern_benchmark 2>/dev/null
|
||||
fi
|
||||
|
||||
echo "🏃 Running legacy benchmark..."
|
||||
echo ""
|
||||
./legacy_benchmark
|
||||
|
||||
echo ""
|
||||
echo "Press Enter to run modern benchmark..."
|
||||
read
|
||||
|
||||
echo "🏃 Running modern benchmark..."
|
||||
echo ""
|
||||
./modern_benchmark
|
||||
|
||||
echo ""
|
||||
echo "🎉 The future of C++ is here!"
|
||||
echo ""
|
||||
echo "Questions?"
|
||||
Executable
+322
@@ -0,0 +1,322 @@
|
||||
#!/bin/bash
|
||||
# Conference Demo Script - simdjson One-Liner Magic (WITH SERIALIZATION!)
|
||||
# Extended demo showcasing both deserialization AND serialization with C++26 reflection
|
||||
|
||||
# Check if cpr is built
|
||||
if [ ! -f "../deps/cpr/build/lib/libcpr.a" ]; then
|
||||
echo "⚠️ CPR library not found. Building it first..."
|
||||
echo ""
|
||||
if [ ! -d "../deps/cpr" ]; then
|
||||
mkdir -p ../deps
|
||||
cd ../deps
|
||||
git clone https://github.com/libcpr/cpr.git
|
||||
cd cpr && git checkout 1.10.5
|
||||
cd ../..
|
||||
fi
|
||||
cd ../deps/cpr
|
||||
mkdir -p build && cd build
|
||||
cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCPR_USE_SYSTEM_CURL=ON -DBUILD_SHARED_LIBS=OFF -DCPR_ENABLE_SSL=OFF
|
||||
make -j4
|
||||
cd ../../../examples
|
||||
echo "✅ CPR built successfully!"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
clear
|
||||
echo "🚀 simdjson: Complete JSON Round-Trip with C++26 Reflection"
|
||||
echo "============================================================"
|
||||
echo ""
|
||||
echo "Today we'll showcase BOTH deserialization AND serialization!"
|
||||
echo ""
|
||||
echo "Press Enter to continue..."
|
||||
read
|
||||
|
||||
# Create a demo program that shows serialization
|
||||
cat > github_serialization_demo.cpp << 'EOF'
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "simdjson.h"
|
||||
#include <cpr/cpr.h>
|
||||
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> blog;
|
||||
std::optional<std::string> location;
|
||||
std::optional<std::string> email;
|
||||
std::optional<std::string> bio;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
int64_t following;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "🔄 C++26 Reflection: Complete JSON Round-Trip Demo\n";
|
||||
std::cout << "================================================\n\n";
|
||||
|
||||
// Step 1: Fetch real data from GitHub
|
||||
std::cout << "📡 Fetching GitHub user data...\n";
|
||||
auto response = cpr::Get(cpr::Url{"https://api.github.com/users/simdjson"});
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "Error: Failed to fetch data\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "✅ Received " << response.text.length() << " bytes of JSON\n\n";
|
||||
|
||||
// Step 2: Deserialize with ONE LINE
|
||||
std::cout << "📥 DESERIALIZATION (JSON → Struct)\n";
|
||||
std::cout << "Code: GitHubUser user = simdjson::from(simdjson::padded_string(response.text));\n\n";
|
||||
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
|
||||
// Show the data we parsed
|
||||
std::cout << "Parsed data:\n";
|
||||
std::cout << " • Login: " << user.login << "\n";
|
||||
std::cout << " • Name: " << (user.name.has_value() ? *user.name : "<not set>") << "\n";
|
||||
std::cout << " • Company: " << (user.company.has_value() ? *user.company : "<not set>") << "\n";
|
||||
std::cout << " • Location: " << (user.location.has_value() ? *user.location : "<not set>") << "\n";
|
||||
std::cout << " • Bio: " << (user.bio.has_value() ? *user.bio : "<not set>") << "\n";
|
||||
std::cout << " • Repos: " << user.public_repos << "\n";
|
||||
std::cout << " • Followers: " << user.followers << "\n\n";
|
||||
|
||||
// Step 3: Modify the data
|
||||
std::cout << "✏️ Modifying data...\n";
|
||||
user.followers += 1000; // Wishful thinking!
|
||||
user.name = "simdjson - JSON at the speed of light"; // Set a name
|
||||
user.bio = user.bio.value_or("") + " (Now with C++26 reflection!)";
|
||||
std::cout << " • Added 1000 followers (we can dream!)\n";
|
||||
std::cout << " • Set organization name\n";
|
||||
std::cout << " • Updated bio\n\n";
|
||||
|
||||
// Step 4: Serialize back to JSON with ONE LINE
|
||||
std::cout << "📤 SERIALIZATION (Struct → JSON)\n";
|
||||
std::cout << "Code: std::string json = simdjson::builder::to_json_string(user);\n\n";
|
||||
|
||||
auto json_result = simdjson::builder::to_json_string(user);
|
||||
if (json_result.error()) {
|
||||
std::cerr << "Serialization error!\n";
|
||||
return 1;
|
||||
}
|
||||
std::string json = json_result.value();
|
||||
|
||||
std::cout << "Generated JSON (" << json.length() << " bytes):\n";
|
||||
// Pretty print first 200 chars
|
||||
if (json.length() > 200) {
|
||||
std::cout << json.substr(0, 200) << "...\n\n";
|
||||
} else {
|
||||
std::cout << json << "\n\n";
|
||||
}
|
||||
|
||||
// Step 5: Verify round-trip
|
||||
std::cout << "🔄 ROUND-TRIP VERIFICATION\n";
|
||||
std::cout << "Parsing our generated JSON back...\n";
|
||||
|
||||
GitHubUser user2 = simdjson::from(simdjson::padded_string(json));
|
||||
|
||||
std::cout << "✅ Round-trip successful!\n";
|
||||
std::cout << " • Original followers: " << user.followers << "\n";
|
||||
std::cout << " • Parsed followers: " << user2.followers << "\n";
|
||||
std::cout << " • Name set correctly: " << (user2.name.has_value() && *user2.name == *user.name ? "YES" : "NO") << "\n";
|
||||
std::cout << " • Bio preserved: " << (user2.bio.has_value() ? "YES" : "NO") << "\n\n";
|
||||
|
||||
std::cout << "🎉 That's it! Two lines of code for complete JSON handling:\n";
|
||||
std::cout << " • Deserialization: simdjson::from(...)\n";
|
||||
std::cout << " • Serialization: simdjson::builder::to_json_string(...)\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create a benchmark that includes serialization
|
||||
cat > serialization_benchmark.cpp << 'EOF'
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
#include "simdjson.h"
|
||||
#include <map>
|
||||
|
||||
struct TestData {
|
||||
std::string name;
|
||||
std::vector<int> numbers;
|
||||
std::map<std::string, double> metrics;
|
||||
bool active;
|
||||
std::optional<std::string> description;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "⚡ Serialization Performance Benchmark\n";
|
||||
std::cout << "=====================================\n\n";
|
||||
|
||||
// Create test data
|
||||
TestData data{
|
||||
.name = "Performance Test",
|
||||
.numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
.metrics = {{"latency", 1.23}, {"throughput", 456.78}, {"cpu", 89.01}},
|
||||
.active = true,
|
||||
.description = "Testing reflection-based serialization performance"
|
||||
};
|
||||
|
||||
const int iterations = 100000;
|
||||
|
||||
// Benchmark serialization
|
||||
std::cout << "📤 Benchmarking serialization (" << iterations << " iterations)...\n";
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::string json;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
auto result = simdjson::builder::to_json_string(data);
|
||||
if (i == 0 && !result.error()) {
|
||||
json = result.value();
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
std::cout << "\nResults:\n";
|
||||
std::cout << " • Generated JSON size: " << json.length() << " bytes\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
|
||||
double time_per_iteration_us = duration.count() / double(iterations);
|
||||
double time_per_iteration_s = time_per_iteration_us / 1000000.0;
|
||||
double bytes_per_second = json.length() / time_per_iteration_s;
|
||||
double mb_per_second = bytes_per_second / (1024.0 * 1024.0);
|
||||
|
||||
std::cout << " • Time per serialization: " << std::fixed << std::setprecision(2) << time_per_iteration_us << " μs\n";
|
||||
std::cout << " • Throughput: " << std::fixed << std::setprecision(2) << mb_per_second << " MB/s\n";
|
||||
|
||||
std::cout << "\nGenerated JSON:\n" << json << "\n\n";
|
||||
|
||||
// Benchmark deserialization for comparison
|
||||
std::cout << "📥 Benchmarking deserialization (for comparison)...\n";
|
||||
|
||||
simdjson::padded_string padded_json(json);
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
TestData parsed = simdjson::from(padded_json);
|
||||
}
|
||||
|
||||
end = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
std::cout << "\nResults:\n";
|
||||
time_per_iteration_us = duration.count() / double(iterations);
|
||||
time_per_iteration_s = time_per_iteration_us / 1000000.0;
|
||||
bytes_per_second = json.length() / time_per_iteration_s;
|
||||
mb_per_second = bytes_per_second / (1024.0 * 1024.0);
|
||||
|
||||
std::cout << " • Time per deserialization: " << std::fixed << std::setprecision(2) << time_per_iteration_us << " μs\n";
|
||||
std::cout << " • Throughput: " << std::fixed << std::setprecision(2) << mb_per_second << " MB/s\n";
|
||||
|
||||
std::cout << "\n✅ Both serialization and deserialization work with reflection!\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "📝 First, let's see the traditional approach to serialization..."
|
||||
echo ""
|
||||
echo "Press Enter to see manual serialization code..."
|
||||
read
|
||||
|
||||
cat > manual_serialization_example.cpp << 'EOF'
|
||||
// The OLD way - Manual serialization
|
||||
std::string serialize_github_user(const GitHubUser& user) {
|
||||
std::string json = "{";
|
||||
json += "\"login\":\"" + user.login + "\",";
|
||||
json += "\"name\":\"" + user.name + "\",";
|
||||
json += "\"company\":\"" + user.company + "\",";
|
||||
json += "\"blog\":\"" + user.blog + "\",";
|
||||
json += "\"location\":\"" + user.location + "\",";
|
||||
|
||||
if (user.email.has_value()) {
|
||||
json += "\"email\":\"" + *user.email + "\",";
|
||||
}
|
||||
if (user.bio.has_value()) {
|
||||
json += "\"bio\":\"" + *user.bio + "\",";
|
||||
}
|
||||
|
||||
json += "\"public_repos\":" + std::to_string(user.public_repos) + ",";
|
||||
json += "\"followers\":" + std::to_string(user.followers) + ",";
|
||||
json += "\"following\":" + std::to_string(user.following);
|
||||
json += "}";
|
||||
|
||||
return json;
|
||||
}
|
||||
EOF
|
||||
|
||||
cat manual_serialization_example.cpp
|
||||
echo ""
|
||||
echo "😓 That's error-prone and doesn't handle escaping!"
|
||||
echo ""
|
||||
echo "Press Enter to see the MODERN approach..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "✨ The MODERN approach with C++26 reflection:"
|
||||
echo ""
|
||||
echo "Deserialization:"
|
||||
echo " GitHubUser user = simdjson::from(json);"
|
||||
echo ""
|
||||
echo "Serialization:"
|
||||
echo " std::string json = simdjson::builder::to_json_string(user);"
|
||||
echo ""
|
||||
echo "🤯 That's it! Bidirectional JSON handling in TWO lines!"
|
||||
echo ""
|
||||
echo "Press Enter to compile and run the complete demo..."
|
||||
read
|
||||
|
||||
# Compile and run serialization demo
|
||||
echo "🔨 Compiling serialization demo..."
|
||||
clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../deps/cpr/include -I../deps/cpr/build/cpr_generated_includes \
|
||||
-I../include -I.. github_serialization_demo.cpp ../singleheader/simdjson.cpp \
|
||||
../deps/cpr/build/lib/libcpr.a -lcurl -pthread -o serialization_demo
|
||||
|
||||
echo "✅ Compiled!"
|
||||
echo ""
|
||||
echo "🏃 Running complete round-trip demo..."
|
||||
echo ""
|
||||
./serialization_demo
|
||||
|
||||
echo ""
|
||||
echo "Press Enter to run performance benchmarks..."
|
||||
read
|
||||
|
||||
clear
|
||||
echo "⚡ PERFORMANCE BENCHMARKS"
|
||||
echo "========================"
|
||||
echo ""
|
||||
|
||||
# Compile benchmark
|
||||
echo "🔨 Compiling performance benchmark..."
|
||||
clang++ -std=c++26 -freflection -O3 -march=native \
|
||||
-DSIMDJSON_STATIC_REFLECTION=1 \
|
||||
-I../include -I.. serialization_benchmark.cpp ../singleheader/simdjson.cpp \
|
||||
-pthread -o serialization_benchmark
|
||||
|
||||
echo "✅ Compiled!"
|
||||
echo ""
|
||||
echo "🏃 Running benchmark..."
|
||||
echo ""
|
||||
./serialization_benchmark
|
||||
|
||||
echo ""
|
||||
echo "🎉 Summary:"
|
||||
echo "==========="
|
||||
echo "• ONE line for deserialization"
|
||||
echo "• ONE line for serialization"
|
||||
echo "• Works with complex nested structures"
|
||||
echo "• Handles optional fields automatically"
|
||||
echo "• Type-safe and performant"
|
||||
echo "• No manual parsing/building code needed!"
|
||||
echo ""
|
||||
echo "The future of C++ JSON handling is here! 🚀"
|
||||
|
||||
# Cleanup
|
||||
rm -f manual_serialization_example.cpp
|
||||
@@ -0,0 +1,83 @@
|
||||
// Legacy approach - manual JSON parsing with simdjson
|
||||
#include <simdjson.h>
|
||||
#include <cpr/cpr.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
int64_t id;
|
||||
std::string name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> location;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
};
|
||||
|
||||
// Legacy approach - manual parsing with lots of boilerplate
|
||||
GitHubUser parse_github_user(const std::string& json_str) {
|
||||
GitHubUser user;
|
||||
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::padded_string json(json_str);
|
||||
simdjson::ondemand::document doc = parser.iterate(json);
|
||||
|
||||
// Manual field extraction with error checking
|
||||
user.login = std::string(doc["login"].get_string().value());
|
||||
user.id = doc["id"].get_int64().value();
|
||||
user.name = std::string(doc["name"].get_string().value());
|
||||
|
||||
// Handle optional fields
|
||||
auto company_result = doc["company"];
|
||||
if (!company_result.is_null()) {
|
||||
user.company = std::string(company_result.get_string().value());
|
||||
}
|
||||
|
||||
auto location_result = doc["location"];
|
||||
if (!location_result.is_null()) {
|
||||
user.location = std::string(location_result.get_string().value());
|
||||
}
|
||||
|
||||
user.public_repos = doc["public_repos"].get_int64().value();
|
||||
user.followers = doc["followers"].get_int64().value();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "📚 Legacy Approach - Manual JSON Parsing\n";
|
||||
std::cout << "========================================\n\n";
|
||||
|
||||
// Fetch data from GitHub API
|
||||
auto response = cpr::Get(
|
||||
cpr::Url{"https://api.github.com/users/lemire"},
|
||||
cpr::Header{{"User-Agent", "simdjson-legacy-demo"}}
|
||||
);
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "❌ Failed to fetch data: HTTP " << response.status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
// 😓 The old way - manual parsing with lots of code
|
||||
GitHubUser user = parse_github_user(response.text);
|
||||
|
||||
// Display results
|
||||
std::cout << "GitHub User: " << user.name << " (@" << user.login << ")\n";
|
||||
std::cout << "ID: " << user.id << "\n";
|
||||
if (user.company) std::cout << "Company: " << *user.company << "\n";
|
||||
if (user.location) std::cout << "Location: " << *user.location << "\n";
|
||||
std::cout << "Public Repos: " << user.public_repos << "\n";
|
||||
std::cout << "Followers: " << user.followers << "\n";
|
||||
|
||||
std::cout << "\n⚠️ Notice all the manual parsing code required!\n";
|
||||
|
||||
} catch (const simdjson::simdjson_error& e) {
|
||||
std::cerr << "❌ Parsing error: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Legacy approach with performance measurement
|
||||
#include <simdjson.h>
|
||||
#include <cpr/cpr.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
int64_t id;
|
||||
std::string name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> location;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
};
|
||||
|
||||
// Manual parsing function (the old way)
|
||||
GitHubUser parse_github_user(const std::string& json_str) {
|
||||
GitHubUser user;
|
||||
|
||||
simdjson::ondemand::parser parser;
|
||||
simdjson::padded_string json(json_str);
|
||||
simdjson::ondemand::document doc = parser.iterate(json);
|
||||
|
||||
// Manual field extraction with error checking
|
||||
user.login = std::string(doc["login"].get_string().value());
|
||||
user.id = doc["id"].get_int64().value();
|
||||
user.name = std::string(doc["name"].get_string().value());
|
||||
|
||||
// Handle optional fields
|
||||
auto company_result = doc["company"];
|
||||
if (!company_result.is_null()) {
|
||||
user.company = std::string(company_result.get_string().value());
|
||||
}
|
||||
|
||||
auto location_result = doc["location"];
|
||||
if (!location_result.is_null()) {
|
||||
user.location = std::string(location_result.get_string().value());
|
||||
}
|
||||
|
||||
user.public_repos = doc["public_repos"].get_int64().value();
|
||||
user.followers = doc["followers"].get_int64().value();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
// Manual serialization function (the old way)
|
||||
std::string serialize_github_user(const GitHubUser& user) {
|
||||
std::ostringstream json;
|
||||
json << "{";
|
||||
json << "\"login\":\"" << user.login << "\",";
|
||||
json << "\"id\":" << user.id << ",";
|
||||
json << "\"name\":\"" << user.name << "\",";
|
||||
|
||||
if (user.company.has_value()) {
|
||||
json << "\"company\":\"" << *user.company << "\",";
|
||||
} else {
|
||||
json << "\"company\":null,";
|
||||
}
|
||||
|
||||
if (user.location.has_value()) {
|
||||
json << "\"location\":\"" << *user.location << "\",";
|
||||
} else {
|
||||
json << "\"location\":null,";
|
||||
}
|
||||
|
||||
json << "\"public_repos\":" << user.public_repos << ",";
|
||||
json << "\"followers\":" << user.followers;
|
||||
json << "}";
|
||||
|
||||
return json.str();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "📚 Legacy Approach - Deserialization Performance Benchmark\n";
|
||||
std::cout << "=========================================================\n\n";
|
||||
|
||||
// Fetch data from GitHub API
|
||||
auto response = cpr::Get(
|
||||
cpr::Url{"https://api.github.com/users/lemire"},
|
||||
cpr::Header{{"User-Agent", "simdjson-benchmark"}}
|
||||
);
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "❌ Failed to fetch data: HTTP " << response.status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
auto user = parse_github_user(response.text);
|
||||
}
|
||||
|
||||
// Benchmark
|
||||
const int iterations = 10000;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
auto user = parse_github_user(response.text);
|
||||
// Prevent optimization
|
||||
if (i == 0) {
|
||||
std::cout << "Parsing: " << user.name << " (@" << user.login << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
// Calculate performance metrics
|
||||
double time_per_parse = duration.count() / static_cast<double>(iterations);
|
||||
double bytes_per_parse = response.text.size();
|
||||
double gb_per_second = (bytes_per_parse * iterations) / (duration.count() * 1000.0);
|
||||
|
||||
std::cout << "📊 Deserialization Performance Results:\n";
|
||||
std::cout << " • JSON size: " << bytes_per_parse << " bytes\n";
|
||||
std::cout << " • Iterations: " << iterations << "\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
std::cout << " • Time per deserialization: " << std::fixed << std::setprecision(2) << time_per_parse << " μs\n";
|
||||
std::cout << " • Deserialization speed: " << std::fixed << std::setprecision(2) << gb_per_second << " GB/s\n";
|
||||
|
||||
// Now benchmark serialization
|
||||
std::cout << "\n📝 Serialization Performance Benchmark\n";
|
||||
std::cout << "=====================================\n\n";
|
||||
|
||||
// Parse once to get a user object
|
||||
auto user = parse_github_user(response.text);
|
||||
|
||||
// Warm up serialization
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
auto json_str = serialize_github_user(user);
|
||||
}
|
||||
|
||||
// Benchmark serialization
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
auto json_str = serialize_github_user(user);
|
||||
// Prevent optimization
|
||||
if (i == 0) {
|
||||
std::cout << "Serialized JSON size: " << json_str.length() << " bytes\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
end = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
// Calculate serialization performance metrics
|
||||
time_per_parse = duration.count() / static_cast<double>(iterations);
|
||||
double serialized_size = serialize_github_user(user).length();
|
||||
gb_per_second = (serialized_size * iterations) / (duration.count() * 1000.0);
|
||||
|
||||
std::cout << "📊 Serialization Performance Results:\n";
|
||||
std::cout << " • JSON size: " << serialized_size << " bytes\n";
|
||||
std::cout << " • Iterations: " << iterations << "\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
std::cout << " • Time per serialization: " << std::fixed << std::setprecision(2) << time_per_parse << " μs\n";
|
||||
std::cout << " • Serialization speed: " << std::fixed << std::setprecision(2) << gb_per_second << " GB/s\n";
|
||||
std::cout << "\n⚠️ Note: Manual serialization with string concatenation\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Modern approach - C++26 reflection with simdjson
|
||||
// Compile with Bloomberg clang fork:
|
||||
// clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 ...
|
||||
#include <simdjson.h>
|
||||
#include <simdjson/convert.h>
|
||||
#include <cpr/cpr.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
||||
// 🎯 JUST DECLARE YOUR STRUCT - THAT'S IT!
|
||||
// No serialization code needed with C++26 reflection
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
int64_t id;
|
||||
std::string name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> location;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
};
|
||||
// NO BOILERPLATE CODE NEEDED! 🎉
|
||||
|
||||
int main() {
|
||||
std::cout << "✨ Modern Approach - C++26 Reflection\n";
|
||||
std::cout << "=====================================\n\n";
|
||||
|
||||
// Fetch data from GitHub API
|
||||
auto response = cpr::Get(
|
||||
cpr::Url{"https://api.github.com/users/lemire"},
|
||||
cpr::Header{{"User-Agent", "simdjson-modern-demo"}}
|
||||
);
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "❌ Failed to fetch data: HTTP " << response.status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
// ✨ THE MAGIC - Just one line, no boilerplate!
|
||||
// C++26 reflection handles everything automatically
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
|
||||
// Display results
|
||||
std::cout << "GitHub User: " << user.name << " (@" << user.login << ")\n";
|
||||
std::cout << "ID: " << user.id << "\n";
|
||||
if (user.company) std::cout << "Company: " << *user.company << "\n";
|
||||
if (user.location) std::cout << "Location: " << *user.location << "\n";
|
||||
std::cout << "Public Repos: " << user.public_repos << "\n";
|
||||
std::cout << "Followers: " << user.followers << "\n";
|
||||
|
||||
std::cout << "\n🚀 That's it! No manual parsing code needed!\n";
|
||||
std::cout << " C++26 reflection generates everything automatically.\n";
|
||||
|
||||
} catch (const simdjson::simdjson_error& e) {
|
||||
std::cerr << "❌ Parsing error: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Modern approach with performance measurement - C++26 reflection
|
||||
#include <simdjson.h>
|
||||
#include <simdjson/convert.h>
|
||||
#include <cpr/cpr.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
// Just declare your struct - reflection handles the rest!
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
int64_t id;
|
||||
std::string name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> location;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "✨ Modern Approach - Deserialization Performance Benchmark (C++26)\n";
|
||||
std::cout << "================================================================\n\n";
|
||||
|
||||
// Fetch data from GitHub API
|
||||
auto response = cpr::Get(
|
||||
cpr::Url{"https://api.github.com/users/lemire"},
|
||||
cpr::Header{{"User-Agent", "simdjson-benchmark"}}
|
||||
);
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "❌ Failed to fetch data: HTTP " << response.status_code << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Warm up
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
}
|
||||
|
||||
// Benchmark
|
||||
const int iterations = 10000;
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
// Prevent optimization
|
||||
if (i == 0) {
|
||||
std::cout << "Parsing: " << user.name << " (@" << user.login << ")\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
// Calculate performance metrics
|
||||
double time_per_parse = duration.count() / static_cast<double>(iterations);
|
||||
double bytes_per_parse = response.text.size();
|
||||
double gb_per_second = (bytes_per_parse * iterations) / (duration.count() * 1000.0);
|
||||
|
||||
std::cout << "📊 Deserialization Performance Results:\n";
|
||||
std::cout << " • JSON size: " << bytes_per_parse << " bytes\n";
|
||||
std::cout << " • Iterations: " << iterations << "\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
std::cout << " • Time per deserialization: " << std::fixed << std::setprecision(2) << time_per_parse << " μs\n";
|
||||
std::cout << " • Deserialization speed: " << std::fixed << std::setprecision(2) << gb_per_second << " GB/s\n";
|
||||
|
||||
std::cout << "\n🚀 Same performance, just ONE line of deserialization code!\n";
|
||||
|
||||
// Serialization note
|
||||
std::cout << "\n📝 Serialization with Reflection\n";
|
||||
std::cout << "================================\n\n";
|
||||
std::cout << "⚠️ NOTE: simdjson doesn't yet support reflection-based serialization.\n";
|
||||
std::cout << " The `simdjson::to` function is not yet implemented.\n";
|
||||
std::cout << " This is a future enhancement that would provide:\n";
|
||||
std::cout << " • One-line serialization: simdjson::to<std::string>(user)\n";
|
||||
std::cout << " • Automatic JSON generation from C++ structs\n";
|
||||
std::cout << " • No manual string building required\n";
|
||||
std::cout << "\n";
|
||||
std::cout << " For now, serialization still requires manual implementation,\n";
|
||||
std::cout << " but deserialization is fully automated with reflection! 🎉\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "simdjson.h"
|
||||
#include <cpr/cpr.h>
|
||||
|
||||
struct GitHubUser {
|
||||
std::string login;
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> company;
|
||||
std::optional<std::string> blog;
|
||||
std::optional<std::string> location;
|
||||
std::optional<std::string> email;
|
||||
std::optional<std::string> bio;
|
||||
int64_t public_repos;
|
||||
int64_t followers;
|
||||
int64_t following;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "🔄 C++26 Reflection: Complete JSON Round-Trip Demo\n";
|
||||
std::cout << "================================================\n\n";
|
||||
|
||||
// Step 1: Fetch real data from GitHub
|
||||
std::cout << "📡 Fetching GitHub user data...\n";
|
||||
auto response = cpr::Get(cpr::Url{"https://api.github.com/users/simdjson"});
|
||||
|
||||
if (response.status_code != 200) {
|
||||
std::cerr << "Error: Failed to fetch data\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "✅ Received " << response.text.length() << " bytes of JSON\n\n";
|
||||
|
||||
// Step 2: Deserialize with ONE LINE
|
||||
std::cout << "📥 DESERIALIZATION (JSON → Struct)\n";
|
||||
std::cout << "Code: GitHubUser user = simdjson::from(simdjson::padded_string(response.text));\n\n";
|
||||
|
||||
GitHubUser user = simdjson::from(simdjson::padded_string(response.text));
|
||||
|
||||
// Show the data we parsed
|
||||
std::cout << "Parsed data:\n";
|
||||
std::cout << " • Login: " << user.login << "\n";
|
||||
std::cout << " • Name: " << (user.name.has_value() ? *user.name : "<not set>") << "\n";
|
||||
std::cout << " • Company: " << (user.company.has_value() ? *user.company : "<not set>") << "\n";
|
||||
std::cout << " • Location: " << (user.location.has_value() ? *user.location : "<not set>") << "\n";
|
||||
std::cout << " • Bio: " << (user.bio.has_value() ? *user.bio : "<not set>") << "\n";
|
||||
std::cout << " • Repos: " << user.public_repos << "\n";
|
||||
std::cout << " • Followers: " << user.followers << "\n\n";
|
||||
|
||||
// Step 3: Modify the data
|
||||
std::cout << "✏️ Modifying data...\n";
|
||||
user.followers += 1000; // Wishful thinking!
|
||||
user.name = "simdjson - JSON at the speed of light"; // Set a name
|
||||
user.bio = user.bio.value_or("") + " (Now with C++26 reflection!)";
|
||||
std::cout << " • Added 1000 followers (we can dream!)\n";
|
||||
std::cout << " • Set organization name\n";
|
||||
std::cout << " • Updated bio\n\n";
|
||||
|
||||
// Step 4: Serialize back to JSON with ONE LINE
|
||||
std::cout << "📤 SERIALIZATION (Struct → JSON)\n";
|
||||
std::cout << "Code: std::string json = simdjson::builder::to_json_string(user);\n\n";
|
||||
|
||||
auto json_result = simdjson::builder::to_json_string(user);
|
||||
if (json_result.error()) {
|
||||
std::cerr << "Serialization error!\n";
|
||||
return 1;
|
||||
}
|
||||
std::string json = json_result.value();
|
||||
|
||||
std::cout << "Generated JSON (" << json.length() << " bytes):\n";
|
||||
// Pretty print first 200 chars
|
||||
if (json.length() > 200) {
|
||||
std::cout << json.substr(0, 200) << "...\n\n";
|
||||
} else {
|
||||
std::cout << json << "\n\n";
|
||||
}
|
||||
|
||||
// Step 5: Verify round-trip
|
||||
std::cout << "🔄 ROUND-TRIP VERIFICATION\n";
|
||||
std::cout << "Parsing our generated JSON back...\n";
|
||||
|
||||
GitHubUser user2 = simdjson::from(simdjson::padded_string(json));
|
||||
|
||||
std::cout << "✅ Round-trip successful!\n";
|
||||
std::cout << " • Original followers: " << user.followers << "\n";
|
||||
std::cout << " • Parsed followers: " << user2.followers << "\n";
|
||||
std::cout << " • Name set correctly: " << (user2.name.has_value() && *user2.name == *user.name ? "YES" : "NO") << "\n";
|
||||
std::cout << " • Bio preserved: " << (user2.bio.has_value() ? "YES" : "NO") << "\n\n";
|
||||
|
||||
std::cout << "🎉 That's it! Two lines of code for complete JSON handling:\n";
|
||||
std::cout << " • Deserialization: simdjson::from(...)\n";
|
||||
std::cout << " • Serialization: simdjson::builder::to_json_string(...)\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Complete JSON Round-Trip Demo with C++26 Reflection
|
||||
// Compile with: clang++ -std=c++26 -freflection -DSIMDJSON_STATIC_REFLECTION=1 reflection_roundtrip_demo.cpp ../singleheader/simdjson.cpp -o reflection_roundtrip_demo
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include "simdjson.h"
|
||||
|
||||
// Define our data structure - just a plain struct!
|
||||
struct Product {
|
||||
std::string name;
|
||||
double price;
|
||||
std::vector<std::string> tags;
|
||||
std::optional<std::string> description;
|
||||
bool in_stock;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "🔄 simdjson C++26 Reflection Round-Trip Demo\n";
|
||||
std::cout << "============================================\n\n";
|
||||
|
||||
// Original JSON data
|
||||
const char* json_data = R"({
|
||||
"name": "High-Performance JSON Parser",
|
||||
"price": 0.0,
|
||||
"tags": ["C++", "performance", "json", "reflection"],
|
||||
"description": "The fastest JSON parser with C++26 reflection support",
|
||||
"in_stock": true
|
||||
})";
|
||||
|
||||
std::cout << "📄 Original JSON:\n" << json_data << "\n\n";
|
||||
|
||||
// Step 1: Deserialize JSON to struct with ONE line
|
||||
std::cout << "📥 Deserializing JSON → Struct...\n";
|
||||
Product product = simdjson::from(simdjson::padded_string(json_data));
|
||||
|
||||
std::cout << "✅ Deserialized successfully!\n";
|
||||
std::cout << " • Name: " << product.name << "\n";
|
||||
std::cout << " • Price: $" << product.price << "\n";
|
||||
std::cout << " • Tags: ";
|
||||
for (const auto& tag : product.tags) {
|
||||
std::cout << tag << " ";
|
||||
}
|
||||
std::cout << "\n • In stock: " << (product.in_stock ? "Yes" : "No") << "\n\n";
|
||||
|
||||
// Step 2: Modify the data
|
||||
std::cout << "✏️ Modifying data...\n";
|
||||
product.price = 99.99; // It's worth something now!
|
||||
product.tags.push_back("C++26");
|
||||
product.description = "Now with serialization support!";
|
||||
std::cout << " • Changed price to $99.99\n";
|
||||
std::cout << " • Added 'C++26' tag\n";
|
||||
std::cout << " • Updated description\n\n";
|
||||
|
||||
// Step 3: Serialize struct back to JSON with ONE line
|
||||
std::cout << "📤 Serializing Struct → JSON...\n";
|
||||
auto json_result = simdjson::builder::to_json_string(product);
|
||||
|
||||
if (json_result.error()) {
|
||||
std::cerr << "❌ Serialization failed!\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string new_json = json_result.value();
|
||||
std::cout << "✅ Serialized successfully!\n\n";
|
||||
|
||||
std::cout << "📄 Generated JSON:\n" << new_json << "\n\n";
|
||||
|
||||
// Step 4: Verify round-trip by parsing again
|
||||
std::cout << "🔍 Verifying round-trip...\n";
|
||||
Product product2 = simdjson::from(simdjson::padded_string(new_json));
|
||||
|
||||
std::cout << "✅ Round-trip successful!\n";
|
||||
std::cout << " • Price preserved: $" << product2.price << "\n";
|
||||
std::cout << " • New tag present: "
|
||||
<< (std::find(product2.tags.begin(), product2.tags.end(), "C++26") != product2.tags.end() ? "Yes" : "No")
|
||||
<< "\n";
|
||||
std::cout << " • Description updated: " << (product2.description.has_value() ? "Yes" : "No") << "\n\n";
|
||||
|
||||
std::cout << "🎉 That's it! Complete JSON handling in just 2 lines:\n";
|
||||
std::cout << " • Deserialize: auto obj = simdjson::from(json);\n";
|
||||
std::cout << " • Serialize: auto json = simdjson::builder::to_json_string(obj);\n\n";
|
||||
|
||||
std::cout << "⚡ No manual parsing, no boilerplate, just pure C++26 magic!\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
#include "simdjson.h"
|
||||
#include <map>
|
||||
|
||||
struct TestData {
|
||||
std::string name;
|
||||
std::vector<int> numbers;
|
||||
std::map<std::string, double> metrics;
|
||||
bool active;
|
||||
std::optional<std::string> description;
|
||||
};
|
||||
|
||||
int main() {
|
||||
std::cout << "⚡ Serialization Performance Benchmark\n";
|
||||
std::cout << "=====================================\n\n";
|
||||
|
||||
// Create test data
|
||||
TestData data{
|
||||
.name = "Performance Test",
|
||||
.numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
.metrics = {{"latency", 1.23}, {"throughput", 456.78}, {"cpu", 89.01}},
|
||||
.active = true,
|
||||
.description = "Testing reflection-based serialization performance"
|
||||
};
|
||||
|
||||
const int iterations = 100000;
|
||||
|
||||
// Benchmark serialization
|
||||
std::cout << "📤 Benchmarking serialization (" << iterations << " iterations)...\n";
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
std::string json;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
auto result = simdjson::builder::to_json_string(data);
|
||||
if (i == 0 && !result.error()) {
|
||||
json = result.value();
|
||||
}
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
std::cout << "\nResults:\n";
|
||||
std::cout << " • Generated JSON size: " << json.length() << " bytes\n";
|
||||
std::cout << " • Total time: " << duration.count() / 1000.0 << " ms\n";
|
||||
|
||||
double time_per_iteration_us = duration.count() / double(iterations);
|
||||
double time_per_iteration_s = time_per_iteration_us / 1000000.0;
|
||||
double bytes_per_second = json.length() / time_per_iteration_s;
|
||||
double mb_per_second = bytes_per_second / (1024.0 * 1024.0);
|
||||
|
||||
std::cout << " • Time per serialization: " << std::fixed << std::setprecision(2) << time_per_iteration_us << " μs\n";
|
||||
std::cout << " • Throughput: " << std::fixed << std::setprecision(2) << mb_per_second << " MB/s\n";
|
||||
|
||||
std::cout << "\nGenerated JSON:\n" << json << "\n\n";
|
||||
|
||||
// Benchmark deserialization for comparison
|
||||
std::cout << "📥 Benchmarking deserialization (for comparison)...\n";
|
||||
|
||||
simdjson::padded_string padded_json(json);
|
||||
start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
TestData parsed = simdjson::from(padded_json);
|
||||
}
|
||||
|
||||
end = std::chrono::high_resolution_clock::now();
|
||||
duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
|
||||
std::cout << "\nResults:\n";
|
||||
time_per_iteration_us = duration.count() / double(iterations);
|
||||
time_per_iteration_s = time_per_iteration_us / 1000000.0;
|
||||
bytes_per_second = json.length() / time_per_iteration_s;
|
||||
mb_per_second = bytes_per_second / (1024.0 * 1024.0);
|
||||
|
||||
std::cout << " • Time per deserialization: " << std::fixed << std::setprecision(2) << time_per_iteration_us << " μs\n";
|
||||
std::cout << " • Throughput: " << std::fixed << std::setprecision(2) << mb_per_second << " MB/s\n";
|
||||
|
||||
std::cout << "\n✅ Both serialization and deserialization work with reflection!\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -53,4 +53,5 @@
|
||||
|
||||
#include "simdjson/dom.h"
|
||||
#include "simdjson/ondemand.h"
|
||||
#include "simdjson/convert.h"
|
||||
#endif // SIMDJSON_H
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#include "simdjson/lsx.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv)
|
||||
#include "simdjson/rvv.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -21,8 +21,6 @@ namespace simdjson {
|
||||
namespace lsx {}
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
namespace lasx {}
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv)
|
||||
namespace rvv {}
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
#include "simdjson/lsx/implementation.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx/implementation.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv)
|
||||
#include "simdjson/rvv/implementation.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
#include "simdjson/lsx/ondemand.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(lasx)
|
||||
#include "simdjson/lasx/ondemand.h"
|
||||
#elif SIMDJSON_BUILTIN_IMPLEMENTATION_IS(rvv)
|
||||
#include "simdjson/rvv/ondemand.h"
|
||||
#else
|
||||
#error Unknown SIMDJSON_BUILTIN_IMPLEMENTATION
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
#ifndef SIMDJSON_CONVERT_H
|
||||
#define SIMDJSON_CONVERT_H
|
||||
#if __cpp_concepts
|
||||
|
||||
#include "simdjson/ondemand.h"
|
||||
#include <optional>
|
||||
#ifdef __cpp_lib_ranges
|
||||
#include <ranges>
|
||||
#endif
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
struct [[nodiscard]] auto_iterator_end {};
|
||||
|
||||
/**
|
||||
* A Wrapper for simdjson_result<ondemand::array_iterator> in order to make it
|
||||
* compatible with ranges (to satisfy std::ranges::input_range).
|
||||
*/
|
||||
struct [[nodiscard]] auto_iterator {
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using type = simdjson_result<ondemand::array_iterator>;
|
||||
using value_type = simdjson_result<ondemand::value>; // type::value_type
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
struct auto_iterator_storage {
|
||||
type m_iter{};
|
||||
mutable value_type m_value{};
|
||||
};
|
||||
|
||||
private:
|
||||
auto_iterator_storage *m_storage = nullptr;
|
||||
|
||||
public:
|
||||
constexpr auto_iterator() noexcept = default;
|
||||
explicit auto_iterator(auto_iterator_storage &storage) noexcept
|
||||
: m_storage{&storage} {};
|
||||
auto_iterator(auto_iterator const &) = default;
|
||||
auto_iterator(auto_iterator &&) = default;
|
||||
auto_iterator &operator=(auto_iterator const &) = default;
|
||||
auto_iterator &operator=(auto_iterator &&) noexcept = default;
|
||||
~auto_iterator() = default;
|
||||
|
||||
reference operator*() const noexcept { return m_storage->m_value; }
|
||||
reference operator*() noexcept { return m_storage->m_value; }
|
||||
|
||||
auto_iterator &operator++() noexcept {
|
||||
++m_storage->m_iter;
|
||||
m_storage->m_value =
|
||||
m_storage->m_iter.at_end() || m_storage->m_iter.error() != SUCCESS
|
||||
? value_type{}
|
||||
: *m_storage->m_iter;
|
||||
return *this;
|
||||
}
|
||||
auto_iterator operator++(int) noexcept {
|
||||
auto_iterator const tmp = *this;
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(auto_iterator const &other) const noexcept {
|
||||
return m_storage == other.m_storage &&
|
||||
m_storage->m_iter == other.m_storage->m_iter;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(auto_iterator_end) const noexcept {
|
||||
return m_storage != nullptr && m_storage->m_iter.at_end();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ParserType = ondemand::parser>
|
||||
struct [[nodiscard]] auto_parser
|
||||
#if __cpp_lib_ranges
|
||||
: std::ranges::view_interface<auto_parser<ParserType>>
|
||||
#endif
|
||||
{
|
||||
using value_type = simdjson_result<ondemand::value>;
|
||||
using size_type = size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type *;
|
||||
using const_pointer = const value_type *;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using iterator = auto_iterator;
|
||||
using const_iterator = auto_iterator; // auto_iterator is already const
|
||||
|
||||
private:
|
||||
ParserType m_parser;
|
||||
ondemand::document m_doc;
|
||||
error_code m_error{SUCCESS};
|
||||
|
||||
// Caching the iterator here:
|
||||
iterator::auto_iterator_storage iter_storage{};
|
||||
|
||||
template <typename T>
|
||||
static constexpr bool is_nothrow_gettable = requires(ondemand::document doc) {
|
||||
{ doc.get<T>() } noexcept;
|
||||
};
|
||||
|
||||
public:
|
||||
// non-pointer constructors:
|
||||
explicit auto_parser(ParserType &&parser, ondemand::document &&doc) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: m_parser{std::move(parser)}, m_doc{std::move(doc)} {}
|
||||
|
||||
explicit auto_parser(ParserType &&parser,
|
||||
padded_string_view const str) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: m_parser{std::move(parser)}, m_doc{}, m_error{SUCCESS} {
|
||||
m_error = m_parser.iterate(str).get(m_doc);
|
||||
}
|
||||
|
||||
explicit auto_parser(padded_string_view const str) noexcept
|
||||
requires(!std::is_pointer_v<ParserType>)
|
||||
: auto_parser{ParserType{}, str} {}
|
||||
|
||||
// pointer constructors:
|
||||
explicit auto_parser(std::remove_pointer_t<ParserType> &parser,
|
||||
ondemand::document &&doc) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: m_parser{&parser}, m_doc{std::move(doc)} {}
|
||||
|
||||
explicit auto_parser(std::remove_pointer_t<ParserType> &parser,
|
||||
padded_string_view const str) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: m_parser{&parser}, m_doc{}, m_error{SUCCESS} {
|
||||
m_error = m_parser->iterate(str).get(m_doc);
|
||||
}
|
||||
|
||||
explicit auto_parser(ParserType parser, ondemand::document &&doc) noexcept
|
||||
requires(std::is_pointer_v<ParserType>)
|
||||
: auto_parser{*parser, std::move(doc)} {}
|
||||
|
||||
auto_parser(auto_parser const &) = delete;
|
||||
auto_parser &operator=(auto_parser const &) = delete;
|
||||
auto_parser(auto_parser &&) noexcept = default;
|
||||
auto_parser &operator=(auto_parser &&) noexcept = default;
|
||||
~auto_parser() = default;
|
||||
|
||||
/// Get the parser
|
||||
[[nodiscard]] std::remove_pointer_t<ParserType> &parser() noexcept {
|
||||
if constexpr (std::is_pointer_v<ParserType>) {
|
||||
return *m_parser;
|
||||
} else {
|
||||
return m_parser;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline simdjson_result<T>
|
||||
result() noexcept(is_nothrow_gettable<T>) {
|
||||
if (m_error != SUCCESS) {
|
||||
return m_error;
|
||||
}
|
||||
// For array and object types, we need to be at the start of the document
|
||||
return m_doc.get<T>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::array>
|
||||
array() noexcept {
|
||||
return result<ondemand::array>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::object>
|
||||
object() noexcept {
|
||||
return result<ondemand::object>();
|
||||
}
|
||||
|
||||
[[nodiscard]] simdjson_inline simdjson_result<ondemand::number>
|
||||
number() noexcept {
|
||||
return result<ondemand::number>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline explicit(false)
|
||||
operator simdjson_result<T>() noexcept(is_nothrow_gettable<T>) {
|
||||
return result<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline explicit(false) operator T() noexcept(false) {
|
||||
if (m_error != SUCCESS) {
|
||||
throw simdjson_error(m_error);
|
||||
}
|
||||
return m_doc.get<T>();
|
||||
}
|
||||
|
||||
// We can't have "operator std::optional<T>" because it would create an
|
||||
// ambiguity for the compiler.
|
||||
// We also cannot have "operator T*" without manual memory management.
|
||||
// We also cannot have "operator T&" without manual memory management either.
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] simdjson_inline std::optional<T>
|
||||
optional() noexcept(is_nothrow_gettable<T>) {
|
||||
if (m_error != SUCCESS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
T value;
|
||||
// For std::optional<T>
|
||||
if (m_doc.get<T>().get(value)) [[unlikely]] {
|
||||
return std::nullopt;
|
||||
}
|
||||
return {std::move(value)};
|
||||
}
|
||||
|
||||
simdjson_inline auto_iterator begin() noexcept {
|
||||
if (m_error != SUCCESS) {
|
||||
// Create an iterator with the error
|
||||
iter_storage.m_iter = iterator::type(m_error);
|
||||
iter_storage.m_value = value_type{};
|
||||
return auto_iterator{iter_storage};
|
||||
}
|
||||
if (iter_storage.m_iter.error() != SUCCESS &&
|
||||
!iter_storage.m_iter.at_end()) {
|
||||
// Try to get the document as an array
|
||||
ondemand::array arr;
|
||||
if(auto error = m_doc.get_array().get(arr); error == SUCCESS) {
|
||||
iter_storage = {.m_iter = iterator::type{arr.begin()},
|
||||
.m_value = iterator::value_type{
|
||||
iter_storage.m_iter.at_end() ||
|
||||
iter_storage.m_iter.error() != SUCCESS
|
||||
? value_type{}
|
||||
: *iter_storage.m_iter}};
|
||||
} else {
|
||||
// If it's not an array, create an error iterator
|
||||
iter_storage.m_iter = iterator::type(error);
|
||||
iter_storage.m_value = value_type{};
|
||||
}
|
||||
}
|
||||
return auto_iterator{iter_storage};
|
||||
}
|
||||
simdjson_inline auto_iterator_end end() noexcept { return {}; }
|
||||
};
|
||||
|
||||
#ifdef __cpp_lib_ranges
|
||||
|
||||
// For C++20, we implement our own pipe operator since range_adaptor_closure is C++23
|
||||
static constexpr struct [[nodiscard]] no_errors_adaptor {
|
||||
|
||||
[[nodiscard]] bool
|
||||
operator()(simdjson_result<ondemand::value> const &val) const noexcept {
|
||||
return val.error() == SUCCESS;
|
||||
}
|
||||
|
||||
template <std::ranges::range Range>
|
||||
auto operator()(Range &&rng) const noexcept {
|
||||
return std::forward<Range>(rng) | std::views::filter(*this);
|
||||
}
|
||||
} no_errors;
|
||||
|
||||
template <typename T = void>
|
||||
struct [[nodiscard]] to_adaptor {
|
||||
|
||||
/// Convert to T
|
||||
[[nodiscard]] T
|
||||
operator()(simdjson_result<ondemand::value> &val) const noexcept {
|
||||
return val.get<T>();
|
||||
}
|
||||
|
||||
/// Make it an adaptor
|
||||
template <std::ranges::range Range>
|
||||
auto operator()(Range &&rng) const noexcept {
|
||||
return std::forward<Range>(rng) | no_errors | std::views::transform(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input string into any object if possible.
|
||||
*/
|
||||
auto operator()(padded_string_view const str) const noexcept {
|
||||
return auto_parser{str};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the input using the specified parser into any object if possible.
|
||||
*/
|
||||
auto operator()(ondemand::parser &parser,
|
||||
padded_string_view const str) const noexcept {
|
||||
return auto_parser<ondemand::parser *>{parser, str};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> static constexpr to_adaptor<T> to{};
|
||||
|
||||
static constexpr to_adaptor<> from{};
|
||||
|
||||
template <typename T = void>
|
||||
using as = to_adaptor<T>;
|
||||
|
||||
// For C++20 ranges without range_adaptor_closure, we need to define pipe operators
|
||||
template <std::ranges::range Range>
|
||||
inline auto operator|(Range&& range, const no_errors_adaptor& adaptor) {
|
||||
return adaptor(std::forward<Range>(range));
|
||||
}
|
||||
|
||||
template <std::ranges::range Range, typename T>
|
||||
inline auto operator|(Range&& range, const to_adaptor<T>& adaptor) {
|
||||
return adaptor(std::forward<Range>(range));
|
||||
}
|
||||
|
||||
#endif // __cpp_lib_ranges
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // __cpp_concepts
|
||||
#endif // SIMDJSON_CONVERT_H
|
||||
@@ -3,8 +3,8 @@
|
||||
#define SIMDJSON_SERIALIZATION_INL_H
|
||||
|
||||
#include "simdjson/dom/base.h"
|
||||
#include "simdjson/dom/serialization.h"
|
||||
#include "simdjson/dom/parser.h"
|
||||
#include "simdjson/dom/serialization.h"
|
||||
#include "simdjson/internal/tape_type.h"
|
||||
|
||||
#include "simdjson/dom/array-inl.h"
|
||||
@@ -16,7 +16,9 @@
|
||||
namespace simdjson {
|
||||
namespace dom {
|
||||
inline bool parser::print_json(std::ostream &os) const noexcept {
|
||||
if (!valid) { return false; }
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(doc.root());
|
||||
std::string_view answer = sb.str();
|
||||
@@ -24,37 +26,51 @@ inline bool parser::print_json(std::ostream &os) const noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::element value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
inline std::ostream &operator<<(std::ostream &out,
|
||||
simdjson::dom::element value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::element> x) {
|
||||
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
|
||||
return (out << x.value());
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::element> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson::simdjson_error(x.error());
|
||||
}
|
||||
return (out << x.value());
|
||||
}
|
||||
#endif
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::array value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::array> x) {
|
||||
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
|
||||
return (out << x.value());
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::array> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson::simdjson_error(x.error());
|
||||
}
|
||||
return (out << x.value());
|
||||
}
|
||||
#endif
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::object value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
inline std::ostream &operator<<(std::ostream &out,
|
||||
simdjson::dom::object value) {
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(value);
|
||||
return (out << sb.str());
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::object> x) {
|
||||
if (x.error()) { throw simdjson::simdjson_error(x.error()); }
|
||||
return (out << x.value());
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::object> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson::simdjson_error(x.error());
|
||||
}
|
||||
return (out << x.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -69,8 +85,9 @@ namespace {
|
||||
* We expect that most compilers will use 8 bytes for this data structure.
|
||||
**/
|
||||
struct escape_sequence {
|
||||
uint8_t length;
|
||||
const char string[7]; // technically, we only ever need 6 characters, we pad to 8
|
||||
uint8_t length;
|
||||
const char
|
||||
string[7]; // technically, we only ever need 6 characters, we pad to 8
|
||||
};
|
||||
/**@private
|
||||
* This converts a signed integer into a character sequence.
|
||||
@@ -86,7 +103,7 @@ static char *fast_itoa(char *output, int64_t value) noexcept {
|
||||
char buffer[20];
|
||||
uint64_t value_positive;
|
||||
// In general, negating a signed integer is unsafe.
|
||||
if(value < 0) {
|
||||
if (value < 0) {
|
||||
*output++ = '-';
|
||||
// Doing value_positive = -value; while avoiding
|
||||
// undefined behavior warnings.
|
||||
@@ -105,7 +122,7 @@ static char *fast_itoa(char *output, int64_t value) noexcept {
|
||||
// A faster approach is possible if we expect large integers:
|
||||
// unroll the loop (work in 100s, 1000s) and use some kind of
|
||||
// memoization.
|
||||
while(value_positive >= 10) {
|
||||
while (value_positive >= 10) {
|
||||
*write_pointer-- = char('0' + (value_positive % 10));
|
||||
value_positive /= 10;
|
||||
}
|
||||
@@ -131,7 +148,7 @@ static char *fast_itoa(char *output, uint64_t value) noexcept {
|
||||
// A faster approach is possible if we expect large integers:
|
||||
// unroll the loop (work in 100s, 1000s) and use some kind of
|
||||
// memoization.
|
||||
while(value >= 10) {
|
||||
while (value >= 10) {
|
||||
*write_pointer-- = char('0' + (value % 10));
|
||||
value /= 10;
|
||||
};
|
||||
@@ -141,7 +158,6 @@ static char *fast_itoa(char *output, uint64_t value) noexcept {
|
||||
return output + len;
|
||||
}
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
namespace internal {
|
||||
|
||||
@@ -149,193 +165,208 @@ namespace internal {
|
||||
* Minifier/formatter code.
|
||||
**/
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::number(uint64_t x) {
|
||||
char number_buffer[24];
|
||||
char *newp = fast_itoa(number_buffer, x);
|
||||
buffer.insert(buffer.end(), number_buffer, newp);
|
||||
chars(number_buffer, newp);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::number(int64_t x) {
|
||||
char number_buffer[24];
|
||||
char *newp = fast_itoa(number_buffer, x);
|
||||
buffer.insert(buffer.end(), number_buffer, newp);
|
||||
chars(number_buffer, newp);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::number(double x) {
|
||||
char number_buffer[24];
|
||||
// Currently, passing the nullptr to the second argument is
|
||||
// safe because our implementation does not check the second
|
||||
// argument.
|
||||
char *newp = internal::to_chars(number_buffer, nullptr, x);
|
||||
buffer.insert(buffer.end(), number_buffer, newp);
|
||||
chars(number_buffer, newp);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::start_array() { one_char('['); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::start_array() {
|
||||
one_char('[');
|
||||
}
|
||||
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::end_array() {
|
||||
one_char(']');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::end_array() { one_char(']'); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::start_object() {
|
||||
one_char('{');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::start_object() { one_char('{'); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::end_object() {
|
||||
one_char('}');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::end_object() { one_char('}'); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::comma() {
|
||||
one_char(',');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::comma() { one_char(','); }
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::true_atom() {
|
||||
const char * s = "true";
|
||||
buffer.insert(buffer.end(), s, s + 4);
|
||||
const char *s = "true";
|
||||
chars(s, s + 4);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::false_atom() {
|
||||
const char * s = "false";
|
||||
buffer.insert(buffer.end(), s, s + 5);
|
||||
const char *s = "false";
|
||||
chars(s, s + 5);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::null_atom() {
|
||||
const char * s = "null";
|
||||
buffer.insert(buffer.end(), s, s + 4);
|
||||
const char *s = "null";
|
||||
chars(s, s + 4);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::one_char(char c) { buffer.push_back(c); }
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::one_char(char c) {
|
||||
buffer.push_back(c);
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::key(std::string_view unescaped) {
|
||||
template <class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::chars(const char *begin,
|
||||
const char *end) {
|
||||
buffer.append(begin, end);
|
||||
}
|
||||
|
||||
template <class formatter>
|
||||
simdjson_inline void
|
||||
base_formatter<formatter>::key(std::string_view unescaped) {
|
||||
string(unescaped);
|
||||
one_char(':');
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
simdjson_inline void base_formatter<formatter>::string(std::string_view unescaped) {
|
||||
template <class formatter>
|
||||
simdjson_inline void
|
||||
base_formatter<formatter>::string(std::string_view unescaped) {
|
||||
one_char('\"');
|
||||
size_t i = 0;
|
||||
// Fast path for the case where we have no control character, no ", and no backslash.
|
||||
// This should include most keys.
|
||||
// Fast path for the case where we have no control character, no ", and no
|
||||
// backslash. This should include most keys.
|
||||
//
|
||||
// We would like to use 'bool' but some compilers take offense to bitwise operation
|
||||
// with bool types.
|
||||
constexpr static char needs_escaping[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
for(;i + 8 <= unescaped.length(); i += 8) {
|
||||
// We would like to use 'bool' but some compilers take offense to bitwise
|
||||
// operation with bool types.
|
||||
constexpr static char needs_escaping[] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
for (; i + 8 <= unescaped.length(); i += 8) {
|
||||
// Poor's man vectorization. This could get much faster if we used SIMD.
|
||||
//
|
||||
// It is not the case that replacing '|' with '||' would be neutral performance-wise.
|
||||
if(needs_escaping[uint8_t(unescaped[i])] | needs_escaping[uint8_t(unescaped[i+1])]
|
||||
| needs_escaping[uint8_t(unescaped[i+2])] | needs_escaping[uint8_t(unescaped[i+3])]
|
||||
| needs_escaping[uint8_t(unescaped[i+4])] | needs_escaping[uint8_t(unescaped[i+5])]
|
||||
| needs_escaping[uint8_t(unescaped[i+6])] | needs_escaping[uint8_t(unescaped[i+7])]
|
||||
) { break; }
|
||||
// It is not the case that replacing '|' with '||' would be neutral
|
||||
// performance-wise.
|
||||
if (needs_escaping[uint8_t(unescaped[i])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 1])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 2])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 3])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 4])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 5])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 6])] |
|
||||
needs_escaping[uint8_t(unescaped[i + 7])]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(;i < unescaped.length(); i++) {
|
||||
if(needs_escaping[uint8_t(unescaped[i])]) { break; }
|
||||
for (; i < unescaped.length(); i++) {
|
||||
if (needs_escaping[uint8_t(unescaped[i])]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The following is also possible and omits a 256-byte table, but it is slower:
|
||||
// for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F)
|
||||
// The following is also possible and omits a 256-byte table, but it is
|
||||
// slower: for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F)
|
||||
// && (unescaped[i] != '\"') && (unescaped[i] != '\\'); i++) {}
|
||||
|
||||
// At least for long strings, the following should be fast. We could
|
||||
// do better by integrating the checks and the insertion.
|
||||
buffer.insert(buffer.end(), unescaped.data(), unescaped.data() + i);
|
||||
chars(unescaped.data(), unescaped.data() + i);
|
||||
// We caught a control character if we enter this loop (slow).
|
||||
// Note that we are do not restart from the beginning, but rather we continue
|
||||
// from the point where we encountered something that requires escaping.
|
||||
for (; i < unescaped.length(); i++) {
|
||||
switch (unescaped[i]) {
|
||||
case '\"':
|
||||
{
|
||||
const char * s = "\\\"";
|
||||
buffer.insert(buffer.end(), s, s + 2);
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
{
|
||||
const char * s = "\\\\";
|
||||
buffer.insert(buffer.end(), s, s + 2);
|
||||
}
|
||||
break;
|
||||
case '\"': {
|
||||
const char *s = "\\\"";
|
||||
chars(s, s + 2);
|
||||
} break;
|
||||
case '\\': {
|
||||
const char *s = "\\\\";
|
||||
chars(s, s + 2);
|
||||
} break;
|
||||
default:
|
||||
if (uint8_t(unescaped[i]) <= 0x1F) {
|
||||
// If packed, this uses 8 * 32 bytes.
|
||||
// Note that we expect most compilers to embed this code in the data
|
||||
// section.
|
||||
constexpr static escape_sequence escaped[32] = {
|
||||
{6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"},
|
||||
{6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"},
|
||||
{2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"},
|
||||
{2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"},
|
||||
{6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"},
|
||||
{6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"},
|
||||
{6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"},
|
||||
{6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}};
|
||||
{6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"},
|
||||
{6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"},
|
||||
{2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"},
|
||||
{2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"},
|
||||
{6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"},
|
||||
{6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"},
|
||||
{6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"},
|
||||
{6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}};
|
||||
auto u = escaped[uint8_t(unescaped[i])];
|
||||
buffer.insert(buffer.end(), u.string, u.string + u.length);
|
||||
chars(u.string, u.string + u.length);
|
||||
} else {
|
||||
one_char(unescaped[i]);
|
||||
}
|
||||
} // switch
|
||||
} // for
|
||||
} // for
|
||||
one_char('\"');
|
||||
}
|
||||
|
||||
|
||||
template<class formatter>
|
||||
inline void base_formatter<formatter>::clear() {
|
||||
template <class formatter> inline void base_formatter<formatter>::clear() {
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
template<class formatter>
|
||||
template <class formatter>
|
||||
simdjson_inline std::string_view base_formatter<formatter>::str() const {
|
||||
return std::string_view(buffer.data(), buffer.size());
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
simdjson_inline void mini_formatter::print_newline() {
|
||||
return;
|
||||
}
|
||||
simdjson_inline void mini_formatter::print_newline() { return; }
|
||||
|
||||
simdjson_inline void mini_formatter::print_indents(size_t depth) {
|
||||
(void)depth;
|
||||
return;
|
||||
(void)depth;
|
||||
return;
|
||||
}
|
||||
|
||||
simdjson_inline void mini_formatter::print_space() {
|
||||
return;
|
||||
}
|
||||
simdjson_inline void mini_formatter::print_space() { return; }
|
||||
|
||||
simdjson_inline void pretty_formatter::print_newline() {
|
||||
one_char('\n');
|
||||
}
|
||||
simdjson_inline void pretty_formatter::print_newline() { one_char('\n'); }
|
||||
|
||||
simdjson_inline void pretty_formatter::print_indents(size_t depth) {
|
||||
if(this->indent_step <= 0) {
|
||||
return;
|
||||
}
|
||||
for(size_t i = 0; i < this->indent_step * depth; i++) {
|
||||
one_char(' ');
|
||||
}
|
||||
if (this->indent_step <= 0) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < this->indent_step * depth; i++) {
|
||||
one_char(' ');
|
||||
}
|
||||
}
|
||||
|
||||
simdjson_inline void pretty_formatter::print_space() {
|
||||
one_char(' ');
|
||||
}
|
||||
simdjson_inline void pretty_formatter::print_space() { one_char(' '); }
|
||||
|
||||
/***
|
||||
* String building code.
|
||||
@@ -514,7 +545,8 @@ inline void string_builder<serializer>::append(simdjson::dom::array value) {
|
||||
}
|
||||
|
||||
template <class serializer>
|
||||
simdjson_inline void string_builder<serializer>::append(simdjson::dom::key_value_pair kv) {
|
||||
simdjson_inline void
|
||||
string_builder<serializer>::append(simdjson::dom::key_value_pair kv) {
|
||||
format.key(kv.key);
|
||||
append(kv.value);
|
||||
}
|
||||
@@ -529,7 +561,6 @@ simdjson_inline std::string_view string_builder<serializer>::str() const {
|
||||
return format.str();
|
||||
}
|
||||
|
||||
|
||||
} // namespace internal
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include "simdjson/dom/element.h"
|
||||
#include "simdjson/dom/object.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
/**
|
||||
@@ -16,8 +14,7 @@ namespace simdjson {
|
||||
*/
|
||||
namespace internal {
|
||||
|
||||
template<class formatter>
|
||||
class base_formatter {
|
||||
template <class formatter> class base_formatter {
|
||||
public:
|
||||
/** Add a comma **/
|
||||
simdjson_inline void comma();
|
||||
@@ -56,24 +53,76 @@ public:
|
||||
/** Prints one character **/
|
||||
simdjson_inline void one_char(char c);
|
||||
|
||||
/** Prints characters in [begin, end) verbatim. **/
|
||||
simdjson_inline void chars(const char *begin, const char *end);
|
||||
|
||||
simdjson_inline void call_print_newline() {
|
||||
static_cast<formatter*>(this)->print_newline();
|
||||
static_cast<formatter *>(this)->print_newline();
|
||||
}
|
||||
|
||||
simdjson_inline void call_print_indents(size_t depth) {
|
||||
static_cast<formatter*>(this)->print_indents(depth);
|
||||
static_cast<formatter *>(this)->print_indents(depth);
|
||||
}
|
||||
|
||||
simdjson_inline void call_print_space() {
|
||||
static_cast<formatter*>(this)->print_space();
|
||||
static_cast<formatter *>(this)->print_space();
|
||||
}
|
||||
|
||||
protected:
|
||||
// implementation details (subject to change)
|
||||
/** Backing buffer **/
|
||||
std::vector<char> buffer{}; // not ideal!
|
||||
};
|
||||
struct vector_with_small_buffer {
|
||||
vector_with_small_buffer() = default;
|
||||
~vector_with_small_buffer() { free_buffer(); }
|
||||
|
||||
vector_with_small_buffer(const vector_with_small_buffer &) = delete;
|
||||
vector_with_small_buffer &
|
||||
operator=(const vector_with_small_buffer &) = delete;
|
||||
|
||||
void clear() {
|
||||
size = 0;
|
||||
capacity = StaticCapacity;
|
||||
free_buffer();
|
||||
buffer = array;
|
||||
}
|
||||
|
||||
simdjson_inline void push_back(char c) {
|
||||
if (capacity < size + 1)
|
||||
grow(capacity * 2);
|
||||
buffer[size++] = c;
|
||||
}
|
||||
|
||||
simdjson_inline void append(const char *begin, const char *end) {
|
||||
const size_t new_size = size + (end - begin);
|
||||
if (capacity < new_size)
|
||||
// std::max(new_size, capacity * 2); is broken in tests on Windows
|
||||
grow(new_size < capacity * 2 ? capacity * 2 : new_size);
|
||||
std::copy(begin, end, buffer + size);
|
||||
size = new_size;
|
||||
}
|
||||
|
||||
std::string_view str() const { return std::string_view(buffer, size); }
|
||||
|
||||
private:
|
||||
void free_buffer() {
|
||||
if (buffer != array)
|
||||
delete[] buffer;
|
||||
}
|
||||
void grow(size_t new_capacity) {
|
||||
auto new_buffer = new char[new_capacity];
|
||||
std::copy(buffer, buffer + size, new_buffer);
|
||||
free_buffer();
|
||||
buffer = new_buffer;
|
||||
capacity = new_capacity;
|
||||
}
|
||||
|
||||
static const size_t StaticCapacity = 64;
|
||||
char array[StaticCapacity];
|
||||
char *buffer = array;
|
||||
size_t size = 0;
|
||||
size_t capacity = StaticCapacity;
|
||||
} buffer{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @private This is the class that we expect to use with the string_builder
|
||||
@@ -111,8 +160,7 @@ protected:
|
||||
* This is not to be confused with the simdjson::builder::string_builder
|
||||
* which is a different class.
|
||||
*/
|
||||
template <class formatter = mini_formatter>
|
||||
class string_builder {
|
||||
template <class formatter = mini_formatter> class string_builder {
|
||||
public:
|
||||
/** Construct an initially empty builder, would print the empty string **/
|
||||
string_builder() = default;
|
||||
@@ -134,11 +182,12 @@ public:
|
||||
simdjson_inline std::string_view str() const;
|
||||
/** Append a key_value_pair to the builder (to be printed) **/
|
||||
simdjson_inline void append(simdjson::dom::key_value_pair value);
|
||||
|
||||
private:
|
||||
formatter format{};
|
||||
};
|
||||
|
||||
} // internal
|
||||
} // namespace internal
|
||||
|
||||
namespace dom {
|
||||
|
||||
@@ -147,33 +196,43 @@ namespace dom {
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The element.
|
||||
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
|
||||
* @throw if there is an error with the underlying output stream. simdjson
|
||||
* itself will not throw.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::element value);
|
||||
inline std::ostream &operator<<(std::ostream &out,
|
||||
simdjson::dom::element value);
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::element> x);
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::element> x);
|
||||
#endif
|
||||
/**
|
||||
* Print JSON to an output stream.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The array.
|
||||
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
|
||||
* @throw if there is an error with the underlying output stream. simdjson
|
||||
* itself will not throw.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::array value);
|
||||
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value);
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::array> x);
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::array> x);
|
||||
#endif
|
||||
/**
|
||||
* Print JSON to an output stream.
|
||||
*
|
||||
* @param out The output stream.
|
||||
* @param value The object.
|
||||
* @throw if there is an error with the underlying output stream. simdjson itself will not throw.
|
||||
* @throw if there is an error with the underlying output stream. simdjson
|
||||
* itself will not throw.
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::dom::object value);
|
||||
inline std::ostream &operator<<(std::ostream &out, simdjson::dom::object value);
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<simdjson::dom::object> x);
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &out,
|
||||
simdjson::simdjson_result<simdjson::dom::object> x);
|
||||
#endif
|
||||
} // namespace dom
|
||||
|
||||
@@ -185,47 +244,47 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result<si
|
||||
* cout << to_string(doc) << endl; // prints [1,2,3]
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
std::string to_string(T x) {
|
||||
// in C++, to_string is standard: http://www.cplusplus.com/reference/string/to_string/
|
||||
// Currently minify and to_string are identical but in the future, they may
|
||||
// differ.
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(x);
|
||||
std::string_view answer = sb.str();
|
||||
return std::string(answer.data(), answer.size());
|
||||
template <class T> std::string to_string(T x) {
|
||||
// in C++, to_string is standard:
|
||||
// http://www.cplusplus.com/reference/string/to_string/ Currently minify and
|
||||
// to_string are identical but in the future, they may differ.
|
||||
simdjson::internal::string_builder<> sb;
|
||||
sb.append(x);
|
||||
std::string_view answer = sb.str();
|
||||
return std::string(answer.data(), answer.size());
|
||||
}
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
std::string to_string(simdjson_result<T> x) {
|
||||
if (x.error()) { throw simdjson_error(x.error()); }
|
||||
return to_string(x.value());
|
||||
template <class T> std::string to_string(simdjson_result<T> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson_error(x.error());
|
||||
}
|
||||
return to_string(x.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Minifies a JSON element or document, printing the smallest possible valid JSON.
|
||||
* Minifies a JSON element or document, printing the smallest possible valid
|
||||
* JSON.
|
||||
*
|
||||
* dom::parser parser;
|
||||
* element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
|
||||
* cout << minify(doc) << endl; // prints [1,2,3]
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
std::string minify(T x) {
|
||||
return to_string(x);
|
||||
}
|
||||
template <class T> std::string minify(T x) { return to_string(x); }
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
std::string minify(simdjson_result<T> x) {
|
||||
if (x.error()) { throw simdjson_error(x.error()); }
|
||||
return to_string(x.value());
|
||||
template <class T> std::string minify(simdjson_result<T> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson_error(x.error());
|
||||
}
|
||||
return to_string(x.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Prettifies a JSON element or document, printing the valid JSON with indentation.
|
||||
* Prettifies a JSON element or document, printing the valid JSON with
|
||||
* indentation.
|
||||
*
|
||||
* dom::parser parser;
|
||||
* element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded);
|
||||
@@ -241,23 +300,22 @@ std::string minify(simdjson_result<T> x) {
|
||||
* cout << prettify(doc) << endl;
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
std::string prettify(T x) {
|
||||
simdjson::internal::string_builder<simdjson::internal::pretty_formatter> sb;
|
||||
sb.append(x);
|
||||
std::string_view answer = sb.str();
|
||||
return std::string(answer.data(), answer.size());
|
||||
template <class T> std::string prettify(T x) {
|
||||
simdjson::internal::string_builder<simdjson::internal::pretty_formatter> sb;
|
||||
sb.append(x);
|
||||
std::string_view answer = sb.str();
|
||||
return std::string(answer.data(), answer.size());
|
||||
}
|
||||
|
||||
#if SIMDJSON_EXCEPTIONS
|
||||
template <class T>
|
||||
std::string prettify(simdjson_result<T> x) {
|
||||
if (x.error()) { throw simdjson_error(x.error()); }
|
||||
return to_string(x.value());
|
||||
template <class T> std::string prettify(simdjson_result<T> x) {
|
||||
if (x.error()) {
|
||||
throw simdjson_error(x.error());
|
||||
}
|
||||
return to_string(x.value());
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -122,6 +122,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
|
||||
@@ -36,6 +36,9 @@ simdjson_inline array_iterator &array_iterator::operator++() noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
simdjson_inline bool array_iterator::at_end() const noexcept {
|
||||
return iter.at_end();
|
||||
}
|
||||
} // namespace ondemand
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
} // namespace simdjson
|
||||
@@ -72,7 +75,9 @@ simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterato
|
||||
++(first);
|
||||
return *this;
|
||||
}
|
||||
|
||||
simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator>::at_end() const noexcept {
|
||||
return !first.iter.is_valid() || first.at_end();
|
||||
}
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H
|
||||
@@ -34,7 +34,8 @@ public:
|
||||
*
|
||||
* Part of the std::iterator interface.
|
||||
*/
|
||||
simdjson_inline simdjson_result<value> operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
|
||||
simdjson_inline simdjson_result<value>
|
||||
operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION.
|
||||
/**
|
||||
* Check if we are at the end of the JSON.
|
||||
*
|
||||
@@ -58,6 +59,11 @@ public:
|
||||
*/
|
||||
simdjson_inline array_iterator &operator++() noexcept;
|
||||
|
||||
/**
|
||||
* Check if the array is at the end.
|
||||
*/
|
||||
[[nodiscard]] simdjson_inline bool at_end() const noexcept;
|
||||
|
||||
private:
|
||||
value_iterator iter{};
|
||||
|
||||
@@ -76,7 +82,6 @@ namespace simdjson {
|
||||
|
||||
template<>
|
||||
struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> {
|
||||
public:
|
||||
simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::array_iterator &&value) noexcept; ///< @private
|
||||
simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
|
||||
simdjson_inline simdjson_result() noexcept = default;
|
||||
@@ -89,6 +94,8 @@ public:
|
||||
simdjson_inline bool operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &) const noexcept;
|
||||
simdjson_inline bool operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &) const noexcept;
|
||||
simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> &operator++() noexcept;
|
||||
|
||||
[[nodiscard]] simdjson_inline bool at_end() const noexcept;
|
||||
};
|
||||
|
||||
} // namespace simdjson
|
||||
|
||||
@@ -296,6 +296,11 @@ string_builder& operator<<(string_builder& b, const Z& z) {
|
||||
}
|
||||
} // namespace builder
|
||||
} // namespace SIMDJSON_IMPLEMENTATION
|
||||
// Alias the function template to 'to' in the global namespace
|
||||
template <class Z>
|
||||
simdjson_result<std::string> to_json(const Z &z, size_t initial_capacity = 1024) {
|
||||
return SIMDJSON_IMPLEMENTATION::builder::to_json_string(z, initial_capacity);
|
||||
}
|
||||
} // namespace simdjson
|
||||
#endif // SIMDJSON_STATIC_REFLECTION
|
||||
|
||||
|
||||
@@ -218,6 +218,8 @@ simdjson_inline void json_iterator::assert_valid_position(token_position positio
|
||||
#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
|
||||
SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
|
||||
SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
|
||||
#else
|
||||
(void)position; // Suppress unused parameter warning
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#if SIMDJSON_STATIC_REFLECTION
|
||||
#include <experimental/meta>
|
||||
#include <meta>
|
||||
// #include <static_reflection> // for std::define_static_string - header not available yet
|
||||
#endif
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_westmere 6
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_lsx 7
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_lasx 8
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_rvv 9
|
||||
|
||||
#define SIMDJSON_IMPLEMENTATION_ID_FOR(IMPL) SIMDJSON_CAT(SIMDJSON_IMPLEMENTATION_ID_, IMPL)
|
||||
#define SIMDJSON_IMPLEMENTATION_ID SIMDJSON_IMPLEMENTATION_ID_FOR(SIMDJSON_IMPLEMENTATION)
|
||||
@@ -24,15 +23,6 @@
|
||||
// in which we include them.
|
||||
//
|
||||
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_RVV
|
||||
#define SIMDJSON_IMPLEMENTATION_RVV (SIMDJSON_IS_RISCV64 && SIMDJSON_IS_RVV)
|
||||
#endif
|
||||
#if SIMDJSON_IMPLEMENTATION_RVV && SIMDJSON_IS_RISCV64 && SIMDJSON_IS_RVV
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_RVV 1
|
||||
#else
|
||||
#define SIMDJSON_CAN_ALWAYS_RUN_RVV 0
|
||||
#endif
|
||||
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_ARM64
|
||||
#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64)
|
||||
#endif
|
||||
@@ -138,7 +128,7 @@
|
||||
|
||||
// Default Fallback to on unless a builtin implementation has already been selected.
|
||||
#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64 || SIMDJSON_CAN_ALWAYS_RUN_LSX || SIMDJSON_CAN_ALWAYS_RUN_LASX || SIMDJSON_CAN_ALWAYS_RUN_RVV
|
||||
#if SIMDJSON_CAN_ALWAYS_RUN_ARM64 || SIMDJSON_CAN_ALWAYS_RUN_ICELAKE || SIMDJSON_CAN_ALWAYS_RUN_HASWELL || SIMDJSON_CAN_ALWAYS_RUN_WESTMERE || SIMDJSON_CAN_ALWAYS_RUN_PPC64 || SIMDJSON_CAN_ALWAYS_RUN_LSX || SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||
// if anything at all except fallback can always run, then disable fallback.
|
||||
#define SIMDJSON_IMPLEMENTATION_FALLBACK 0
|
||||
#else
|
||||
@@ -164,8 +154,6 @@
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION lsx
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_LASX
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION lasx
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_RVV
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION rvv
|
||||
#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK
|
||||
#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback
|
||||
#else
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef SIMDJSON_RVV_H
|
||||
#define SIMDJSON_RVV_H
|
||||
|
||||
#include "simdjson/rvv/begin.h"
|
||||
#include "simdjson/generic/amalgamated.h"
|
||||
#include "simdjson/rvv/end.h"
|
||||
|
||||
#endif // SIMDJSON_RVV_H
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef SIMDJSON_RVV_BASE_H
|
||||
#define SIMDJSON_RVV_BASE_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#endif // SIMDJSON_RVV_BASE_H
|
||||
@@ -1,12 +0,0 @@
|
||||
#define SIMDJSON_IMPLEMENTATION rvv
|
||||
|
||||
// include RVV intrinsics and definitions
|
||||
#include "simdjson/rvv/base.h"
|
||||
#include "simdjson/rvv/intrinsics.h"
|
||||
#include "simdjson/rvv/bitmanipulation.h"
|
||||
#include "simdjson/rvv/bitmask.h"
|
||||
#include "simdjson/rvv/numberparsing_defs.h"
|
||||
#include "simdjson/rvv/simd.h"
|
||||
#include "simdjson/rvv/stringparsing_defs.h"
|
||||
|
||||
#define SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT 1
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/rvv/base.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#undef SIMDJSON_SKIP_BACKSLASH_SHORT_CIRCUIT
|
||||
#undef SIMDJSON_IMPLEMENTATION
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef SIMDJSON_RVV_IMPLEMENTATION_H
|
||||
#define SIMDJSON_RVV_IMPLEMENTATION_H
|
||||
|
||||
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#define SIMDJSON_CONDITIONAL_INCLUDE
|
||||
#include "simdjson/base.h"
|
||||
#include "simdjson/implementation.h"
|
||||
#include "simdjson/internal/instruction_set.h"
|
||||
#endif // SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
namespace simdjson {
|
||||
namespace rvv {
|
||||
|
||||
class implementation final: public simdjson::implementation {
|
||||
public:
|
||||
simdjson_inline implementation() : simdjson::implementation("rvv", "RISC-V Vector Extension", 0) {}
|
||||
simdjson_warn_unused error_code create_dom_parser_implementation(
|
||||
size_t capacity,
|
||||
size_t max_depth,
|
||||
std::unique_ptr<internal::dom_parser_implementation>& dst
|
||||
) const noexcept final;
|
||||
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
|
||||
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
|
||||
};
|
||||
|
||||
} // namespace rvv
|
||||
} // namespace simdjson
|
||||
|
||||
#endif // SIMDJSON_RVV_IMPLEMENTATION_H
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef SIMDJSON_RVV_ONDEMAND_H
|
||||
#define SIMDJSON_RVV_ONDEMAND_H
|
||||
|
||||
#include "simdjson/rvv/begin.h"
|
||||
#include "simdjson/generic/ondemand/amalgamated.h"
|
||||
#include "simdjson/rvv/end.h"
|
||||
|
||||
#endif // SIMDJSON_RVV_ONDEMAND_H
|
||||
|
||||
+5
-1
@@ -8,7 +8,11 @@ RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends ca-certificates gnupg \
|
||||
build-essential cmake make python3 zlib1g wget subversion unzip ninja-build git linux-perf && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
RUN git clone --depth=1 --branch p2996 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source
|
||||
ARG CLANG_COMMIT=d77eff1cbd78fd065668acf93b1f5f400d39134d
|
||||
RUN git clone --depth=1 https://github.com/bloomberg/clang-p2996.git /tmp/clang-source && \
|
||||
cd /tmp/clang-source && \
|
||||
git fetch origin $CLANG_COMMIT --depth=1 && \
|
||||
git checkout $CLANG_COMMIT
|
||||
RUN cmake -S /tmp/clang-source/llvm -B /tmp/clang-source/build-llvm -DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_ASSERTIONS=ON \
|
||||
-DLLVM_UNREACHABLE_OPTIMIZE=ON \
|
||||
|
||||
@@ -64,6 +64,7 @@ cmake --build buildreflect --target benchmark_serialization_citm_catalog benchma
|
||||
|
||||
6. Run the tests...
|
||||
```bash
|
||||
cmake --build buildreflect
|
||||
ctest --test-dir buildreflect --output-on-failure
|
||||
```
|
||||
|
||||
|
||||
@@ -22,9 +22,6 @@ rules = """
|
||||
|
||||
We refer your to the HACKING.md file for more information on how the project is organized.
|
||||
|
||||
If you are trying to add a new implementation, you need to edit the amalgamate.py script
|
||||
to add your implementation to the IMPLEMENTATIONS list.
|
||||
|
||||
To help understand the error, here are the rules for including files in simdjson:
|
||||
|
||||
All implementation-specific files, including arm64.h, arm64/implementation.h and
|
||||
@@ -71,7 +68,7 @@ else:
|
||||
RelativeRoot = str # Literal['src','include'] # Literal not supported in Python 3.7 (CI)
|
||||
RELATIVE_ROOTS: List[RelativeRoot] = ['src', 'include' ]
|
||||
Implementation = str # Literal['arm64', 'fallback', 'haswell', 'icelake', 'ppc64', 'westmere', 'lsx', 'lasx'] # Literal not supported in Python 3.7 (CI)
|
||||
IMPLEMENTATIONS: List[Implementation] = [ 'arm64', 'haswell', 'icelake', 'lasx', 'lsx', 'ppc64', 'westmere', 'fallback', 'rvv' ]
|
||||
IMPLEMENTATIONS: List[Implementation] = [ 'arm64', 'haswell', 'icelake', 'lasx', 'lsx', 'ppc64', 'westmere', 'fallback' ]
|
||||
GENERIC_INCLUDE = "simdjson/generic"
|
||||
GENERIC_SRC = "generic"
|
||||
BUILTIN = "simdjson/builtin"
|
||||
@@ -358,6 +355,7 @@ class Amalgamator:
|
||||
self.include_stack.append(file)
|
||||
|
||||
file.processed = False
|
||||
|
||||
self.write(f"/* begin file {self.file_to_str(file)} */")
|
||||
|
||||
if file == BUILTIN_BEGIN_H:
|
||||
|
||||
+109
-45
@@ -1,4 +1,4 @@
|
||||
/* auto-generated on 2025-07-14 15:43:52 -0400. Do not edit! */
|
||||
/* auto-generated on 2025-08-05 16:29:59 +0000. version 4.0.0 Do not edit! */
|
||||
/* including simdjson.cpp: */
|
||||
/* begin file simdjson.cpp */
|
||||
#define SIMDJSON_SRC_SIMDJSON_CPP
|
||||
@@ -577,17 +577,6 @@ double from_chars(const char *first, const char* end) noexcept;
|
||||
// We assume by default static linkage
|
||||
#define SIMDJSON_DLLIMPORTEXPORT
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Workaround for the vcpkg package manager. Only vcpkg should
|
||||
* ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused.
|
||||
*/
|
||||
#if SIMDJSON_USING_LIBRARY
|
||||
#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport)
|
||||
#endif
|
||||
/**
|
||||
* End of workaround for the vcpkg package manager.
|
||||
*/
|
||||
#else
|
||||
#define SIMDJSON_DLLIMPORTEXPORT
|
||||
#endif
|
||||
@@ -2444,6 +2433,18 @@ namespace std {
|
||||
#define SIMDJSON_AVX512_ALLOWED 1
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __has_cpp_attribute
|
||||
#define simdjson_lifetime_bound
|
||||
#elif __has_cpp_attribute(msvc::lifetimebound)
|
||||
#define simdjson_lifetime_bound [[msvc::lifetimebound]]
|
||||
#elif __has_cpp_attribute(clang::lifetimebound)
|
||||
#define simdjson_lifetime_bound [[clang::lifetimebound]]
|
||||
#elif __has_cpp_attribute(lifetimebound)
|
||||
#define simdjson_lifetime_bound [[lifetimebound]]
|
||||
#else
|
||||
#define simdjson_lifetime_bound
|
||||
#endif
|
||||
#endif // SIMDJSON_COMMON_DEFS_H
|
||||
/* end file simdjson/common_defs.h */
|
||||
/* skipped duplicate #include "simdjson/compiler_check.h" */
|
||||
@@ -2908,7 +2909,6 @@ concept optional_type = requires(std::remove_cvref_t<T> obj) {
|
||||
{ obj.value() } -> std::same_as<typename std::remove_cvref_t<T>::value_type&>;
|
||||
requires requires(typename std::remove_cvref_t<T>::value_type &&val) {
|
||||
obj.emplace(std::move(val));
|
||||
obj = std::move(val);
|
||||
{
|
||||
obj.value_or(val)
|
||||
} -> std::convertible_to<typename std::remove_cvref_t<T>::value_type>;
|
||||
@@ -9170,6 +9170,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -15609,6 +15610,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -21903,6 +21905,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -28354,6 +28357,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -35164,6 +35168,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -41796,6 +41801,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -47874,6 +47880,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -53544,6 +53551,7 @@ struct implementation_simdjson_result_base {
|
||||
* the error() method returns a value that evaluates to false.
|
||||
*/
|
||||
simdjson_inline T&& value_unsafe() && noexcept;
|
||||
|
||||
protected:
|
||||
/** users should never directly access first and second. **/
|
||||
T first{}; /** Users should never directly access 'first'. **/
|
||||
@@ -56568,10 +56576,78 @@ simdjson_inline void validate_utf8_character() {
|
||||
idx += 4;
|
||||
}
|
||||
|
||||
static const uint8_t CHAR_TYPE_SPACE = 1 << 0;
|
||||
static const uint8_t CHAR_TYPE_OPERATOR = 1 << 1;
|
||||
static const uint8_t CHAR_TYPE_ESC_ASCII = 1 << 2;
|
||||
static const uint8_t CHAR_TYPE_NON_ASCII = 1 << 3;
|
||||
|
||||
const uint8_t char_table[256] = {
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x04, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
|
||||
};
|
||||
|
||||
simdjson_inline bool char_is_type(uint8_t c, uint8_t type) {
|
||||
return (char_table[c] & type);
|
||||
}
|
||||
|
||||
simdjson_inline bool char_is_space(uint8_t c) {
|
||||
return char_is_type(c, CHAR_TYPE_SPACE);
|
||||
}
|
||||
|
||||
simdjson_inline bool char_is_operator(uint8_t c) {
|
||||
return char_is_type(c, CHAR_TYPE_OPERATOR);
|
||||
}
|
||||
|
||||
simdjson_inline bool char_is_space_or_operator(uint8_t c) {
|
||||
return char_is_type(c, CHAR_TYPE_SPACE | CHAR_TYPE_OPERATOR);
|
||||
}
|
||||
|
||||
simdjson_inline bool char_is_ascii_stop(uint8_t c) {
|
||||
return char_is_type(c, CHAR_TYPE_ESC_ASCII | CHAR_TYPE_NON_ASCII);
|
||||
}
|
||||
|
||||
// Returns true if the string is unclosed.
|
||||
simdjson_inline bool validate_string() {
|
||||
idx++; // skip first quote
|
||||
while (idx < len && buf[idx] != '"') {
|
||||
while (idx < len) {
|
||||
do {
|
||||
if (char_is_ascii_stop(buf[idx])) { break; }
|
||||
idx++;
|
||||
} while (idx < len);
|
||||
if (idx >= len) { return true; }
|
||||
if (buf[idx] == '"') {
|
||||
return false;
|
||||
}
|
||||
if (buf[idx] == '\\') {
|
||||
idx += 2;
|
||||
} else if (simdjson_unlikely(buf[idx] & 0x80)) {
|
||||
@@ -56585,43 +56661,31 @@ simdjson_inline bool validate_string() {
|
||||
return false;
|
||||
}
|
||||
|
||||
simdjson_inline bool is_whitespace_or_operator(uint8_t c) {
|
||||
switch (c) {
|
||||
case '{': case '}': case '[': case ']': case ',': case ':':
|
||||
case ' ': case '\r': case '\n': case '\t':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Parse the entire input in STEP_SIZE-byte chunks.
|
||||
//
|
||||
simdjson_inline error_code scan() {
|
||||
bool unclosed_string = false;
|
||||
for (;idx<len;idx++) {
|
||||
switch (buf[idx]) {
|
||||
// String
|
||||
case '"':
|
||||
add_structural();
|
||||
unclosed_string |= validate_string();
|
||||
break;
|
||||
// Operator
|
||||
case '{': case '}': case '[': case ']': case ',': case ':':
|
||||
add_structural();
|
||||
break;
|
||||
// Whitespace
|
||||
case ' ': case '\r': case '\n': case '\t':
|
||||
break;
|
||||
// Primitive or invalid character (invalid characters will be checked in stage 2)
|
||||
default:
|
||||
// Anything else, add the structural and go until we find the next one
|
||||
add_structural();
|
||||
while (idx+1<len && !is_whitespace_or_operator(buf[idx+1])) {
|
||||
idx++;
|
||||
};
|
||||
break;
|
||||
do {
|
||||
if (!char_is_space(buf[idx])) { break; }
|
||||
idx++;
|
||||
} while (idx < len);
|
||||
if (idx >= len) { break; }
|
||||
// String
|
||||
if (buf[idx] == '"') {
|
||||
add_structural();
|
||||
unclosed_string |= validate_string();
|
||||
// Operator
|
||||
} else if (char_is_operator(buf[idx])) {
|
||||
add_structural();
|
||||
// Primitive or invalid character (invalid characters will be checked in stage 2)
|
||||
} else {
|
||||
// Anything else, add the structural and go until we find the next one
|
||||
add_structural();
|
||||
while (idx+1<len && !char_is_space_or_operator(buf[idx+1])) {
|
||||
idx++;
|
||||
};
|
||||
}
|
||||
}
|
||||
// We pad beyond.
|
||||
|
||||
+3015
-287
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -21,19 +21,6 @@ bool implementation::supported_by_runtime_system() const {
|
||||
|
||||
#define SIMDJSON_CONDITIONAL_INCLUDE
|
||||
|
||||
#if SIMDJSON_IMPLEMENTATION_RVV
|
||||
#include <simdjson/rvv/implementation.h>
|
||||
namespace simdjson {
|
||||
namespace internal {
|
||||
static const rvv::implementation* get_rvv_singleton() {
|
||||
static const rvv::implementation rvv_singleton{};
|
||||
return &rvv_singleton;
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace simdjson
|
||||
#endif // SIMDJSON_IMPLEMENTATION_RVV
|
||||
|
||||
|
||||
#if SIMDJSON_IMPLEMENTATION_ARM64
|
||||
#include <simdjson/arm64/implementation.h>
|
||||
namespace simdjson {
|
||||
@@ -143,7 +130,6 @@ namespace internal {
|
||||
+ SIMDJSON_IMPLEMENTATION_HASWELL + SIMDJSON_IMPLEMENTATION_WESTMERE \
|
||||
+ SIMDJSON_IMPLEMENTATION_ARM64 + SIMDJSON_IMPLEMENTATION_PPC64 \
|
||||
+ SIMDJSON_IMPLEMENTATION_LSX + SIMDJSON_IMPLEMENTATION_LASX \
|
||||
+ SIMDJSON_IMPLEMENTATION_RVV \
|
||||
+ SIMDJSON_IMPLEMENTATION_FALLBACK == 1)
|
||||
|
||||
#if SIMDJSON_SINGLE_IMPLEMENTATION
|
||||
@@ -170,9 +156,6 @@ namespace internal {
|
||||
#if SIMDJSON_IMPLEMENTATION_LASX
|
||||
get_lasx_singleton();
|
||||
#endif
|
||||
#if SIMDJSON_IMPLEMENTATION_RVV
|
||||
get_rvv_singleton();
|
||||
#endif
|
||||
#if SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||
get_fallback_singleton();
|
||||
#endif
|
||||
@@ -233,9 +216,6 @@ static const std::initializer_list<const implementation *>& get_available_implem
|
||||
#if SIMDJSON_IMPLEMENTATION_LASX
|
||||
get_lasx_singleton(),
|
||||
#endif
|
||||
#if SIMDJSON_IMPLEMENTATION_RVV
|
||||
get_rvv_singleton(),
|
||||
#endif
|
||||
#if SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||
get_fallback_singleton(),
|
||||
#endif
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
#ifndef SIMDJSON_SRC_RVV_CPP
|
||||
#define SIMDJSON_SRC_RVV_CPP
|
||||
|
||||
#include "simdjson/rvv/implementation.h"
|
||||
|
||||
|
||||
|
||||
namespace simdjson {
|
||||
namespace rvv {
|
||||
error_code implementation::create_dom_parser_implementation(
|
||||
size_t, size_t,
|
||||
std::unique_ptr<simdjson::internal::dom_parser_implementation>&) const noexcept {
|
||||
return error_code::UNSUPPORTED_ARCHITECTURE;
|
||||
}
|
||||
} // namespace rvv
|
||||
} // namespace simdjson
|
||||
#endif // SIMDJSON_SRC_RVV_CPP
|
||||
@@ -41,9 +41,6 @@ SIMDJSON_PUSH_DISABLE_UNUSED_WARNINGS
|
||||
#if SIMDJSON_IMPLEMENTATION_LASX
|
||||
#include <lasx.cpp>
|
||||
#endif
|
||||
#if SIMDJSON_IMPLEMENTATION_RVV
|
||||
#include <rvv.cpp>
|
||||
#endif
|
||||
#if SIMDJSON_IMPLEMENTATION_FALLBACK
|
||||
#include <fallback.cpp>
|
||||
#endif
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace builder_tests {
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
append(sb, c);
|
||||
std::string_view p{sb};
|
||||
(void)p; // to avoid unused variable warning
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool car_test_exception2() {
|
||||
@@ -85,12 +86,18 @@ namespace builder_tests {
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
sb << c;
|
||||
std::string_view p{sb};
|
||||
(void)p; // to avoid unused variable warning
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
void car_test_to_json_exception() {
|
||||
bool car_test_to_json_exception() {
|
||||
TEST_START();
|
||||
Car c = {"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}};
|
||||
std::string json = simdjson::builder::to_json_string(c);
|
||||
std::string json = simdjson::to_json(c);
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
bool car_test_to_json_exception_value() {
|
||||
TEST_START();
|
||||
std::string json = simdjson::to_json(Car{"Toyota", "Corolla", 2017, {30.0,30.2,30.513,30.79}});
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
@@ -165,6 +172,7 @@ bool serialize_deserialize_x_y_z() {
|
||||
car_test_exception() &&
|
||||
car_test_exception2() &&
|
||||
car_test_to_json_exception() &&
|
||||
car_test_to_json_exception_value() &&
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
car_test() &&
|
||||
serialize_deserialize_kid() &&
|
||||
|
||||
@@ -132,7 +132,9 @@ if(
|
||||
)
|
||||
message(STATUS "compiler id: ${CMAKE_CXX_COMPILER_ID} version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
add_cpp_test(ranges_test LABELS dom acceptance per_implementation)
|
||||
set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||
if(NOT SIMDJSON_STATIC_REFLECTION)
|
||||
set_target_properties(ranges_test PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32 AND BUILD_SHARED_LIBS)
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
#include "simdjson.h"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "simdjson.h"
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ add_cpp_test(ondemand_iterate_many_csv LABELS ondemand acceptance
|
||||
add_cpp_test(ondemand_custom_types_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_custom_types_document_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_stl_types_tests LABELS ondemand acceptance per_implementation)
|
||||
add_cpp_test(ondemand_convert_tests LABELS ondemand acceptance per_implementation)
|
||||
if(NOT SIMDJSON_SANITIZE)
|
||||
add_cpp_test(ondemand_cacheline LABELS ondemand acceptance per_implementation)
|
||||
endif()
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#endif
|
||||
#include "simdjson.h"
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
// Returns the default size of the page in bytes on this system.
|
||||
long page_size() {
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
#include "simdjson.h"
|
||||
#include "simdjson/convert.h"
|
||||
#include "test_ondemand.h"
|
||||
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __cpp_lib_ranges
|
||||
|
||||
namespace convert_tests {
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||
struct Car {
|
||||
std::string make{};
|
||||
std::string model{};
|
||||
int year{};
|
||||
std::vector<double> tire_pressure{};
|
||||
|
||||
friend simdjson::error_code tag_invoke(simdjson::deserialize_tag, auto &val,
|
||||
Car &car) {
|
||||
simdjson::ondemand::object obj;
|
||||
auto error = val.get_object().get(obj);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
// Instead of repeatedly obj["something"], we iterate through the object
|
||||
// which we expect to be faster.
|
||||
for (auto field : obj) {
|
||||
simdjson::ondemand::raw_json_string key;
|
||||
error = field.key().get(key);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if (key == "make") {
|
||||
error = field.value().get_string(car.make);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
} else if (key == "model") {
|
||||
error = field.value().get_string(car.model);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
} else if (key == "year") {
|
||||
error = field.value().get(car.year);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
} else if (key == "tire_pressure") {
|
||||
error = field.value().get(car.tire_pressure);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
return simdjson::SUCCESS;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(simdjson::custom_deserializable<std::unique_ptr<Car>>,
|
||||
"It should be deserializable");
|
||||
|
||||
static_assert(std::input_or_output_iterator<simdjson::auto_iterator>,
|
||||
"Must be a valid input iterator");
|
||||
static_assert(std::semiregular<simdjson::auto_iterator>,
|
||||
"Should be kinda regular");
|
||||
// static_assert(std::ranges::__access::__member_end<simdjson::auto_parser<>>,
|
||||
// "Must be a valid input iterator");
|
||||
// static_assert(std::ranges::views::__adaptor::__is_range_adaptor_closure<
|
||||
// simdjson::auto_parser<>>,
|
||||
// "Parser need to be range adaptor closure.");
|
||||
// static_assert(std::ranges::views::__adaptor::__adaptor_invocable<
|
||||
// decltype(simdjson::to<Car>()),
|
||||
// simdjson::auto_parser<>>,
|
||||
// "I don't even know!");
|
||||
static_assert(std::ranges::range<simdjson::auto_parser<>>,
|
||||
"Parser need to be a range.");
|
||||
static_assert(std::ranges::forward_range<simdjson::auto_parser<>>,
|
||||
"Parser need to be an input range.");
|
||||
static_assert(
|
||||
requires(simdjson::auto_parser<> &parser) {
|
||||
{ parser.begin() } -> std::input_or_output_iterator;
|
||||
}, "Must be valid iterator.");
|
||||
|
||||
simdjson::padded_string json_car =
|
||||
R"( {
|
||||
"make": "Toyota",
|
||||
"model": "Camry",
|
||||
"year": 2018,
|
||||
"tire_pressure": [ 40.1, 39.9 ]
|
||||
} )"_padded;
|
||||
simdjson::padded_string json_cars =
|
||||
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;
|
||||
|
||||
bool simple() {
|
||||
TEST_START();
|
||||
Car car = simdjson::from(json_car);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool broken() {
|
||||
TEST_START();
|
||||
simdjson::padded_string short_json_cars = R"( { "make )"_padded;
|
||||
try {
|
||||
Car car = simdjson::from(json_cars);
|
||||
TEST_FAIL("Should not have succeeded");
|
||||
} catch (...) {
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool simple_optional() {
|
||||
TEST_START();
|
||||
auto car = simdjson::from(json_car).optional<Car>();
|
||||
if (!car.has_value() || car->make != "Toyota" || car->model != "Camry" ||
|
||||
car->year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool with_parser() {
|
||||
TEST_START();
|
||||
simdjson::ondemand::parser parser;
|
||||
Car car = simdjson::from(parser, json_car);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_array() {
|
||||
TEST_START();
|
||||
auto parser = simdjson::from(json_cars);
|
||||
for (auto val : parser.array()) {
|
||||
Car car{};
|
||||
if (auto const error = val.get(car)) {
|
||||
std::cerr << simdjson::error_message(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_array_shortcut() {
|
||||
TEST_START();
|
||||
simdjson::ondemand::parser parser;
|
||||
for (auto val : simdjson::from(parser, json_cars)) {
|
||||
Car car{};
|
||||
if (auto const error = val.get(car)) {
|
||||
std::cerr << simdjson::error_message(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_bad_array() {
|
||||
TEST_START();
|
||||
auto parser = simdjson::from(json_car);
|
||||
try {
|
||||
auto array_result = parser.array();
|
||||
// Check if array_result has an error
|
||||
if (array_result.error() != simdjson::SUCCESS) {
|
||||
// This is expected - trying to get array from an object should fail
|
||||
if (array_result.error() != simdjson::INCORRECT_TYPE) {
|
||||
std::cerr << "Expected INCORRECT_TYPE but got: " << array_result.error()
|
||||
<< " (" << simdjson::error_message(array_result.error()) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Got expected error, test passes
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
// If we get here without error, try to iterate
|
||||
// This might throw when we try to use the array
|
||||
for (auto val : array_result) {
|
||||
static_cast<void>(val);
|
||||
// Should not reach here - the JSON is an object, not an array
|
||||
std::cerr << "Unexpectedly succeeded in iterating over non-array JSON" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Also should not reach here
|
||||
std::cerr << "array() succeeded on object JSON without throwing" << std::endl;
|
||||
return false;
|
||||
} catch (simdjson::simdjson_error &e) {
|
||||
if (e.error() != simdjson::INCORRECT_TYPE) {
|
||||
std::cerr << "Expected INCORRECT_TYPE but got: " << e.error() << " (" << simdjson::error_message(e.error()) << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Got expected exception, test passes
|
||||
} catch (...) {
|
||||
std::cerr << "Unexpected exception type" << std::endl;
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_basic_adaptor() {
|
||||
TEST_START();
|
||||
for (Car car : simdjson::from(json_cars) | simdjson::as<Car>()) {
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_no_errors() {
|
||||
TEST_START();
|
||||
auto cars = simdjson::from(json_cars) | simdjson::no_errors;
|
||||
for (auto val : cars) {
|
||||
Car car = val.get<Car>();
|
||||
if (car.year < 1998) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool to_clean_array() {
|
||||
TEST_START();
|
||||
for (auto val : simdjson::from(json_cars) | simdjson::no_errors) {
|
||||
Car car = val.get<Car>();
|
||||
if (car.year < 1998) {
|
||||
std::cerr << car.make << " " << car.model << " " << car.year << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_adaptor_basic() {
|
||||
TEST_START();
|
||||
// Test 1: Basic usage of to<T> with a value reference
|
||||
simdjson::ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json_car);
|
||||
if (doc_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::document doc = std::move(doc_result.value());
|
||||
simdjson::simdjson_result<simdjson::ondemand::value> val = doc.get_value();
|
||||
|
||||
// to<T> converts a simdjson_result<value>& to T
|
||||
Car car = simdjson::to<Car>(val);
|
||||
if (car.make != "Toyota" || car.model != "Camry" || car.year != 2018) {
|
||||
return false;
|
||||
}
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_adaptor_with_single_value() {
|
||||
TEST_START();
|
||||
// Test 2: Using to<T> to convert individual values
|
||||
simdjson::ondemand::parser parser;
|
||||
auto doc_result = parser.iterate(json_car);
|
||||
if (doc_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::document doc = std::move(doc_result.value());
|
||||
|
||||
// Get individual field and convert it
|
||||
auto obj_result = doc.get_object();
|
||||
if (obj_result.error()) {
|
||||
return false;
|
||||
}
|
||||
simdjson::ondemand::object obj = std::move(obj_result.value());
|
||||
|
||||
auto year_val = obj["year"];
|
||||
int64_t year = simdjson::to<int64_t>(year_val);
|
||||
if (year != 2018) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
bool test_to_vs_from_equivalence() {
|
||||
TEST_START();
|
||||
// Test 3: Verify that simdjson::to<> and simdjson::from behave equivalently
|
||||
// Both are instances of to_adaptor - from is just to<void>
|
||||
|
||||
// These should produce identical auto_parser objects
|
||||
auto parser1 = simdjson::from(json_car);
|
||||
// simdjson::from is an alias for simdjson::to<void>
|
||||
auto parser2 = simdjson::from(json_car); // Same as parser1
|
||||
|
||||
// Both should parse the same way
|
||||
Car car1 = parser1;
|
||||
Car car2 = parser2;
|
||||
|
||||
if (car1.make != car2.make || car1.model != car2.model || car1.year != car2.year) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_SUCCEED();
|
||||
}
|
||||
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
bool run() {
|
||||
return
|
||||
#if SIMDJSON_EXCEPTIONS && SIMDJSON_SUPPORTS_DESERIALIZATION
|
||||
test_basic_adaptor() && broken() && simple() && simple_optional() && with_parser() && to_array() &&
|
||||
to_array_shortcut() && to_bad_array() && test_no_errors() &&
|
||||
to_clean_array() && test_to_adaptor_basic() &&
|
||||
test_to_adaptor_with_single_value() && test_to_vs_from_equivalence() &&
|
||||
#endif // SIMDJSON_EXCEPTIONS
|
||||
true;
|
||||
}
|
||||
|
||||
} // namespace convert_tests
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
return test_main(argc, argv, convert_tests::run);
|
||||
}
|
||||
#else
|
||||
int main() { return 0; }
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
#define TEST_MACROS_H
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#ifndef SIMDJSON_BENCHMARK_DATA_DIR
|
||||
#define SIMDJSON_BENCHMARK_DATA_DIR "jsonexamples/"
|
||||
|
||||
Reference in New Issue
Block a user