mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55f698c97b | |||
| 7e2a1d7651 | |||
| c4058d9d22 | |||
| 54af3feca2 |
+21
@@ -49,6 +49,27 @@ objs
|
|||||||
|
|
||||||
# C++ ignore from https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore
|
# 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
|
# Prerequisites
|
||||||
*.d
|
*.d
|
||||||
|
|
||||||
|
|||||||
@@ -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_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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user