├── .gitignore ├── simdjson-sys ├── README.md ├── src │ ├── lib.rs │ ├── simdjson_c_api.h │ └── simdjson_c_api.cpp ├── Cargo.toml └── build.rs ├── .gitmodules ├── src ├── prelude.rs ├── lib.rs ├── dom │ ├── mod.rs │ ├── document.rs │ ├── document_stream.rs │ ├── object.rs │ ├── parser.rs │ ├── element.rs │ └── array.rs ├── ondemand │ ├── mod.rs │ ├── json_type.rs │ ├── number.rs │ ├── field.rs │ ├── object_iterator.rs │ ├── parser.rs │ ├── array_iterator.rs │ ├── array.rs │ ├── object.rs │ ├── value.rs │ └── document.rs ├── utils.rs ├── serde │ ├── mod.rs │ └── de.rs ├── padded_string.rs ├── macros.rs └── error.rs ├── examples ├── quickstart.rs ├── simple.rs └── issue_20.rs ├── rustfmt.toml ├── CHANGELOG.md ├── Cargo.toml ├── tests └── basic_tests.rs ├── .github └── workflows │ └── CI.yml ├── README.md └── LICENSE.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | /simdjson-sys/target 5 | 6 | .vscode/ 7 | .idea/ 8 | .cache/ 9 | build/ -------------------------------------------------------------------------------- /simdjson-sys/README.md: -------------------------------------------------------------------------------- 1 | # simdjson-sys 2 | 3 | Low level bindings and C-API for [simdjson](https://github.com/simdjson/simdjson) -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "simdjson-sys/simdjson"] 2 | path = simdjson-sys/simdjson 3 | url = https://github.com/simdjson/simdjson.git 4 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::padded_string::{ 2 | load_padded_string, make_padded_string, IntoPaddedString, ToPaddedString, 3 | }; 4 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod macros; 2 | 3 | pub mod dom; 4 | mod error; 5 | pub mod ondemand; 6 | pub mod padded_string; 7 | pub mod prelude; 8 | mod utils; 9 | 10 | pub use error::{Result, SimdJsonError}; 11 | pub use simdjson_sys::{SIMDJSON_MAXSIZE_BYTES, SIMDJSON_PADDING}; 12 | 13 | // pub mod serde; 14 | 15 | #[cfg(test)] 16 | mod tests {} 17 | -------------------------------------------------------------------------------- /simdjson-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | 5 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 6 | 7 | pub const SIMDJSON_PADDING: usize = 64; 8 | pub const SIMDJSON_MAXSIZE_BYTES: usize = 0xFFFFFFFF; 9 | pub const DEFAULT_BATCH_SIZE: usize = 1000000; 10 | -------------------------------------------------------------------------------- /src/dom/mod.rs: -------------------------------------------------------------------------------- 1 | mod array; 2 | mod document; 3 | mod document_stream; 4 | mod element; 5 | mod object; 6 | mod parser; 7 | 8 | pub use array::{Array, ArrayIter}; 9 | pub use document::Document; 10 | pub use document_stream::{DocumentStream, DocumentStreamIter}; 11 | pub use element::{Element, ElementType}; 12 | pub use object::{Object, ObjectIter}; 13 | pub use parser::Parser; 14 | -------------------------------------------------------------------------------- /examples/quickstart.rs: -------------------------------------------------------------------------------- 1 | use simdjson_rust::{ondemand, prelude::*, Result}; 2 | 3 | fn main() -> Result<()> { 4 | let ps = load_padded_string("simdjson-sys/simdjson/jsonexamples/twitter.json")?; 5 | let mut parser = ondemand::Parser::default(); 6 | let mut tweets = parser.iterate(&ps)?; 7 | println!( 8 | "{} results.", 9 | tweets.at_pointer("/search_metadata/count")?.get_uint64()? 10 | ); 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | unstable_features = true 2 | 3 | version = "Two" 4 | 5 | group_imports = "StdExternalCrate" 6 | imports_granularity = "Crate" 7 | reorder_imports = true 8 | 9 | wrap_comments = true 10 | normalize_comments = true 11 | 12 | reorder_impl_items = true 13 | condense_wildcard_suffixes = true 14 | enum_discrim_align_threshold = 20 15 | use_field_init_shorthand = true 16 | 17 | format_strings = true 18 | format_code_in_doc_comments = true 19 | format_macro_matchers = true 20 | -------------------------------------------------------------------------------- /src/ondemand/mod.rs: -------------------------------------------------------------------------------- 1 | mod array; 2 | mod array_iterator; 3 | mod document; 4 | mod field; 5 | mod json_type; 6 | mod number; 7 | mod object; 8 | mod object_iterator; 9 | mod parser; 10 | mod value; 11 | 12 | pub use array::Array; 13 | pub use array_iterator::ArrayIterator; 14 | pub use document::Document; 15 | pub use field::Field; 16 | pub use json_type::{JsonType, NumberType}; 17 | pub use number::Number; 18 | pub use object::Object; 19 | pub use object_iterator::ObjectIterator; 20 | pub use parser::Parser; 21 | pub use value::Value; 22 | -------------------------------------------------------------------------------- /simdjson-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simdjson-sys" 3 | version = "0.1.0-alpha.2" 4 | edition = "2021" 5 | authors = ["SunDoge <384813529@qq.com>"] 6 | license = "Apache-2.0" 7 | description = "Low level Rust bindings for simdjson." 8 | homepage = "https://crates.io/crates/simdjson-sys" 9 | documentation = "https://docs.rs/simdjson-sys" 10 | repository = "https://github.com/SunDoge/simdjson-rust/tree/master/simdjson-sys" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | exclude = ["simdjson/", "!simdjson/singleheader/simdjson.*"] 15 | 16 | [dependencies] 17 | 18 | [build-dependencies] 19 | bindgen = "0.66.1" 20 | cc = { version = "1.0.83", features = ["parallel"] } 21 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::NonNull; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | #[inline] 6 | pub fn string_view_to_str<'a>(sv: NonNull) -> &'a str { 7 | let s = unsafe { 8 | let s = std::slice::from_raw_parts( 9 | ffi::STD_string_view_data(sv.as_ptr()).cast(), 10 | ffi::STD_string_view_size(sv.as_ptr()), 11 | ); 12 | std::str::from_utf8_unchecked(s) 13 | }; 14 | unsafe { ffi::STD_string_view_free(sv.as_ptr()) }; 15 | s 16 | } 17 | 18 | #[inline] 19 | pub fn string_view_struct_to_str<'a>(sv: ffi::SJ_string_view) -> &'a str { 20 | unsafe { 21 | let s = std::slice::from_raw_parts(sv.data.cast(), sv.len); 22 | std::str::from_utf8_unchecked(s) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | - Bindings for [simdjson] v0.3.1. 11 | - Use [cxx] instead of [bindgen] because of too many generics. 12 | 13 | 14 | ## [0.1.0] - 2019-03-13 15 | - Bindings for [simdjson] v0.0.1 16 | 17 | 18 | [Unreleased]: https://github.com/SunDoge/simdjson-rust 19 | [0.1.0]: https://github.com/SunDoge/simdjson-rust/releases/tag/v0.1.0 20 | 21 | [simdjson]: https://github.com/simdjson/simdjson 22 | [cxx]: https://github.com/dtolnay/cxx 23 | [bindgen]: https://github.com/rust-lang/rust-bindgen -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | use simdjson_rust::{dom, ondemand, prelude::*}; 2 | 3 | fn main() -> simdjson_rust::Result<()> { 4 | let ps = make_padded_string("[0,1,2,3]"); 5 | 6 | // ondemand api. 7 | { 8 | let mut parser = ondemand::Parser::default(); 9 | let mut doc = parser.iterate(&ps)?; 10 | let mut array = doc.get_array()?; 11 | for (index, value) in array.iter()?.enumerate() { 12 | assert_eq!(index as u64, value?.get_uint64()?); 13 | } 14 | } 15 | 16 | // dom api. 17 | { 18 | let mut parser = dom::Parser::default(); 19 | let elem = parser.parse(&ps)?; 20 | let arr = elem.get_array()?; 21 | for (index, value) in arr.iter().enumerate() { 22 | assert_eq!(index as u64, value.get_uint64()?); 23 | } 24 | } 25 | 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /src/serde/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod de; 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use super::*; 6 | // use super::element::GetValue; 7 | use crate::dom::parser::Parser; 8 | use serde::Deserialize; 9 | 10 | #[test] 11 | fn test_element() -> Result<(), Box> { 12 | let mut parser = Parser::default(); 13 | let elm = parser.parse(r#"[true, false]"#)?; 14 | println!("{}", elm); 15 | let a: Vec = de::from_element(&elm)?; 16 | assert_eq!(vec![true, false], a); 17 | 18 | #[derive(Debug, Deserialize)] 19 | struct A { 20 | field1: bool, 21 | } 22 | let elm = parser.parse(r#"{"field1": false}"#)?; 23 | println!("{}", elm); 24 | let a: A = de::from_element(&elm)?; 25 | assert!(!a.field1); 26 | 27 | Ok(()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/dom/document.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::NonNull; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::Element; 6 | use crate::macros::impl_drop; 7 | 8 | pub struct Document { 9 | ptr: NonNull, 10 | } 11 | 12 | impl Default for Document { 13 | fn default() -> Self { 14 | Self { 15 | ptr: unsafe { NonNull::new_unchecked(ffi::SJ_DOM_document_new()) }, 16 | } 17 | } 18 | } 19 | 20 | impl Document { 21 | pub fn new(ptr: NonNull) -> Self { 22 | Self { ptr } 23 | } 24 | 25 | pub fn root(&self) -> Element<'_> { 26 | Element::new(unsafe { 27 | NonNull::new_unchecked(ffi::SJ_DOM_document_root(self.ptr.as_ptr())) 28 | }) 29 | } 30 | 31 | pub fn as_ptr(&self) -> *mut ffi::SJ_DOM_document { 32 | self.ptr.as_ptr() 33 | } 34 | } 35 | 36 | impl_drop!(Document, ffi::SJ_DOM_document_free); 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simdjson-rust" 3 | version = { workspace = true } 4 | authors = ["SunDoge <384813529@qq.com>"] 5 | edition = "2021" 6 | license = "Apache-2.0" 7 | description = "Rust bindings for the simdjson project." 8 | homepage = "https://crates.io/crates/simdjson-rust" 9 | documentation = "https://docs.rs/simdjson-rust" 10 | repository = "https://github.com/SunDoge/simdjson-rust" 11 | readme = "README.md" 12 | exclude = [".github/", "examples/"] 13 | 14 | 15 | [workspace] 16 | resolver = "2" 17 | members = ["simdjson-sys"] 18 | 19 | [workspace.package] 20 | version = "0.3.0-alpha.3" 21 | 22 | [workspace.dependencies] 23 | simdjson-sys = { path = "simdjson-sys", version = "0.1.0-alpha.2" } 24 | 25 | 26 | [dependencies] 27 | thiserror = "1.0" 28 | simdjson-sys = { workspace = true } 29 | 30 | # serde compatibilty 31 | serde = { version = "1", features = ["derive"], optional = true } 32 | serde_json = { version = "1", optional = true } 33 | 34 | 35 | [features] 36 | default = [] 37 | 38 | # serde compatibility 39 | serde_impl = ["serde", "serde_json"] 40 | -------------------------------------------------------------------------------- /src/ondemand/json_type.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq)] 2 | pub enum JsonType { 3 | Array = 1, 4 | Object, 5 | Number, 6 | String, 7 | Boolean, 8 | Null, 9 | } 10 | 11 | impl From for JsonType { 12 | fn from(value: i32) -> Self { 13 | match value { 14 | 1 => JsonType::Array, 15 | 2 => JsonType::Object, 16 | 3 => JsonType::Number, 17 | 4 => JsonType::String, 18 | 5 => JsonType::Boolean, 19 | 6 => JsonType::Null, 20 | _ => panic!("Invalid JsonType value: {}", value), 21 | } 22 | } 23 | } 24 | 25 | #[derive(Debug, PartialEq)] 26 | pub enum NumberType { 27 | FloatingPointNumber = 1, 28 | SignedInteger, 29 | UnsignedInteger, 30 | } 31 | 32 | impl From for NumberType { 33 | fn from(value: i32) -> Self { 34 | match value { 35 | 1 => NumberType::FloatingPointNumber, 36 | 2 => NumberType::SignedInteger, 37 | 3 => NumberType::UnsignedInteger, 38 | _ => panic!("Invalid NumberType value: {}", value), 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/ondemand/number.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{Document, NumberType}; 6 | use crate::macros::impl_drop; 7 | 8 | pub struct Number<'a> { 9 | ptr: NonNull, 10 | _doc: PhantomData<&'a mut Document<'a, 'a>>, 11 | } 12 | 13 | impl<'a> Number<'a> { 14 | pub fn new(ptr: NonNull) -> Self { 15 | Self { 16 | ptr, 17 | _doc: PhantomData, 18 | } 19 | } 20 | 21 | pub fn get_uint64(&mut self) -> u64 { 22 | unsafe { ffi::SJ_OD_number_get_uint64(self.ptr.as_mut()) } 23 | } 24 | 25 | pub fn get_int64(&mut self) -> i64 { 26 | unsafe { ffi::SJ_OD_number_get_int64(self.ptr.as_mut()) } 27 | } 28 | 29 | pub fn get_double(&mut self) -> f64 { 30 | unsafe { ffi::SJ_OD_number_get_double(self.ptr.as_mut()) } 31 | } 32 | 33 | pub fn get_number_type(&mut self) -> NumberType { 34 | unsafe { ffi::SJ_OD_number_get_number_type(self.ptr.as_mut()) }.into() 35 | } 36 | } 37 | 38 | impl_drop!(Number<'a>, ffi::SJ_OD_number_free); 39 | -------------------------------------------------------------------------------- /src/padded_string.rs: -------------------------------------------------------------------------------- 1 | use std::{io::Read, path::Path}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | pub fn make_padded_string(s: &str) -> String { 6 | let mut ps = String::with_capacity(s.len() + ffi::SIMDJSON_PADDING); 7 | ps.push_str(s); 8 | ps 9 | } 10 | 11 | pub fn load_padded_string>(path: P) -> std::io::Result { 12 | let mut file = std::fs::File::open(path)?; 13 | let len = file.metadata()?.len() as usize; 14 | let mut buf = String::with_capacity(len + ffi::SIMDJSON_PADDING); 15 | file.read_to_string(&mut buf)?; 16 | Ok(buf) 17 | } 18 | 19 | pub trait ToPaddedString { 20 | fn to_padded_string(&self) -> String; 21 | } 22 | 23 | pub trait IntoPaddedString { 24 | fn into_padded_string(self) -> String; 25 | } 26 | 27 | impl ToPaddedString for &str { 28 | fn to_padded_string(&self) -> String { 29 | make_padded_string(self) 30 | } 31 | } 32 | 33 | impl ToPaddedString for &mut str { 34 | fn to_padded_string(&self) -> String { 35 | make_padded_string(self) 36 | } 37 | } 38 | 39 | impl IntoPaddedString for String { 40 | fn into_padded_string(mut self) -> String { 41 | if self.capacity() < self.len() + ffi::SIMDJSON_PADDING { 42 | self.reserve(ffi::SIMDJSON_PADDING); 43 | } 44 | self 45 | } 46 | } 47 | 48 | #[cfg(test)] 49 | mod tests {} 50 | -------------------------------------------------------------------------------- /tests/basic_tests.rs: -------------------------------------------------------------------------------- 1 | // #[cfg(test)] 2 | // mod number_tests { 3 | 4 | // use simdjson_rust::dom; 5 | // use simdjson_rust::error::SimdJsonError; 6 | 7 | // // ulp distance 8 | // // Marc B. Reynolds, 2016-2019 9 | // // Public Domain under http://unlicense.org, see link for details. 10 | // // adapted by D. Lemire 11 | // #[allow(dead_code)] 12 | // fn f64_ulp_dist(a: f64, b: f64) -> u64 { 13 | // // let ua: u64 = transmute(a); 14 | // // let ub: u64 = transmute(b); 15 | // let (ua, ub): (u64, u64) = (a.to_bits(), b.to_bits()); 16 | 17 | // if (ub ^ ua) as i64 >= 0 { 18 | // if (ua - ub) as i64 >= 0 { 19 | // ua - ub 20 | // } else { 21 | // ub - ua 22 | // } 23 | // } else { 24 | // ua + ub + 0x80000000 25 | // } 26 | // } 27 | 28 | // #[test] 29 | // fn small_integers() -> Result<(), SimdJsonError> { 30 | // let mut parser = dom::parser::Parser::default(); 31 | 32 | // for _m in 10..20 { 33 | // for i in -1024..1024 { 34 | // let s = i.to_string(); 35 | // let actual = parser.parse(&s)?.get_i64()?; 36 | 37 | // assert_eq!(actual, i); 38 | // } 39 | // } 40 | 41 | // Ok(()) 42 | // } 43 | // } 44 | -------------------------------------------------------------------------------- /src/ondemand/field.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{document::Document, value::Value}; 6 | use crate::{ 7 | error::Result, 8 | macros::{impl_drop, map_result}, 9 | utils::string_view_to_str, 10 | }; 11 | 12 | pub struct Field<'a> { 13 | ptr: NonNull, 14 | _doc: PhantomData<&'a mut Document<'a, 'a>>, 15 | } 16 | 17 | impl<'a> Field<'a> { 18 | pub fn new(ptr: NonNull) -> Self { 19 | Self { 20 | ptr, 21 | _doc: PhantomData, 22 | } 23 | } 24 | 25 | pub fn unescaped_key(&mut self, allow_replacement: bool) -> Result<&'a str> { 26 | let sv = map_result!( 27 | ffi::SJ_OD_field_unescaped_key(self.ptr.as_mut(), allow_replacement), 28 | ffi::STD_string_view_result_error, 29 | ffi::STD_string_view_result_value_unsafe 30 | )?; 31 | Ok(string_view_to_str(sv)) 32 | } 33 | 34 | // Double free error. 35 | // pub fn value(&mut self) -> Value { 36 | // let ptr = unsafe { 37 | // let ptr = ffi::SJ_OD_field_value(self.ptr.as_mut()); 38 | // NonNull::new_unchecked(ptr) 39 | // }; 40 | // Value::new(ptr) 41 | // } 42 | 43 | pub fn take_value(self) -> Value<'a> { 44 | let ptr = unsafe { 45 | let ptr = ffi::SJ_OD_field_take_value(self.ptr.as_ptr()); 46 | NonNull::new_unchecked(ptr) 47 | }; 48 | 49 | Value::new(ptr) 50 | } 51 | } 52 | 53 | impl_drop!(Field<'a>, ffi::SJ_OD_field_free); 54 | -------------------------------------------------------------------------------- /simdjson-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, path::PathBuf}; 2 | 3 | fn main() { 4 | cc::Build::new() 5 | .cpp(true) 6 | .flag_if_supported("-std=c++17") 7 | .flag_if_supported("/std:c++20") // error C7555: use of designated initializers requires at least '/std:c++20' 8 | .flag_if_supported("-pthread") 9 | .flag_if_supported("-O3") 10 | .flag_if_supported("/O2") 11 | .flag_if_supported("-DNDEBUG") 12 | .flag_if_supported("/DNDEBUG") 13 | .include("simdjson/singleheader") 14 | .file("src/simdjson_c_api.cpp") 15 | .file("simdjson/singleheader/simdjson.cpp") 16 | .cargo_metadata(true) 17 | .compile("simdjson_c_api"); 18 | 19 | let bindings = bindgen::Builder::default() 20 | // The input header we would like to generate 21 | // bindings for. 22 | .header("src/simdjson_c_api.h") 23 | // Tell cargo to invalidate the built crate whenever any of the 24 | // included header files changed. 25 | .parse_callbacks(Box::new(bindgen::CargoCallbacks)) 26 | // Finish the builder and generate the bindings. 27 | .generate() 28 | // Unwrap the Result and panic on failure. 29 | .expect("Unable to generate bindings"); 30 | 31 | println!("cargo:rerun-if-changed=src/simdjson_c_api.h"); 32 | println!("cargo:rerun-if-changed=src/simdjson_c_api.cpp"); 33 | 34 | // Write the bindings to the $OUT_DIR/bindings.rs file. 35 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 36 | 37 | bindings 38 | .write_to_file(out_path.join("bindings.rs")) 39 | .expect("Couldn't write bindings!"); 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | Test: 10 | if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }} 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: 16 | - ubuntu-22.04 17 | - windows-2022 18 | - macos-12 19 | rust: 20 | - "stable" 21 | cpp: 22 | - llvm 23 | include: 24 | - os: ubuntu-22.04 25 | rust: stable 26 | cpp: gcc 27 | - os: windows-2022 28 | rust: stable 29 | cpp: msvc 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | with: 34 | submodules: true 35 | 36 | # Cache 37 | - name: Cache 38 | uses: actions/cache@v3 39 | with: 40 | path: | 41 | ~/.cargo/bin/ 42 | ~/.cargo/registry/index/ 43 | ~/.cargo/registry/cache/ 44 | ~/.cargo/registry/src/ 45 | ~/.cargo/git/db/ 46 | target/ 47 | key: "cache-OS:${{ matrix.os }}-Rust:${{ matrix.rust }}-${{ hashFiles('Cargo.toml') }}-Cpp:${{ matrix.cpp }}" 48 | restore-keys: | 49 | "cache-OS:${{ matrix.os }}-Rust:${{ matrix.rust }}-" 50 | 51 | # Setup compilers and tools 52 | 53 | - name: Setup Rust 54 | uses: actions-rs/toolchain@v1 55 | with: 56 | toolchain: ${{ matrix.rust }} 57 | default: true 58 | components: rustfmt, clippy 59 | 60 | - name: Setup Cpp 61 | uses: aminya/setup-cpp@v1 62 | with: 63 | compiler: ${{ matrix.cpp }} 64 | vcvarsall: ${{ contains(matrix.os, 'windows') }} 65 | 66 | # Build and Test 67 | 68 | - name: Build 69 | run: cargo build 70 | 71 | - name: Test 72 | run: cargo test --all-targets 73 | -------------------------------------------------------------------------------- /src/ondemand/object_iterator.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{document::Document, field::Field}; 6 | use crate::{error::Result, macros::map_result}; 7 | 8 | pub struct ObjectIterator<'a> { 9 | begin: NonNull, 10 | end: NonNull, 11 | running: bool, 12 | _doc: PhantomData<&'a mut Document<'a, 'a>>, 13 | } 14 | 15 | impl<'a> ObjectIterator<'a> { 16 | pub fn new( 17 | begin: NonNull, 18 | end: NonNull, 19 | ) -> Self { 20 | Self { 21 | begin, 22 | end, 23 | running: false, 24 | _doc: PhantomData, 25 | } 26 | } 27 | 28 | pub fn get(&mut self) -> Result> { 29 | map_result!( 30 | ffi::SJ_OD_object_iterator_get(self.begin.as_mut()), 31 | ffi::SJ_OD_field_result_error, 32 | ffi::SJ_OD_field_result_value_unsafe 33 | ) 34 | .map(Field::new) 35 | } 36 | 37 | pub fn not_equal(&self) -> bool { 38 | unsafe { ffi::SJ_OD_object_iterator_not_equal(self.begin.as_ref(), self.end.as_ref()) } 39 | } 40 | 41 | pub fn step(&mut self) { 42 | unsafe { ffi::SJ_OD_object_iterator_step(self.begin.as_mut()) } 43 | } 44 | } 45 | 46 | impl<'a> Drop for ObjectIterator<'a> { 47 | fn drop(&mut self) { 48 | unsafe { 49 | ffi::SJ_OD_object_iterator_free(self.begin.as_mut()); 50 | ffi::SJ_OD_object_iterator_free(self.end.as_mut()); 51 | } 52 | } 53 | } 54 | 55 | impl<'a> Iterator for ObjectIterator<'a> { 56 | type Item = Result>; 57 | 58 | fn next(&mut self) -> Option { 59 | if self.running { 60 | self.step(); 61 | } 62 | 63 | if self.not_equal() { 64 | self.running = true; 65 | Some(self.get()) 66 | } else { 67 | self.running = false; 68 | None 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/ondemand/parser.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::NonNull; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::document::Document; 6 | use crate::{ 7 | error::Result, 8 | macros::{impl_drop, map_result}, 9 | }; 10 | 11 | pub struct Parser { 12 | ptr: NonNull, 13 | } 14 | 15 | impl Default for Parser { 16 | fn default() -> Self { 17 | Parser::new(ffi::SIMDJSON_MAXSIZE_BYTES) 18 | } 19 | } 20 | 21 | impl Parser { 22 | pub fn new(max_capacity: usize) -> Self { 23 | let ptr = unsafe { NonNull::new_unchecked(ffi::SJ_OD_parser_new(max_capacity)) }; 24 | Self { ptr } 25 | } 26 | 27 | pub fn iterate<'p, 's>(&'p mut self, padded_string: &'s String) -> Result> { 28 | map_result!( 29 | ffi::SJ_OD_parser_iterate_padded_string_view( 30 | self.ptr.as_mut(), 31 | padded_string.as_ptr().cast(), 32 | padded_string.len(), 33 | padded_string.capacity() 34 | ), 35 | ffi::SJ_OD_document_result_error, 36 | ffi::SJ_OD_document_result_value_unsafe 37 | ) 38 | .map(Document::new) 39 | } 40 | } 41 | 42 | impl_drop!(Parser, ffi::SJ_OD_parser_free); 43 | 44 | #[cfg(test)] 45 | mod tests { 46 | use super::*; 47 | use crate::padded_string::make_padded_string; 48 | 49 | #[test] 50 | fn test_new() { 51 | let mut parser = Parser::default(); 52 | let ps = make_padded_string("[1,2,3]"); 53 | let mut doc = parser.iterate(&ps).unwrap(); 54 | // drop(ps); 55 | // doc.get_array().unwrap(); 56 | let arr = doc.get_array().unwrap(); 57 | // drop(doc); 58 | // for v in arr.iter().unwrap() { 59 | // let _ = v.unwrap().get_uint64().unwrap(); 60 | // } 61 | // doc.get_value().unwrap(); 62 | 63 | drop(arr); 64 | drop(doc); 65 | 66 | let ps2 = make_padded_string("1"); 67 | let mut doc2 = parser.iterate(&ps2).unwrap(); 68 | let v = doc2.get_int64().unwrap(); 69 | assert_eq!(v, 1); 70 | let v = doc2.get_uint64().unwrap(); 71 | assert_eq!(v, 1); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_drop { 2 | ($name:ident < $($lt:lifetime),+ > , $free_fn:expr) => { 3 | impl<$($lt),+> Drop for $name<$($lt),+> { 4 | fn drop(&mut self) { 5 | unsafe { 6 | $free_fn(self.ptr.as_ptr()); 7 | } 8 | } 9 | } 10 | }; 11 | ($name:ty, $free_fn:expr) => { 12 | impl Drop for $name { 13 | fn drop(&mut self) { 14 | unsafe { 15 | $free_fn(self.ptr.as_ptr()); 16 | } 17 | } 18 | } 19 | }; 20 | } 21 | 22 | macro_rules! map_result { 23 | (primitive, $func_call:expr, $get_code:expr, $get_value:expr) => { 24 | unsafe { 25 | let ptr = $func_call; 26 | let code = $get_code(ptr); 27 | if code == 0 { 28 | Ok($get_value(ptr)) 29 | } else { 30 | Err(crate::error::SimdJsonError::from(code)) 31 | } 32 | } 33 | }; 34 | ($func_call:expr, $get_code:expr, $get_value:expr) => { 35 | unsafe { 36 | let ptr = $func_call; 37 | let code = $get_code(ptr); 38 | if code == 0 { 39 | Ok(std::ptr::NonNull::new_unchecked($get_value(ptr))) 40 | } else { 41 | Err(crate::error::SimdJsonError::from(code)) 42 | } 43 | } 44 | }; 45 | } 46 | 47 | macro_rules! map_ptr_result { 48 | ($call:expr) => { 49 | unsafe { 50 | let res = $call; 51 | if res.error == 0 { 52 | Ok(std::ptr::NonNull::new_unchecked(res.value)) 53 | } else { 54 | Err(crate::error::SimdJsonError::from(res.error)) 55 | } 56 | } 57 | }; 58 | } 59 | 60 | macro_rules! map_primitive_result { 61 | ($call:expr) => { 62 | unsafe { 63 | let res = $call; 64 | if res.error == 0 { 65 | Ok(res.value) 66 | } else { 67 | Err(crate::error::SimdJsonError::from(res.error)) 68 | } 69 | } 70 | }; 71 | } 72 | 73 | pub(crate) use impl_drop; 74 | pub(crate) use map_primitive_result; 75 | pub(crate) use map_ptr_result; 76 | pub(crate) use map_result; 77 | -------------------------------------------------------------------------------- /src/dom/document_stream.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::Element; 6 | use crate::{ 7 | macros::{impl_drop, map_ptr_result}, 8 | Result, 9 | }; 10 | 11 | pub struct DocumentStream { 12 | ptr: NonNull, 13 | } 14 | 15 | impl DocumentStream { 16 | pub fn new(ptr: NonNull) -> Self { 17 | Self { ptr } 18 | } 19 | 20 | pub fn iter(&self) -> DocumentStreamIter { 21 | let begin = 22 | unsafe { NonNull::new_unchecked(ffi::SJ_DOM_document_stream_begin(self.ptr.as_ptr())) }; 23 | let end = 24 | unsafe { NonNull::new_unchecked(ffi::SJ_DOM_document_stream_end(self.ptr.as_ptr())) }; 25 | DocumentStreamIter::new(begin, end) 26 | } 27 | } 28 | 29 | impl_drop!(DocumentStream, ffi::SJ_DOM_document_stream_free); 30 | 31 | pub struct DocumentStreamIter<'a> { 32 | begin: NonNull, 33 | end: NonNull, 34 | running: bool, 35 | _parser: PhantomData<&'a DocumentStream>, 36 | } 37 | 38 | impl<'a> DocumentStreamIter<'a> { 39 | pub fn new( 40 | begin: NonNull, 41 | end: NonNull, 42 | ) -> Self { 43 | Self { 44 | begin, 45 | end, 46 | running: false, 47 | _parser: PhantomData, 48 | } 49 | } 50 | 51 | pub fn get(&self) -> Result> { 52 | map_ptr_result!(ffi::SJ_DOM_document_stream_iterator_get( 53 | self.begin.as_ptr() 54 | )) 55 | .map(Element::new) 56 | } 57 | 58 | pub fn step(&mut self) { 59 | unsafe { ffi::SJ_DOM_document_stream_iterator_step(self.begin.as_ptr()) } 60 | } 61 | 62 | pub fn not_equal(&self) -> bool { 63 | unsafe { 64 | ffi::SJ_DOM_document_stream_iterator_not_equal(self.begin.as_ptr(), self.end.as_ptr()) 65 | } 66 | } 67 | } 68 | 69 | impl<'a> Iterator for DocumentStreamIter<'a> { 70 | type Item = Result>; 71 | 72 | fn next(&mut self) -> Option { 73 | if self.running { 74 | self.step(); 75 | } 76 | 77 | if self.not_equal() { 78 | self.running = true; 79 | Some(self.get()) 80 | } else { 81 | None 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/ondemand/array_iterator.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{document::Document, value::Value}; 6 | use crate::{error::Result, macros::map_result}; 7 | 8 | pub struct ArrayIterator<'a> { 9 | begin: NonNull, 10 | end: NonNull, 11 | running: bool, 12 | _doc: PhantomData<&'a mut Document<'a, 'a>>, 13 | } 14 | 15 | impl<'a> ArrayIterator<'a> { 16 | pub fn new( 17 | begin: NonNull, 18 | end: NonNull, 19 | ) -> Self { 20 | Self { 21 | begin, 22 | end, 23 | running: false, 24 | _doc: PhantomData, 25 | } 26 | } 27 | 28 | pub fn get(&mut self) -> Result> { 29 | map_result!( 30 | ffi::SJ_OD_array_iterator_get(self.begin.as_mut()), 31 | ffi::SJ_OD_value_result_error, 32 | ffi::SJ_OD_value_result_value_unsafe 33 | ) 34 | .map(Value::new) 35 | } 36 | 37 | pub fn not_equal(&self) -> bool { 38 | unsafe { ffi::SJ_OD_array_iterator_not_equal(self.begin.as_ref(), self.end.as_ref()) } 39 | } 40 | 41 | pub fn step(&mut self) { 42 | unsafe { ffi::SJ_OD_array_iterator_step(self.begin.as_mut()) } 43 | } 44 | } 45 | 46 | impl<'a> Drop for ArrayIterator<'a> { 47 | fn drop(&mut self) { 48 | unsafe { 49 | ffi::SJ_OD_array_iterator_free(self.begin.as_mut()); 50 | ffi::SJ_OD_array_iterator_free(self.end.as_mut()); 51 | } 52 | } 53 | } 54 | 55 | impl<'a> Iterator for ArrayIterator<'a> { 56 | type Item = Result>; 57 | 58 | fn next(&mut self) -> Option { 59 | if self.running { 60 | self.step(); 61 | } 62 | 63 | if self.not_equal() { 64 | self.running = true; 65 | Some(self.get()) 66 | } else { 67 | self.running = false; 68 | None 69 | } 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests { 75 | use crate::{ondemand::parser::Parser, padded_string::make_padded_string}; 76 | 77 | #[test] 78 | fn test_iter() { 79 | let mut parser = Parser::default(); 80 | let ps = make_padded_string("[1,2,3]"); 81 | let mut doc = parser.iterate(&ps).unwrap(); 82 | // drop(ps); 83 | let mut arr = doc.get_array().unwrap(); 84 | for (v, num) in arr.iter().unwrap().zip([1u64, 2, 3]) { 85 | let res = v.unwrap().get_uint64().unwrap(); 86 | assert_eq!(res, num); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/dom/object.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{document::Document, Element}; 6 | use crate::{macros::impl_drop, utils::string_view_struct_to_str}; 7 | 8 | pub struct Object<'a> { 9 | ptr: NonNull, 10 | _doc: PhantomData<&'a Document>, 11 | } 12 | 13 | impl<'a> Object<'a> { 14 | pub fn new(ptr: NonNull) -> Self { 15 | Self { 16 | ptr, 17 | _doc: PhantomData, 18 | } 19 | } 20 | 21 | pub fn iter(&self) -> ObjectIter { 22 | let begin = unsafe { NonNull::new_unchecked(ffi::SJ_DOM_object_begin(self.ptr.as_ptr())) }; 23 | let end = unsafe { NonNull::new_unchecked(ffi::SJ_DOM_object_end(self.ptr.as_ptr())) }; 24 | ObjectIter::new(begin, end) 25 | } 26 | } 27 | 28 | pub struct ObjectIter<'a> { 29 | begin: NonNull, 30 | end: NonNull, 31 | running: bool, 32 | _doc: PhantomData<&'a Document>, 33 | } 34 | 35 | impl<'a> ObjectIter<'a> { 36 | pub fn new( 37 | begin: NonNull, 38 | end: NonNull, 39 | ) -> Self { 40 | Self { 41 | begin, 42 | end, 43 | running: false, 44 | _doc: PhantomData, 45 | } 46 | } 47 | 48 | pub fn get(&self) -> (&'a str, Element<'a>) { 49 | let kv = unsafe { ffi::SJ_DOM_object_iterator_get(self.begin.as_ptr()) }; 50 | let key = string_view_struct_to_str(kv.key); 51 | let value = Element::new(unsafe { NonNull::new_unchecked(kv.value) }); 52 | (key, value) 53 | } 54 | 55 | pub fn step(&mut self) { 56 | unsafe { ffi::SJ_DOM_object_iterator_step(self.begin.as_ptr()) } 57 | } 58 | 59 | pub fn not_equal(&self) -> bool { 60 | unsafe { ffi::SJ_DOM_object_iterator_not_equal(self.begin.as_ptr(), self.end.as_ptr()) } 61 | } 62 | } 63 | 64 | impl<'a> Drop for ObjectIter<'a> { 65 | fn drop(&mut self) { 66 | unsafe { 67 | ffi::SJ_DOM_object_iterator_free(self.begin.as_ptr()); 68 | ffi::SJ_DOM_object_iterator_free(self.end.as_ptr()); 69 | } 70 | } 71 | } 72 | 73 | impl<'a> Iterator for ObjectIter<'a> { 74 | type Item = (&'a str, Element<'a>); 75 | 76 | fn next(&mut self) -> Option { 77 | if self.running { 78 | self.step(); 79 | } 80 | 81 | if self.not_equal() { 82 | self.running = true; 83 | Some(self.get()) 84 | } else { 85 | None 86 | } 87 | } 88 | } 89 | 90 | impl_drop!(Object<'a>, ffi::SJ_DOM_object_free); 91 | -------------------------------------------------------------------------------- /src/dom/parser.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::NonNull; 2 | 3 | use ffi::DEFAULT_BATCH_SIZE; 4 | use simdjson_sys as ffi; 5 | 6 | use super::{document::Document, document_stream::DocumentStream, element::Element}; 7 | use crate::{ 8 | macros::{impl_drop, map_ptr_result}, 9 | Result, 10 | }; 11 | 12 | pub struct Parser { 13 | ptr: NonNull, 14 | } 15 | 16 | impl Default for Parser { 17 | fn default() -> Self { 18 | Self::new(ffi::SIMDJSON_MAXSIZE_BYTES) 19 | } 20 | } 21 | 22 | impl Parser { 23 | pub fn new(max_capacity: usize) -> Self { 24 | let ptr = unsafe { NonNull::new_unchecked(ffi::SJ_DOM_parser_new(max_capacity)) }; 25 | Self { ptr } 26 | } 27 | 28 | pub fn parse(&mut self, padded_string: &String) -> Result { 29 | map_ptr_result!(ffi::SJ_DOM_parser_parse( 30 | self.ptr.as_ptr(), 31 | padded_string.as_ptr().cast(), 32 | padded_string.len() 33 | )) 34 | .map(Element::new) 35 | } 36 | 37 | pub fn parse_into_document<'d>( 38 | &self, 39 | doc: &'d mut Document, 40 | padded_string: &String, 41 | ) -> Result> { 42 | map_ptr_result!(ffi::SJ_DOM_parser_parse_into_document( 43 | self.ptr.as_ptr(), 44 | doc.as_ptr(), 45 | padded_string.as_ptr().cast(), 46 | padded_string.len() 47 | )) 48 | .map(Element::new) 49 | } 50 | 51 | pub fn parse_many(&mut self, padded_string: &String) -> Result { 52 | self.parse_batch(padded_string, DEFAULT_BATCH_SIZE) 53 | } 54 | 55 | pub fn parse_batch( 56 | &mut self, 57 | padded_string: &String, 58 | batch_size: usize, 59 | ) -> Result { 60 | map_ptr_result!(ffi::SJ_DOM_parser_parse_many( 61 | self.ptr.as_ptr(), 62 | padded_string.as_ptr().cast(), 63 | padded_string.len(), 64 | batch_size 65 | )) 66 | .map(DocumentStream::new) 67 | } 68 | } 69 | 70 | impl_drop!(Parser, ffi::SJ_DOM_parser_free); 71 | 72 | #[cfg(test)] 73 | mod tests { 74 | use super::*; 75 | use crate::prelude::*; 76 | 77 | #[test] 78 | fn parse() { 79 | let ps = "1".to_padded_string(); 80 | let mut parser = Parser::default(); 81 | let elem = parser.parse(&ps).unwrap(); 82 | assert_eq!(elem.get_uint64().unwrap(), 1); 83 | } 84 | 85 | #[test] 86 | fn parse_into_document() { 87 | let ps = "[1,2,3]".to_padded_string(); 88 | let parser = Parser::default(); 89 | let mut doc = Document::default(); 90 | let elem = parser.parse_into_document(&mut doc, &ps).unwrap(); 91 | assert_eq!( 92 | elem.get_array() 93 | .unwrap() 94 | .at(0) 95 | .unwrap() 96 | .get_uint64() 97 | .unwrap(), 98 | 1 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/dom/element.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{array::Array, document::Document, object::Object}; 6 | use crate::{ 7 | macros::{impl_drop, map_primitive_result, map_ptr_result}, 8 | utils::string_view_struct_to_str, 9 | Result, 10 | }; 11 | 12 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 13 | pub enum ElementType { 14 | Array = '[' as _, 15 | Object = '{' as _, 16 | Int64 = 'l' as _, 17 | UInt64 = 'u' as _, 18 | Double = 'd' as _, 19 | String = '"' as _, 20 | Bool = 't' as _, 21 | NullValue = 'n' as _, 22 | } 23 | 24 | impl From for ElementType { 25 | fn from(value: i32) -> Self { 26 | match value as u8 as char { 27 | '[' => Self::Array, 28 | '{' => Self::Object, 29 | 'l' => Self::Int64, 30 | 'u' => Self::UInt64, 31 | 'd' => Self::Double, 32 | '"' => Self::String, 33 | 't' => Self::Bool, 34 | 'n' => Self::NullValue, 35 | _ => unreachable!(), 36 | } 37 | } 38 | } 39 | 40 | pub struct Element<'a> { 41 | ptr: NonNull, 42 | _doc: PhantomData<&'a Document>, 43 | } 44 | 45 | impl<'a> Element<'a> { 46 | pub fn new(ptr: NonNull) -> Self { 47 | Self { 48 | ptr, 49 | _doc: PhantomData, 50 | } 51 | } 52 | 53 | pub fn get_type(&self) -> ElementType { 54 | unsafe { ElementType::from(ffi::SJ_DOM_element_type(self.ptr.as_ptr())) } 55 | } 56 | 57 | pub fn get_array(&self) -> Result { 58 | map_ptr_result!(ffi::SJ_DOM_element_get_array(self.ptr.as_ptr())).map(Array::new) 59 | } 60 | 61 | pub fn get_object(&self) -> Result { 62 | map_ptr_result!(ffi::SJ_DOM_element_get_object(self.ptr.as_ptr())).map(Object::new) 63 | } 64 | 65 | pub fn get_string(&self) -> Result<&'a str> { 66 | map_primitive_result!(ffi::SJ_DOM_element_get_string(self.ptr.as_ptr())) 67 | .map(string_view_struct_to_str) 68 | } 69 | 70 | pub fn get_int64(&self) -> Result { 71 | map_primitive_result!(ffi::SJ_DOM_element_get_int64(self.ptr.as_ptr())) 72 | } 73 | 74 | pub fn get_uint64(&self) -> Result { 75 | map_primitive_result!(ffi::SJ_DOM_element_get_uint64(self.ptr.as_ptr())) 76 | } 77 | 78 | pub fn get_double(&self) -> Result { 79 | map_primitive_result!(ffi::SJ_DOM_element_get_double(self.ptr.as_ptr())) 80 | } 81 | 82 | pub fn get_bool(&self) -> Result { 83 | map_primitive_result!(ffi::SJ_DOM_element_get_bool(self.ptr.as_ptr())) 84 | } 85 | 86 | pub fn at_pointer(&self, json_pointer: &str) -> Result { 87 | map_ptr_result!(ffi::SJ_DOM_element_at_pointer( 88 | self.ptr.as_ptr(), 89 | json_pointer.as_ptr().cast(), 90 | json_pointer.len() 91 | )) 92 | .map(Element::new) 93 | } 94 | } 95 | 96 | impl_drop!(Element<'a>, ffi::SJ_DOM_element_free); 97 | -------------------------------------------------------------------------------- /src/ondemand/array.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{array_iterator::ArrayIterator, document::Document, value::Value}; 6 | use crate::{ 7 | error::Result, 8 | macros::{impl_drop, map_result}, 9 | utils::string_view_to_str, 10 | }; 11 | 12 | pub struct Array<'a> { 13 | ptr: NonNull, 14 | _doc: PhantomData<&'a mut Document<'a, 'a>>, 15 | } 16 | 17 | impl<'a> Array<'a> { 18 | pub fn new(ptr: NonNull) -> Self { 19 | Self { 20 | ptr, 21 | _doc: PhantomData, 22 | } 23 | } 24 | 25 | pub fn count_elements(&mut self) -> Result { 26 | map_result!( 27 | primitive, 28 | ffi::SJ_OD_array_count_elements(self.ptr.as_mut()), 29 | ffi::size_t_result_error, 30 | ffi::size_t_result_value_unsafe 31 | ) 32 | } 33 | 34 | pub fn is_empty(&mut self) -> Result { 35 | map_result!( 36 | primitive, 37 | ffi::SJ_OD_array_is_empty(self.ptr.as_mut()), 38 | ffi::bool_result_error, 39 | ffi::bool_result_value_unsafe 40 | ) 41 | } 42 | 43 | pub fn at(&mut self, index: usize) -> Result> { 44 | map_result!( 45 | ffi::SJ_OD_array_at(self.ptr.as_mut(), index), 46 | ffi::SJ_OD_value_result_error, 47 | ffi::SJ_OD_value_result_value_unsafe 48 | ) 49 | .map(Value::new) 50 | } 51 | 52 | pub fn at_pointer(&mut self, key: &str) -> Result> { 53 | map_result!( 54 | ffi::SJ_OD_array_at_pointer(self.ptr.as_mut(), key.as_ptr().cast(), key.len()), 55 | ffi::SJ_OD_value_result_error, 56 | ffi::SJ_OD_value_result_value_unsafe 57 | ) 58 | .map(Value::new) 59 | } 60 | 61 | pub fn reset(&mut self) -> Result { 62 | map_result!( 63 | primitive, 64 | ffi::SJ_OD_array_reset(self.ptr.as_mut()), 65 | ffi::bool_result_error, 66 | ffi::bool_result_value_unsafe 67 | ) 68 | } 69 | 70 | pub fn iter(&mut self) -> Result { 71 | let begin = map_result!( 72 | ffi::SJ_OD_array_begin(self.ptr.as_mut()), 73 | ffi::SJ_OD_array_iterator_result_error, 74 | ffi::SJ_OD_array_iterator_result_value_unsafe 75 | )?; 76 | let end = map_result!( 77 | ffi::SJ_OD_array_end(self.ptr.as_mut()), 78 | ffi::SJ_OD_array_iterator_result_error, 79 | ffi::SJ_OD_array_iterator_result_value_unsafe 80 | )?; 81 | Ok(ArrayIterator::new(begin, end)) 82 | } 83 | 84 | pub fn raw_json(&mut self) -> Result<&'a str> { 85 | let sv = map_result!( 86 | ffi::SJ_OD_array_raw_json(self.ptr.as_mut()), 87 | ffi::STD_string_view_result_error, 88 | ffi::STD_string_view_result_value_unsafe 89 | )?; 90 | Ok(string_view_to_str(sv)) 91 | } 92 | } 93 | 94 | impl_drop!(Array<'a>, ffi::SJ_OD_array_free); 95 | -------------------------------------------------------------------------------- /src/dom/array.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{document::Document, element::Element}; 6 | use crate::{ 7 | macros::{impl_drop, map_ptr_result}, 8 | Result, 9 | }; 10 | 11 | pub struct Array<'a> { 12 | ptr: NonNull, 13 | _doc: PhantomData<&'a Document>, 14 | } 15 | 16 | impl<'a> Array<'a> { 17 | pub fn new(ptr: NonNull) -> Self { 18 | Self { 19 | ptr, 20 | _doc: PhantomData, 21 | } 22 | } 23 | 24 | pub fn iter(&self) -> ArrayIter { 25 | let begin = unsafe { NonNull::new_unchecked(ffi::SJ_DOM_array_begin(self.ptr.as_ptr())) }; 26 | let end = unsafe { NonNull::new_unchecked(ffi::SJ_DOM_array_end(self.ptr.as_ptr())) }; 27 | ArrayIter::new(begin, end) 28 | } 29 | 30 | pub fn size(&self) -> usize { 31 | unsafe { ffi::SJ_DOM_array_size(self.ptr.as_ptr()) } 32 | } 33 | 34 | pub fn number_of_slots(&self) -> usize { 35 | unsafe { ffi::SJ_DOM_array_number_of_slots(self.ptr.as_ptr()) } 36 | } 37 | 38 | pub fn at_pointer(&self, json_pointer: &str) -> Result> { 39 | map_ptr_result!(ffi::SJ_DOM_array_at_pointer( 40 | self.ptr.as_ptr(), 41 | json_pointer.as_ptr().cast(), 42 | json_pointer.len() 43 | )) 44 | .map(Element::new) 45 | } 46 | 47 | pub fn at(&self, index: usize) -> Result> { 48 | map_ptr_result!(ffi::SJ_DOM_array_at(self.ptr.as_ptr(), index)).map(Element::new) 49 | } 50 | } 51 | 52 | impl_drop!(Array<'a>, ffi::SJ_DOM_array_free); 53 | 54 | pub struct ArrayIter<'a> { 55 | begin: NonNull, 56 | end: NonNull, 57 | running: bool, 58 | _doc: PhantomData<&'a Document>, 59 | } 60 | 61 | impl<'a> ArrayIter<'a> { 62 | pub fn new( 63 | begin: NonNull, 64 | end: NonNull, 65 | ) -> Self { 66 | Self { 67 | begin, 68 | end, 69 | running: false, 70 | _doc: PhantomData, 71 | } 72 | } 73 | 74 | pub fn get(&self) -> Element<'a> { 75 | let ptr = unsafe { ffi::SJ_DOM_array_iterator_get(self.begin.as_ptr()) }; 76 | Element::new(unsafe { NonNull::new_unchecked(ptr) }) 77 | } 78 | 79 | pub fn step(&mut self) { 80 | unsafe { ffi::SJ_DOM_array_iterator_step(self.begin.as_ptr()) } 81 | } 82 | 83 | pub fn not_equal(&self) -> bool { 84 | unsafe { ffi::SJ_DOM_array_iterator_not_equal(self.begin.as_ptr(), self.end.as_ptr()) } 85 | } 86 | } 87 | 88 | impl<'a> Drop for ArrayIter<'a> { 89 | fn drop(&mut self) { 90 | unsafe { 91 | ffi::SJ_DOM_array_iterator_free(self.begin.as_ptr()); 92 | ffi::SJ_DOM_array_iterator_free(self.end.as_ptr()); 93 | } 94 | } 95 | } 96 | 97 | impl<'a> Iterator for ArrayIter<'a> { 98 | type Item = Element<'a>; 99 | 100 | fn next(&mut self) -> Option { 101 | if self.running { 102 | self.step(); 103 | } 104 | 105 | if self.not_equal() { 106 | self.running = true; 107 | Some(self.get()) 108 | } else { 109 | None 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/ondemand/object.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{document::Document, object_iterator::ObjectIterator, value::Value}; 6 | use crate::{ 7 | error::Result, 8 | macros::{impl_drop, map_result}, 9 | utils::string_view_to_str, 10 | }; 11 | 12 | pub struct Object<'a> { 13 | ptr: NonNull, 14 | _doc: PhantomData<&'a mut Document<'a, 'a>>, 15 | } 16 | 17 | impl<'a> Object<'a> { 18 | pub fn new(ptr: NonNull) -> Self { 19 | Self { 20 | ptr, 21 | _doc: PhantomData, 22 | } 23 | } 24 | 25 | pub fn at_pointer(&mut self, json_pointer: &str) -> Result> { 26 | map_result!( 27 | ffi::SJ_OD_object_at_pointer( 28 | self.ptr.as_mut(), 29 | json_pointer.as_ptr().cast(), 30 | json_pointer.len() 31 | ), 32 | ffi::SJ_OD_value_result_error, 33 | ffi::SJ_OD_value_result_value_unsafe 34 | ) 35 | .map(Value::new) 36 | } 37 | 38 | pub fn iter(&mut self) -> Result> { 39 | let begin = map_result!( 40 | ffi::SJ_OD_object_begin(self.ptr.as_mut()), 41 | ffi::SJ_OD_object_iterator_result_error, 42 | ffi::SJ_OD_object_iterator_result_value_unsafe 43 | )?; 44 | let end = map_result!( 45 | ffi::SJ_OD_object_end(self.ptr.as_mut()), 46 | ffi::SJ_OD_object_iterator_result_error, 47 | ffi::SJ_OD_object_iterator_result_value_unsafe 48 | )?; 49 | Ok(ObjectIterator::new(begin, end)) 50 | } 51 | 52 | pub fn raw_json(&mut self) -> Result<&'a str> { 53 | let sv = map_result!( 54 | ffi::SJ_OD_object_raw_json(self.ptr.as_mut()), 55 | ffi::STD_string_view_result_error, 56 | ffi::STD_string_view_result_value_unsafe 57 | )?; 58 | Ok(string_view_to_str(sv)) 59 | } 60 | 61 | pub fn find_field(&mut self, key: &str) -> Result> { 62 | map_result!( 63 | ffi::SJ_OD_object_find_field(self.ptr.as_mut(), key.as_ptr().cast(), key.len()), 64 | ffi::SJ_OD_value_result_error, 65 | ffi::SJ_OD_value_result_value_unsafe 66 | ) 67 | .map(Value::new) 68 | } 69 | 70 | pub fn find_field_unordered(&mut self, key: &str) -> Result> { 71 | map_result!( 72 | ffi::SJ_OD_object_find_field_unordered( 73 | self.ptr.as_mut(), 74 | key.as_ptr().cast(), 75 | key.len() 76 | ), 77 | ffi::SJ_OD_value_result_error, 78 | ffi::SJ_OD_value_result_value_unsafe 79 | ) 80 | .map(Value::new) 81 | } 82 | 83 | pub fn count_fields(&mut self) -> Result { 84 | map_result!( 85 | primitive, 86 | ffi::SJ_OD_object_count_fields(self.ptr.as_mut()), 87 | ffi::size_t_result_error, 88 | ffi::size_t_result_value_unsafe 89 | ) 90 | } 91 | 92 | pub fn is_empty(&mut self) -> Result { 93 | map_result!( 94 | primitive, 95 | ffi::SJ_OD_object_is_empty(self.ptr.as_mut()), 96 | ffi::bool_result_error, 97 | ffi::bool_result_value_unsafe 98 | ) 99 | } 100 | 101 | pub fn reset(&mut self) -> Result { 102 | map_result!( 103 | primitive, 104 | ffi::SJ_OD_object_reset(self.ptr.as_mut()), 105 | ffi::bool_result_error, 106 | ffi::bool_result_value_unsafe 107 | ) 108 | } 109 | } 110 | 111 | impl_drop!(Object<'a>, ffi::SJ_OD_object_free); 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simdjson-rust 2 | 3 | [![Github Actions](https://img.shields.io/github/actions/workflow/status/SunDoge/simdjson-rust/CI.yml?branch=master&style=for-the-badge)](https://github.com/SunDoge/simdjson-rust/actions/workflows/CI.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/simdjson-rust?style=for-the-badge)](https://crates.io/crates/simdjson-rust) 5 | [![docs.rs](https://img.shields.io/docsrs/simdjson-rust/latest?style=for-the-badge)](https://docs.rs/simdjson-rust) 6 | 7 | This crate currently uses [`simdjson 3.2.3`][simdjson]. You can have a try and give feedback. 8 | 9 | If you 10 | 11 | - find certain APIs are missing 12 | - encounter memory errors 13 | - experience performance degradation 14 | 15 | Please submit an issue. 16 | 17 | ## Benchmark 18 | 19 | Check [SunDoge/json-benchmark](https://github.com/SunDoge/json-benchmark/tree/simdjson-rust) 20 | 21 | ``` 22 | DOM STRUCT 23 | ======= serde_json ======= parse|stringify ===== parse|stringify ==== 24 | data/canada.json 400 MB/s 460 MB/s 500 MB/s 390 MB/s 25 | data/citm_catalog.json 540 MB/s 790 MB/s 1000 MB/s 1070 MB/s 26 | data/twitter.json 390 MB/s 1020 MB/s 680 MB/s 1090 MB/s 27 | 28 | ======= simd-json ======== parse|stringify ===== parse|stringify ==== 29 | data/canada.json 400 MB/s 540 MB/s 530 MB/s 30 | data/citm_catalog.json 1080 MB/s 1000 MB/s 1570 MB/s 31 | data/twitter.json 1100 MB/s 1460 MB/s 1180 MB/s 32 | 33 | ===== simdjson-rust ====== parse|stringify ===== parse|stringify ==== 34 | data/canada.json 960 MB/s 35 | data/citm_catalog.json 3110 MB/s 36 | data/twitter.json 3160 MB/s 37 | ``` 38 | 39 | ## Usage 40 | 41 | Add this to your `Cargo.toml` 42 | 43 | ```toml 44 | # In the `[dependencies]` section 45 | simdjson-rust = "0.3.0" 46 | ``` 47 | 48 | Then, get started. 49 | 50 | ```rust 51 | use simdjson_rust::prelude::*; 52 | use simdjson_rust::{dom, ondemand}; 53 | 54 | fn main() -> simdjson_rust::Result<()> { 55 | let ps = "[0,1,2,3]".to_padded_string(); 56 | 57 | // ondemand api. 58 | { 59 | let mut parser = ondemand::Parser::default(); 60 | let mut doc = parser.iterate(&ps)?; 61 | let mut array = doc.get_array()?; 62 | for (index, value) in array.iter()?.enumerate() { 63 | assert_eq!(index as u64, value?.get_uint64()?); 64 | } 65 | } 66 | 67 | // dom api. 68 | { 69 | let mut parser = dom::Parser::default(); 70 | let elem = parser.parse(&ps)?; 71 | let arr = elem.get_array()?; 72 | for (index, value) in arr.iter().enumerate() { 73 | assert_eq!(index as u64, value.get_uint64()?); 74 | } 75 | } 76 | 77 | Ok(()) 78 | } 79 | ``` 80 | 81 | ### `dom` and `ondemand` 82 | 83 | [`simdjson`][simdjson] now offer two kinds of API, `dom` and `ondemand`. 84 | `dom` will parsed the whole string while `ondemand` only parse what you request. 85 | Due to `ffi`, the overhead of `ondemand` API is relatively high. I have tested `lto` but it only improves a little :( 86 | 87 | Thus it is suggestted that 88 | 89 | - use `ondemand` if you only want to access a specific part of a large json, 90 | - use `dom` if you want to parse the whole json. 91 | 92 | 93 | ### `padded_string` 94 | 95 | [`simdjson`][simdjson] requires the input string to be padded. We must provide a string with `capacity = len + SIMDJSON_PADDING`. 96 | We provide utils to do so. 97 | 98 | ```rust 99 | use simdjson_rust::prelude::*; 100 | 101 | fn main() -> simdjson_rust::Result<()> { 102 | let ps = make_padded_string("[0,1,2,3]"); 103 | let ps = "[0,1,2,3]".to_padded_string(); 104 | // or reuse a buffer. 105 | let unpadded = String::from("[1,2,3,4]"); 106 | let ps = unpadded.into_padded_string(); 107 | // or load from file. 108 | let ps = load_padded_string("test.json")?; 109 | Ok(()) 110 | } 111 | ``` 112 | 113 | ## Other interesting things 114 | 115 | There are also pure Rust port of [`simdjson`][simdjson] available here [`simd-json`](https://github.com/simd-lite/simd-json). 116 | 117 | 118 | [simdjson]: https://github.com/simdjson/simdjson -------------------------------------------------------------------------------- /src/ondemand/value.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{array::Array, document::Document, number::Number, object::Object, JsonType}; 6 | use crate::{ 7 | error::Result, 8 | macros::{impl_drop, map_result}, 9 | utils::string_view_to_str, 10 | }; 11 | 12 | pub struct Value<'a> { 13 | ptr: NonNull, 14 | _doc: PhantomData<&'a mut Document<'a, 'a>>, 15 | } 16 | 17 | impl<'a> Value<'a> { 18 | pub fn new(ptr: NonNull) -> Self { 19 | Self { 20 | ptr, 21 | _doc: PhantomData, 22 | } 23 | } 24 | 25 | pub fn get_uint64(&mut self) -> Result { 26 | map_result!( 27 | primitive, 28 | ffi::SJ_OD_value_get_uint64(self.ptr.as_mut()), 29 | ffi::uint64_t_result_error, 30 | ffi::uint64_t_result_value_unsafe 31 | ) 32 | } 33 | 34 | pub fn get_int64(&mut self) -> Result { 35 | map_result!( 36 | primitive, 37 | ffi::SJ_OD_value_get_int64(self.ptr.as_mut()), 38 | ffi::int64_t_result_error, 39 | ffi::int64_t_result_value_unsafe 40 | ) 41 | } 42 | 43 | pub fn get_bool(&mut self) -> Result { 44 | map_result!( 45 | primitive, 46 | ffi::SJ_OD_value_get_bool(self.ptr.as_mut()), 47 | ffi::bool_result_error, 48 | ffi::bool_result_value_unsafe 49 | ) 50 | } 51 | 52 | pub fn get_double(&mut self) -> Result { 53 | map_result!( 54 | primitive, 55 | ffi::SJ_OD_value_get_double(self.ptr.as_mut()), 56 | ffi::double_result_error, 57 | ffi::double_result_value_unsafe 58 | ) 59 | } 60 | 61 | pub fn get_array(&mut self) -> Result> { 62 | map_result!( 63 | ffi::SJ_OD_value_get_array(self.ptr.as_mut()), 64 | ffi::SJ_OD_array_result_error, 65 | ffi::SJ_OD_array_result_value_unsafe 66 | ) 67 | .map(Array::new) 68 | } 69 | 70 | pub fn get_object(&mut self) -> Result> { 71 | map_result!( 72 | ffi::SJ_OD_value_get_object(self.ptr.as_mut()), 73 | ffi::SJ_OD_object_result_error, 74 | ffi::SJ_OD_object_result_value_unsafe 75 | ) 76 | .map(Object::new) 77 | } 78 | 79 | pub fn get_number(&mut self) -> Result> { 80 | map_result!( 81 | ffi::SJ_OD_value_get_number(self.ptr.as_mut()), 82 | ffi::SJ_OD_number_result_error, 83 | ffi::SJ_OD_number_result_value_unsafe 84 | ) 85 | .map(Number::new) 86 | } 87 | 88 | pub fn get_string(&mut self, allow_replacement: bool) -> Result<&'a str> { 89 | let sv = map_result!( 90 | ffi::SJ_OD_value_get_string(self.ptr.as_mut(), allow_replacement), 91 | ffi::STD_string_view_result_error, 92 | ffi::STD_string_view_result_value_unsafe 93 | )?; 94 | Ok(string_view_to_str(sv)) 95 | } 96 | 97 | pub fn at_pointer(&mut self, json_pointer: &str) -> Result> { 98 | map_result!( 99 | ffi::SJ_OD_value_at_pointer( 100 | self.ptr.as_mut(), 101 | json_pointer.as_ptr().cast(), 102 | json_pointer.len() 103 | ), 104 | ffi::SJ_OD_value_result_error, 105 | ffi::SJ_OD_value_result_value_unsafe 106 | ) 107 | .map(Value::new) 108 | } 109 | 110 | pub fn is_null(&mut self) -> Result { 111 | map_result!( 112 | primitive, 113 | ffi::SJ_OD_value_is_null(self.ptr.as_mut()), 114 | ffi::bool_result_error, 115 | ffi::bool_result_value_unsafe 116 | ) 117 | } 118 | 119 | pub fn json_type(&mut self) -> Result { 120 | let json_type = map_result!( 121 | primitive, 122 | ffi::SJ_OD_value_type(self.ptr.as_mut()), 123 | ffi::int_result_error, 124 | ffi::int_result_value_unsafe 125 | )?; 126 | Ok(JsonType::from(json_type)) 127 | } 128 | } 129 | 130 | impl_drop!(Value<'a>, ffi::SJ_OD_value_free); 131 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | pub type Result = std::result::Result; 4 | 5 | #[derive(Debug, Error)] 6 | pub enum SimdJsonError { 7 | #[error("This parser can't support a document that big")] 8 | Capacity, 9 | 10 | #[error("Error allocating memory, we're most likely out of memory")] 11 | MemAlloc, 12 | 13 | #[error("Something went wrong while writing to the tape")] 14 | TapeError, 15 | 16 | #[error("The JSON document was too deep (too many nested objects and arrays)")] 17 | DepthError, 18 | 19 | #[error("Problem while parsing a string")] 20 | StringError, 21 | 22 | #[error("Problem while parsing an atom starting with the letter 't'")] 23 | TAtomError, 24 | 25 | #[error("Problem while parsing an atom starting with the letter 'f'")] 26 | FAtomError, 27 | 28 | #[error("Problem while parsing an atom starting with the letter 'n'")] 29 | NAtomError, 30 | 31 | #[error("Problem while parsing a number")] 32 | NumberError, 33 | 34 | #[error("The input is not valid UTF-8")] 35 | Utf8Error, 36 | 37 | #[error("Uninitialized")] 38 | Uninitialized, 39 | 40 | #[error("Empty: no JSON found")] 41 | Empty, 42 | 43 | #[error("Within strings, some characters must be escaped, we found unescaped characters")] 44 | UnescapedChars, 45 | 46 | #[error("A string is opened, but never closed.")] 47 | UnclosedString, 48 | 49 | #[error( 50 | "simdjson does not have an implementation supported by this CPU architecture (perhaps \ 51 | it's a non-SIMD CPU?)." 52 | )] 53 | UnsupportedArchitecture, 54 | 55 | #[error("The JSON element does not have the requested type.")] 56 | IncorrectType, 57 | 58 | #[error("The JSON number is too large or too small to fit within the requested type.")] 59 | NumberOutOfRange, 60 | 61 | #[error("Attempted to access an element of a JSON array that is beyond its length.")] 62 | IndexOutOfBounds, 63 | 64 | #[error("The JSON field referenced does not exist in this object.")] 65 | NoSuchField, 66 | 67 | #[error("Error reading the file.")] 68 | IoError, 69 | 70 | #[error("Invalid JSON pointer syntax.")] 71 | InvalidJsonPointer, 72 | 73 | #[error("Invalid URI fragment syntax.")] 74 | InvalidUriFragment, 75 | 76 | #[error("todo")] 77 | UnexpectedError, 78 | 79 | #[error("todo")] 80 | ParserInUse, 81 | 82 | #[error("todo")] 83 | OutOfOrderIteration, 84 | 85 | #[error("todo")] 86 | InsufficientPadding, 87 | 88 | #[error("todo")] 89 | IncompleteArrayOrObject, 90 | 91 | #[error("todo")] 92 | ScalarDocumentAsValue, 93 | 94 | #[error("todo")] 95 | OutOfBounds, 96 | 97 | #[error("todo")] 98 | TailingContent, 99 | 100 | #[error("todo")] 101 | NumErrorCodes, 102 | 103 | #[error("todo")] 104 | StdIoError(#[from] std::io::Error), 105 | } 106 | 107 | impl From for SimdJsonError { 108 | fn from(error_code: i32) -> Self { 109 | match error_code { 110 | 1 => SimdJsonError::Capacity, 111 | 2 => SimdJsonError::MemAlloc, 112 | 3 => SimdJsonError::TapeError, 113 | 4 => SimdJsonError::DepthError, 114 | 5 => SimdJsonError::StringError, 115 | 6 => SimdJsonError::TAtomError, 116 | 7 => SimdJsonError::FAtomError, 117 | 8 => SimdJsonError::NAtomError, 118 | 9 => SimdJsonError::NumberError, 119 | 10 => SimdJsonError::Utf8Error, 120 | 11 => SimdJsonError::Uninitialized, 121 | 12 => SimdJsonError::Empty, 122 | 13 => SimdJsonError::UnescapedChars, 123 | 14 => SimdJsonError::UnclosedString, 124 | 15 => SimdJsonError::UnsupportedArchitecture, 125 | 16 => SimdJsonError::IncorrectType, 126 | 17 => SimdJsonError::NumberOutOfRange, 127 | 18 => SimdJsonError::IndexOutOfBounds, 128 | 19 => SimdJsonError::NoSuchField, 129 | 20 => SimdJsonError::IoError, 130 | 21 => SimdJsonError::InvalidJsonPointer, 131 | 22 => SimdJsonError::InvalidUriFragment, 132 | 23 => SimdJsonError::UnexpectedError, 133 | 24 => SimdJsonError::ParserInUse, 134 | 25 => SimdJsonError::OutOfOrderIteration, 135 | 26 => SimdJsonError::InsufficientPadding, 136 | 27 => SimdJsonError::IncompleteArrayOrObject, 137 | 28 => SimdJsonError::ScalarDocumentAsValue, 138 | 29 => SimdJsonError::OutOfBounds, 139 | 30 => SimdJsonError::TailingContent, 140 | 31 => SimdJsonError::NumErrorCodes, 141 | x => panic!("Unknown error code: {}", x), 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/ondemand/document.rs: -------------------------------------------------------------------------------- 1 | use std::{marker::PhantomData, ptr::NonNull}; 2 | 3 | use simdjson_sys as ffi; 4 | 5 | use super::{array::Array, number::Number, object::Object, parser::Parser, value::Value, JsonType}; 6 | use crate::{ 7 | error::Result, 8 | macros::{impl_drop, map_result}, 9 | utils::string_view_to_str, 10 | }; 11 | 12 | pub struct Document<'p, 's> { 13 | ptr: NonNull, 14 | _parser: PhantomData<&'p mut Parser>, 15 | _padded_string: PhantomData<&'s String>, 16 | } 17 | impl<'p, 's> Document<'p, 's> { 18 | pub fn new(ptr: NonNull) -> Self { 19 | Self { 20 | ptr, 21 | _parser: PhantomData, 22 | _padded_string: PhantomData, 23 | } 24 | } 25 | 26 | pub fn get_uint64(&mut self) -> Result { 27 | map_result!( 28 | primitive, 29 | ffi::SJ_OD_document_get_uint64(self.ptr.as_mut()), 30 | ffi::uint64_t_result_error, 31 | ffi::uint64_t_result_value_unsafe 32 | ) 33 | } 34 | 35 | pub fn get_int64(&mut self) -> Result { 36 | map_result!( 37 | primitive, 38 | ffi::SJ_OD_document_get_int64(self.ptr.as_mut()), 39 | ffi::int64_t_result_error, 40 | ffi::int64_t_result_value_unsafe 41 | ) 42 | } 43 | 44 | pub fn get_bool(&mut self) -> Result { 45 | map_result!( 46 | primitive, 47 | ffi::SJ_OD_document_get_bool(self.ptr.as_mut()), 48 | ffi::bool_result_error, 49 | ffi::bool_result_value_unsafe 50 | ) 51 | } 52 | 53 | pub fn get_double(&mut self) -> Result { 54 | map_result!( 55 | primitive, 56 | ffi::SJ_OD_document_get_double(self.ptr.as_mut()), 57 | ffi::double_result_error, 58 | ffi::double_result_value_unsafe 59 | ) 60 | } 61 | 62 | pub fn get_value<'a>(&mut self) -> Result> { 63 | map_result!( 64 | ffi::SJ_OD_document_get_value(self.ptr.as_mut()), 65 | ffi::SJ_OD_value_result_error, 66 | ffi::SJ_OD_value_result_value_unsafe 67 | ) 68 | .map(Value::new) 69 | } 70 | 71 | pub fn get_array<'a>(&mut self) -> Result> { 72 | map_result!( 73 | ffi::SJ_OD_document_get_array(self.ptr.as_mut()), 74 | ffi::SJ_OD_array_result_error, 75 | ffi::SJ_OD_array_result_value_unsafe 76 | ) 77 | .map(Array::new) 78 | } 79 | 80 | pub fn get_object<'a>(&mut self) -> Result> { 81 | map_result!( 82 | ffi::SJ_OD_document_get_object(self.ptr.as_mut()), 83 | ffi::SJ_OD_object_result_error, 84 | ffi::SJ_OD_object_result_value_unsafe 85 | ) 86 | .map(Object::new) 87 | } 88 | 89 | pub fn get_wobbly_string<'a>(&mut self) -> Result<&'a str> { 90 | let sv = map_result!( 91 | ffi::SJ_OD_document_get_wobbly_string(self.ptr.as_mut()), 92 | ffi::STD_string_view_result_error, 93 | ffi::STD_string_view_result_value_unsafe 94 | )?; 95 | Ok(string_view_to_str(sv)) 96 | } 97 | 98 | pub fn get_string<'a>(&mut self) -> Result<&'a str> { 99 | let sv = map_result!( 100 | ffi::SJ_OD_document_get_string(self.ptr.as_mut()), 101 | ffi::STD_string_view_result_error, 102 | ffi::STD_string_view_result_value_unsafe 103 | )?; 104 | Ok(string_view_to_str(sv)) 105 | } 106 | 107 | pub fn at_pointer<'a>(&mut self, json_pointer: &str) -> Result> { 108 | map_result!( 109 | ffi::SJ_OD_document_at_pointer( 110 | self.ptr.as_mut(), 111 | json_pointer.as_ptr().cast(), 112 | json_pointer.len() 113 | ), 114 | ffi::SJ_OD_value_result_error, 115 | ffi::SJ_OD_value_result_value_unsafe 116 | ) 117 | .map(Value::new) 118 | } 119 | 120 | pub fn get_number<'a>(&mut self) -> Result> { 121 | map_result!( 122 | ffi::SJ_OD_document_get_number(self.ptr.as_mut()), 123 | ffi::SJ_OD_number_result_error, 124 | ffi::SJ_OD_number_result_value_unsafe 125 | ) 126 | .map(Number::new) 127 | } 128 | 129 | pub fn is_null(&mut self) -> Result { 130 | map_result!( 131 | primitive, 132 | ffi::SJ_OD_document_is_null(self.ptr.as_mut()), 133 | ffi::bool_result_error, 134 | ffi::bool_result_value_unsafe 135 | ) 136 | } 137 | 138 | pub fn json_type(&mut self) -> Result { 139 | let json_type = map_result!( 140 | primitive, 141 | ffi::SJ_OD_document_type(self.ptr.as_mut()), 142 | ffi::int_result_error, 143 | ffi::int_result_value_unsafe 144 | )?; 145 | Ok(JsonType::from(json_type)) 146 | } 147 | } 148 | 149 | impl_drop!(Document<'p, 's>, ffi::SJ_OD_document_free); 150 | 151 | #[cfg(test)] 152 | mod tests { 153 | use crate::{ondemand, prelude::*}; 154 | 155 | #[test] 156 | fn get_bool() { 157 | let mut parser = ondemand::Parser::default(); 158 | 159 | { 160 | let json = "true".to_padded_string(); 161 | let mut doc = parser.iterate(&json).unwrap(); 162 | assert!(doc.get_bool().unwrap()); 163 | } 164 | { 165 | let json = "false".to_padded_string(); 166 | let mut doc = parser.iterate(&json).unwrap(); 167 | assert!(!doc.get_bool().unwrap()); 168 | } 169 | { 170 | let json = "1".to_padded_string(); 171 | let mut doc = parser.iterate(&json).unwrap(); 172 | assert!(doc.get_bool().is_err()); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018-2019 The simdjson authors 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /src/serde/de.rs: -------------------------------------------------------------------------------- 1 | use crate::dom::array::ArrayIter; 2 | use crate::dom::element::{Element, ElementType}; 3 | use crate::dom::object::ObjectIter; 4 | 5 | use crate::error::SimdJsonError; 6 | use crate::libsimdjson::ffi; 7 | use serde::de::{ 8 | Deserialize, DeserializeSeed, Deserializer, IntoDeserializer, MapAccess, SeqAccess, Visitor, 9 | }; 10 | 11 | // pub struct ElementVisitor; 12 | 13 | // impl<'de> Visitor for ElementVisitor { 14 | 15 | // } 16 | 17 | pub fn from_element<'a, T>(element: &'a Element<'a>) -> Result 18 | where 19 | T: Deserialize<'a>, 20 | { 21 | // let mut parser = Parser::default(); 22 | // let mut doc = parser.parse_str(s)?; 23 | let t = T::deserialize(element)?; 24 | Ok(t) 25 | } 26 | 27 | impl<'de, 'a> Deserializer<'de> for &'a Element<'a> { 28 | type Error = SimdJsonError; 29 | 30 | fn deserialize_any(self, visitor: V) -> Result 31 | where 32 | V: Visitor<'de>, 33 | { 34 | match self.get_type() { 35 | ElementType::NullValue => self.deserialize_unit(visitor), 36 | ElementType::Bool => self.deserialize_bool(visitor), 37 | ElementType::String => self.deserialize_str(visitor), 38 | ElementType::Uint64 => self.deserialize_u64(visitor), 39 | ElementType::Int64 => self.deserialize_i64(visitor), 40 | ElementType::Array => self.deserialize_seq(visitor), 41 | ElementType::Object => self.deserialize_map(visitor), 42 | ElementType::Double => self.deserialize_f64(visitor), 43 | } 44 | } 45 | 46 | fn deserialize_bool(self, visitor: V) -> Result 47 | where 48 | V: Visitor<'de>, 49 | { 50 | visitor.visit_bool(self.get_bool()?) 51 | } 52 | 53 | fn deserialize_unit(self, visitor: V) -> Result 54 | where 55 | V: Visitor<'de>, 56 | { 57 | visitor.visit_unit() 58 | } 59 | 60 | fn deserialize_i8(self, visitor: V) -> Result 61 | where 62 | V: Visitor<'de>, 63 | { 64 | visitor.visit_i8(self.get_i64()? as i8) 65 | } 66 | 67 | fn deserialize_i16(self, visitor: V) -> Result 68 | where 69 | V: Visitor<'de>, 70 | { 71 | visitor.visit_i16(self.get_i64()? as i16) 72 | } 73 | 74 | fn deserialize_i32(self, visitor: V) -> Result 75 | where 76 | V: Visitor<'de>, 77 | { 78 | visitor.visit_i32(self.get_i64()? as i32) 79 | } 80 | 81 | fn deserialize_i64(self, visitor: V) -> Result 82 | where 83 | V: Visitor<'de>, 84 | { 85 | visitor.visit_i64(self.get_i64()?) 86 | } 87 | 88 | fn deserialize_u8(self, visitor: V) -> Result 89 | where 90 | V: Visitor<'de>, 91 | { 92 | visitor.visit_u8(self.get_u64()? as u8) 93 | } 94 | 95 | fn deserialize_u16(self, visitor: V) -> Result 96 | where 97 | V: Visitor<'de>, 98 | { 99 | visitor.visit_u16(self.get_u64()? as u16) 100 | } 101 | 102 | fn deserialize_u32(self, visitor: V) -> Result 103 | where 104 | V: Visitor<'de>, 105 | { 106 | visitor.visit_u32(self.get_u64()? as u32) 107 | } 108 | 109 | fn deserialize_u64(self, visitor: V) -> Result 110 | where 111 | V: Visitor<'de>, 112 | { 113 | visitor.visit_u64(self.get_u64()?) 114 | } 115 | 116 | // Float parsing is stupidly hard. 117 | fn deserialize_f32(self, visitor: V) -> Result 118 | where 119 | V: Visitor<'de>, 120 | { 121 | visitor.visit_f32(self.get_f64()? as f32) 122 | } 123 | 124 | // Float parsing is stupidly hard. 125 | fn deserialize_f64(self, visitor: V) -> Result 126 | where 127 | V: Visitor<'de>, 128 | { 129 | visitor.visit_f64(self.get_f64()?) 130 | } 131 | 132 | // The `Serializer` implementation on the previous page serialized chars as 133 | // single-character strings so handle that representation here. 134 | fn deserialize_char(self, _visitor: V) -> Result 135 | where 136 | V: Visitor<'de>, 137 | { 138 | // Parse a string, check that it is one character, call `visit_char`. 139 | unimplemented!() 140 | } 141 | 142 | // Refer to the "Understanding deserializer lifetimes" page for information 143 | // about the three deserialization flavors of strings in Serde. 144 | fn deserialize_str(self, _visitor: V) -> Result 145 | where 146 | V: Visitor<'de>, 147 | { 148 | unimplemented!() 149 | } 150 | 151 | fn deserialize_string(self, visitor: V) -> Result 152 | where 153 | V: Visitor<'de>, 154 | { 155 | visitor.visit_string(self.get_string()?) 156 | } 157 | 158 | // The `Serializer` implementation on the previous page serialized byte 159 | // arrays as JSON arrays of bytes. Handle that representation here. 160 | fn deserialize_bytes(self, _visitor: V) -> Result 161 | where 162 | V: Visitor<'de>, 163 | { 164 | unimplemented!() 165 | } 166 | 167 | fn deserialize_byte_buf(self, _visitor: V) -> Result 168 | where 169 | V: Visitor<'de>, 170 | { 171 | unimplemented!() 172 | } 173 | 174 | // An absent optional is represented as the JSON `null` and a present 175 | // optional is represented as just the contained value. 176 | // 177 | // As commented in `Serializer` implementation, this is a lossy 178 | // representation. For example the values `Some(())` and `None` both 179 | // serialize as just `null`. Unfortunately this is typically what people 180 | // expect when working with JSON. Other formats are encouraged to behave 181 | // more intelligently if possible. 182 | fn deserialize_option(self, visitor: V) -> Result 183 | where 184 | V: Visitor<'de>, 185 | { 186 | if self.is_null() { 187 | visitor.visit_none() 188 | } else { 189 | visitor.visit_some(self) 190 | } 191 | } 192 | 193 | // Unit struct means a named value containing no data. 194 | fn deserialize_unit_struct( 195 | self, 196 | _name: &'static str, 197 | visitor: V, 198 | ) -> Result 199 | where 200 | V: Visitor<'de>, 201 | { 202 | self.deserialize_unit(visitor) 203 | } 204 | 205 | // As is done here, serializers are encouraged to treat newtype structs as 206 | // insignificant wrappers around the data they contain. That means not 207 | // parsing anything other than the contained value. 208 | fn deserialize_newtype_struct( 209 | self, 210 | _name: &'static str, 211 | visitor: V, 212 | ) -> Result 213 | where 214 | V: Visitor<'de>, 215 | { 216 | visitor.visit_newtype_struct(self) 217 | } 218 | 219 | // Deserialization of compound types like sequences and maps happens by 220 | // passing the visitor an "Access" object that gives it the ability to 221 | // iterate through the data contained in the sequence. 222 | fn deserialize_seq(self, visitor: V) -> Result 223 | where 224 | V: Visitor<'de>, 225 | { 226 | // Parse the opening bracket of the sequence. 227 | // de::Deserializer::deserialize_seq(self.into_iter(), visitor) 228 | // unimplemented!() 229 | let value = visitor.visit_seq(ArrayIter::new(&self.get_array()?))?; 230 | Ok(value) 231 | } 232 | 233 | // Tuples look just like sequences in JSON. Some formats may be able to 234 | // represent tuples more efficiently. 235 | // 236 | // As indicated by the length parameter, the `Deserialize` implementation 237 | // for a tuple in the Serde data model is required to know the length of the 238 | // tuple before even looking at the input data. 239 | fn deserialize_tuple(self, _len: usize, visitor: V) -> Result 240 | where 241 | V: Visitor<'de>, 242 | { 243 | self.deserialize_seq(visitor) 244 | } 245 | 246 | // Tuple structs look just like sequences in JSON. 247 | fn deserialize_tuple_struct( 248 | self, 249 | _name: &'static str, 250 | _len: usize, 251 | visitor: V, 252 | ) -> Result 253 | where 254 | V: Visitor<'de>, 255 | { 256 | self.deserialize_seq(visitor) 257 | } 258 | 259 | // Much like `deserialize_seq` but calls the visitors `visit_map` method 260 | // with a `MapAccess` implementation, rather than the visitor's `visit_seq` 261 | // method with a `SeqAccess` implementation. 262 | fn deserialize_map(self, visitor: V) -> Result 263 | where 264 | V: Visitor<'de>, 265 | { 266 | visitor.visit_map(ObjectIter::new(&self.get_object()?)) 267 | } 268 | 269 | // Structs look just like maps in JSON. 270 | // 271 | // Notice the `fields` parameter - a "struct" in the Serde data model means 272 | // that the `Deserialize` implementation is required to know what the fields 273 | // are before even looking at the input data. Any key-value pairing in which 274 | // the fields cannot be known ahead of time is probably a map. 275 | fn deserialize_struct( 276 | self, 277 | _name: &'static str, 278 | _fields: &'static [&'static str], 279 | visitor: V, 280 | ) -> Result 281 | where 282 | V: Visitor<'de>, 283 | { 284 | self.deserialize_map(visitor) 285 | } 286 | 287 | fn deserialize_enum( 288 | self, 289 | _name: &'static str, 290 | _variants: &'static [&'static str], 291 | _visitor: V, 292 | ) -> Result 293 | where 294 | V: Visitor<'de>, 295 | { 296 | unimplemented!() 297 | } 298 | 299 | // An identifier in Serde is the type that identifies a field of a struct or 300 | // the variant of an enum. In JSON, struct fields and enum variants are 301 | // represented as strings. In other formats they may be represented as 302 | // numeric indices. 303 | fn deserialize_identifier(self, visitor: V) -> Result 304 | where 305 | V: Visitor<'de>, 306 | { 307 | self.deserialize_str(visitor) 308 | } 309 | 310 | // Like `deserialize_any` but indicates to the `Deserializer` that it makes 311 | // no difference which `Visitor` method is called because the data is 312 | // ignored. 313 | // 314 | // Some deserializers are able to implement this more efficiently than 315 | // `deserialize_any`, for example by rapidly skipping over matched 316 | // delimiters without paying close attention to the data in between. 317 | // 318 | // Some formats are not able to implement this at all. Formats that can 319 | // implement `deserialize_any` and `deserialize_ignored_any` are known as 320 | // self-describing. 321 | fn deserialize_ignored_any(self, visitor: V) -> Result 322 | where 323 | V: Visitor<'de>, 324 | { 325 | self.deserialize_any(visitor) 326 | } 327 | } 328 | 329 | impl<'de, 'a> SeqAccess<'de> for ArrayIter<'a> { 330 | type Error = SimdJsonError; 331 | 332 | fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> 333 | where 334 | T: DeserializeSeed<'de>, 335 | { 336 | if let Some(element) = self.next() { 337 | seed.deserialize(&element).map(Some) 338 | } else { 339 | Ok(None) 340 | } 341 | } 342 | } 343 | 344 | impl<'de, 'a> MapAccess<'de> for ObjectIter<'a> { 345 | type Error = SimdJsonError; 346 | 347 | fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> 348 | where 349 | K: DeserializeSeed<'de>, 350 | { 351 | if self.has_next() { 352 | seed.deserialize(self.key().into_deserializer()).map(Some) 353 | } else { 354 | Ok(None) 355 | } 356 | } 357 | 358 | fn next_value_seed(&mut self, seed: V) -> Result 359 | where 360 | V: DeserializeSeed<'de>, 361 | { 362 | let result = seed.deserialize(&self.value()); 363 | ffi::object_iterator_next(self.ptr.pin_mut()); 364 | result 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /simdjson-sys/src/simdjson_c_api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define DEFINE_CLASS(name) \ 8 | typedef struct name name; \ 9 | void name##_free(name *r); 10 | 11 | // `value` method will take the ownership of T. 12 | #define DEFINE_RESULT(name) \ 13 | DEFINE_CLASS(name##_result) \ 14 | int name##_result_error(const name##_result *r); \ 15 | name *name##_result_value_unsafe(name##_result *r); 16 | 17 | // `value` method will free simdjson_result. 18 | #define DEFINE_PRIMITIVE_RESULT(name) \ 19 | DEFINE_CLASS(name##_result) \ 20 | int name##_result_error(const name##_result *r); \ 21 | name name##_result_value_unsafe(name##_result *r); 22 | 23 | #define DEFINE_GET(self, value, method) \ 24 | value##_result *self##_##method(self *r); 25 | 26 | #define DEFINE_AT_POINTER(self) \ 27 | SJ_OD_value_result *self##_at_pointer(self *r, const char *s, size_t len); 28 | 29 | #define DEFINE_GET_PRIMITIVE(self, value, method) \ 30 | value self##_##method(self *r); 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | // SJ for simdjson, OD for ondemand 37 | DEFINE_CLASS(SJ_padded_string) 38 | DEFINE_RESULT(SJ_padded_string) 39 | DEFINE_CLASS(SJ_OD_parser) 40 | DEFINE_CLASS(SJ_OD_document) 41 | DEFINE_RESULT(SJ_OD_document) 42 | DEFINE_CLASS(SJ_OD_value) 43 | DEFINE_RESULT(SJ_OD_value) 44 | DEFINE_CLASS(SJ_OD_array) 45 | DEFINE_RESULT(SJ_OD_array) 46 | DEFINE_CLASS(SJ_OD_object) 47 | DEFINE_RESULT(SJ_OD_object) 48 | DEFINE_CLASS(SJ_OD_raw_json_string) 49 | DEFINE_RESULT(SJ_OD_raw_json_string) 50 | DEFINE_CLASS(STD_string_view) 51 | DEFINE_RESULT(STD_string_view) 52 | DEFINE_CLASS(SJ_OD_array_iterator) 53 | DEFINE_RESULT(SJ_OD_array_iterator) 54 | DEFINE_CLASS(SJ_OD_object_iterator) 55 | DEFINE_RESULT(SJ_OD_object_iterator) 56 | DEFINE_CLASS(SJ_OD_field) 57 | DEFINE_RESULT(SJ_OD_field) 58 | DEFINE_CLASS(SJ_OD_number) 59 | DEFINE_RESULT(SJ_OD_number) 60 | 61 | DEFINE_PRIMITIVE_RESULT(uint64_t) 62 | DEFINE_PRIMITIVE_RESULT(int64_t) 63 | DEFINE_PRIMITIVE_RESULT(double) 64 | DEFINE_PRIMITIVE_RESULT(bool) 65 | DEFINE_PRIMITIVE_RESULT(size_t) 66 | DEFINE_PRIMITIVE_RESULT(int) 67 | 68 | // padded_string 69 | // SJ_padded_string *SJ_padded_string_new(const char *s, size_t len); 70 | // SJ_padded_string_result * 71 | // SJ_padded_string_load(const char *path); // null terminated string. 72 | // size_t SJ_padded_string_length(const SJ_padded_string *ps); 73 | // const uint8_t *SJ_padded_string_u8data(const SJ_padded_string *ps); 74 | 75 | // ondemand::parser 76 | SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity); 77 | SJ_OD_document_result * 78 | SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, 79 | const SJ_padded_string *s); 80 | SJ_OD_document_result * 81 | SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, 82 | size_t len, size_t allocated); 83 | 84 | // ondemand::value 85 | DEFINE_GET(SJ_OD_value, uint64_t, get_uint64) 86 | DEFINE_GET(SJ_OD_value, int64_t, get_int64) 87 | DEFINE_GET(SJ_OD_value, double, get_double) 88 | DEFINE_GET(SJ_OD_value, bool, get_bool) 89 | DEFINE_GET(SJ_OD_value, SJ_OD_array, get_array) 90 | DEFINE_GET(SJ_OD_value, SJ_OD_object, get_object) 91 | DEFINE_GET(SJ_OD_value, SJ_OD_raw_json_string, get_raw_json_string) 92 | DEFINE_GET(SJ_OD_value, STD_string_view, get_wobbly_string) 93 | DEFINE_GET(SJ_OD_value, bool, is_null) 94 | DEFINE_GET(SJ_OD_value, int, type) 95 | DEFINE_GET(SJ_OD_value, SJ_OD_number, get_number) 96 | DEFINE_AT_POINTER(SJ_OD_value) 97 | 98 | // ondemand::document 99 | SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc); 100 | DEFINE_GET(SJ_OD_document, uint64_t, get_uint64) 101 | DEFINE_GET(SJ_OD_document, int64_t, get_int64) 102 | DEFINE_GET(SJ_OD_document, double, get_double) 103 | DEFINE_GET(SJ_OD_document, bool, get_bool) 104 | DEFINE_GET(SJ_OD_document, SJ_OD_array, get_array) 105 | DEFINE_GET(SJ_OD_document, SJ_OD_object, get_object) 106 | DEFINE_GET(SJ_OD_document, SJ_OD_raw_json_string, get_raw_json_string) 107 | DEFINE_GET(SJ_OD_document, STD_string_view, get_wobbly_string) 108 | DEFINE_GET(SJ_OD_document, bool, is_null) 109 | DEFINE_GET(SJ_OD_document, int, type) 110 | DEFINE_GET(SJ_OD_document, SJ_OD_number, get_number) 111 | DEFINE_AT_POINTER(SJ_OD_document) 112 | 113 | // get_string is special. 114 | STD_string_view_result *SJ_OD_value_get_string(SJ_OD_value *value, 115 | bool allow_replacement); 116 | STD_string_view_result *SJ_OD_document_get_string(SJ_OD_document *doc); 117 | 118 | // std::string_view 119 | const char *STD_string_view_data(STD_string_view *sv); 120 | size_t STD_string_view_size(STD_string_view *sv); 121 | 122 | // ondemand::array 123 | DEFINE_GET(SJ_OD_array, size_t, count_elements) 124 | DEFINE_GET(SJ_OD_array, bool, is_empty) 125 | DEFINE_GET(SJ_OD_array, bool, reset) 126 | DEFINE_GET(SJ_OD_array, SJ_OD_array_iterator, begin) 127 | DEFINE_GET(SJ_OD_array, SJ_OD_array_iterator, end) 128 | DEFINE_GET(SJ_OD_array, STD_string_view, raw_json) 129 | DEFINE_AT_POINTER(SJ_OD_array) 130 | 131 | SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index); 132 | 133 | // ondemand::array_iterator 134 | DEFINE_GET(SJ_OD_array_iterator, SJ_OD_value, get) 135 | bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator *lhs, 136 | const SJ_OD_array_iterator *rhs); 137 | void SJ_OD_array_iterator_step(SJ_OD_array_iterator *self); 138 | 139 | // ondemand::object 140 | DEFINE_GET(SJ_OD_object, SJ_OD_object_iterator, begin) 141 | DEFINE_GET(SJ_OD_object, SJ_OD_object_iterator, end) 142 | DEFINE_GET(SJ_OD_object, STD_string_view, raw_json) 143 | DEFINE_GET(SJ_OD_object, bool, is_empty) 144 | DEFINE_GET(SJ_OD_object, bool, reset) 145 | DEFINE_GET(SJ_OD_object, size_t, count_fields) 146 | DEFINE_AT_POINTER(SJ_OD_object) 147 | 148 | SJ_OD_value_result *SJ_OD_object_find_field(SJ_OD_object *object, 149 | const char *data, size_t len); 150 | SJ_OD_value_result *SJ_OD_object_find_field_unordered(SJ_OD_object *object, 151 | const char *data, 152 | size_t len); 153 | 154 | // ondemand::object_iterator 155 | DEFINE_GET(SJ_OD_object_iterator, SJ_OD_field, get) 156 | bool SJ_OD_object_iterator_not_equal(const SJ_OD_object_iterator *lhs, 157 | const SJ_OD_object_iterator *rhs); 158 | void SJ_OD_object_iterator_step(SJ_OD_object_iterator *self); 159 | 160 | // ondemand::field 161 | STD_string_view_result *SJ_OD_field_unescaped_key(SJ_OD_field *self, 162 | bool allow_replacement); 163 | SJ_OD_value *SJ_OD_field_value(SJ_OD_field *self); 164 | SJ_OD_value *SJ_OD_field_take_value(SJ_OD_field *self); 165 | 166 | // ondemand::number 167 | DEFINE_GET_PRIMITIVE(SJ_OD_number, int, get_number_type) 168 | DEFINE_GET_PRIMITIVE(SJ_OD_number, uint64_t, get_uint64) 169 | DEFINE_GET_PRIMITIVE(SJ_OD_number, int64_t, get_int64) 170 | DEFINE_GET_PRIMITIVE(SJ_OD_number, double, get_double) 171 | 172 | // New macros for dom 173 | 174 | #define DEFINE_HANDLE(name) \ 175 | typedef struct name name; \ 176 | void name##_free(name *r); 177 | 178 | #define DEFINE_HANDLE_RESULT(name) \ 179 | typedef struct name##_result { \ 180 | int error; \ 181 | name *value; \ 182 | } name##_result; 183 | 184 | // Add prefix SJ_ so we can use allowlist. 185 | #define DEFINE_PRIMITIVE_RESULT_V2(name) \ 186 | typedef struct SJ_##name##_result { \ 187 | int error; \ 188 | name value; \ 189 | } SJ_##name##_result; 190 | 191 | #define DEFINE_GET_V2(self, value, method) value self##_##method(self *r); 192 | 193 | DEFINE_HANDLE(SJ_DOM_parser) 194 | 195 | DEFINE_HANDLE(SJ_DOM_element) 196 | DEFINE_HANDLE_RESULT(SJ_DOM_element) 197 | DEFINE_HANDLE(SJ_DOM_array) 198 | DEFINE_HANDLE_RESULT(SJ_DOM_array) 199 | DEFINE_HANDLE(SJ_DOM_object) 200 | DEFINE_HANDLE_RESULT(SJ_DOM_object) 201 | DEFINE_HANDLE(SJ_DOM_array_iterator) 202 | DEFINE_HANDLE(SJ_DOM_object_iterator) 203 | DEFINE_HANDLE(SJ_DOM_document) 204 | DEFINE_HANDLE_RESULT(SJ_DOM_document) 205 | DEFINE_HANDLE(SJ_DOM_document_stream) 206 | DEFINE_HANDLE_RESULT(SJ_DOM_document_stream) 207 | DEFINE_HANDLE(SJ_DOM_document_stream_iterator) 208 | 209 | DEFINE_PRIMITIVE_RESULT_V2(uint64_t) 210 | DEFINE_PRIMITIVE_RESULT_V2(int64_t) 211 | DEFINE_PRIMITIVE_RESULT_V2(double) 212 | DEFINE_PRIMITIVE_RESULT_V2(bool) 213 | DEFINE_PRIMITIVE_RESULT_V2(size_t) 214 | DEFINE_PRIMITIVE_RESULT_V2(int) 215 | 216 | typedef struct SJ_string_view { 217 | const char *data; 218 | size_t len; 219 | } SJ_string_view; 220 | 221 | typedef struct SJ_string_view_result { 222 | int error; 223 | SJ_string_view value; 224 | } SJ_string_view_result; 225 | 226 | typedef struct SJ_DOM_key_value_pair { 227 | SJ_string_view key; 228 | SJ_DOM_element *value; 229 | } SJ_DOM_key_value_pair; 230 | 231 | // dom::parser 232 | SJ_DOM_parser *SJ_DOM_parser_new(size_t max_capacity); 233 | SJ_DOM_element_result SJ_DOM_parser_parse(SJ_DOM_parser *parser, 234 | const char *json, size_t len); 235 | SJ_DOM_element_result SJ_DOM_parser_parse_into_document(SJ_DOM_parser *parser, 236 | SJ_DOM_document *doc, 237 | const char *json, 238 | size_t len); 239 | SJ_DOM_document_stream_result SJ_DOM_parser_parse_many(SJ_DOM_parser *parser, 240 | const char *json, 241 | size_t len, 242 | size_t batch_size); 243 | 244 | // dom::element 245 | DEFINE_GET_V2(SJ_DOM_element, int, type) 246 | DEFINE_GET_V2(SJ_DOM_element, SJ_DOM_array_result, get_array) 247 | DEFINE_GET_V2(SJ_DOM_element, SJ_DOM_object_result, get_object) 248 | DEFINE_GET_V2(SJ_DOM_element, SJ_string_view_result, get_string) 249 | DEFINE_GET_V2(SJ_DOM_element, SJ_int64_t_result, get_int64) 250 | DEFINE_GET_V2(SJ_DOM_element, SJ_uint64_t_result, get_uint64) 251 | DEFINE_GET_V2(SJ_DOM_element, SJ_double_result, get_double) 252 | DEFINE_GET_V2(SJ_DOM_element, SJ_bool_result, get_bool) 253 | SJ_DOM_element_result SJ_DOM_element_at_pointer(SJ_DOM_element *element, 254 | const char *s, size_t len); 255 | 256 | // dom::array 257 | DEFINE_GET_V2(SJ_DOM_array, SJ_DOM_array_iterator *, begin) 258 | DEFINE_GET_V2(SJ_DOM_array, SJ_DOM_array_iterator *, end) 259 | DEFINE_GET_V2(SJ_DOM_array, size_t, size) 260 | DEFINE_GET_V2(SJ_DOM_array, size_t, number_of_slots) 261 | 262 | SJ_DOM_element_result SJ_DOM_array_at(SJ_DOM_array *array, size_t index); 263 | SJ_DOM_element_result SJ_DOM_array_at_pointer(SJ_DOM_array *array, 264 | const char *s, size_t len); 265 | 266 | // dom::object 267 | DEFINE_GET_V2(SJ_DOM_object, SJ_DOM_object_iterator *, begin) 268 | DEFINE_GET_V2(SJ_DOM_object, SJ_DOM_object_iterator *, end) 269 | DEFINE_GET_V2(SJ_DOM_object, size_t, size) 270 | 271 | SJ_DOM_element_result SJ_DOM_object_at_pointer(SJ_DOM_object *object, 272 | const char *s, size_t len); 273 | SJ_DOM_element_result SJ_DOM_object_at_key(SJ_DOM_object *object, const char *s, 274 | size_t len); 275 | SJ_DOM_element_result 276 | SJ_DOM_object_at_key_case_insensitive(SJ_DOM_object *object, const char *s, 277 | size_t len); 278 | 279 | // dom::iterator 280 | DEFINE_GET_V2(SJ_DOM_array_iterator, SJ_DOM_element *, get) 281 | DEFINE_GET_V2(SJ_DOM_array_iterator, void, step) 282 | bool SJ_DOM_array_iterator_not_equal(SJ_DOM_array_iterator *lhs, 283 | SJ_DOM_array_iterator *rhs); 284 | 285 | DEFINE_GET_V2(SJ_DOM_object_iterator, SJ_DOM_key_value_pair, get) 286 | DEFINE_GET_V2(SJ_DOM_object_iterator, void, step) 287 | bool SJ_DOM_object_iterator_not_equal(SJ_DOM_object_iterator *lhs, 288 | SJ_DOM_object_iterator *rhs); 289 | 290 | // dom::document 291 | SJ_DOM_document *SJ_DOM_document_new(); 292 | DEFINE_GET_V2(SJ_DOM_document, SJ_DOM_element *, root) 293 | DEFINE_GET_V2(SJ_DOM_document_stream, SJ_DOM_document_stream_iterator *, begin) 294 | DEFINE_GET_V2(SJ_DOM_document_stream, SJ_DOM_document_stream_iterator *, end) 295 | DEFINE_GET_V2(SJ_DOM_document_stream_iterator, SJ_DOM_element_result, get) 296 | DEFINE_GET_V2(SJ_DOM_document_stream_iterator, void, step) 297 | bool SJ_DOM_document_stream_iterator_not_equal( 298 | SJ_DOM_document_stream_iterator *lhs, SJ_DOM_document_stream_iterator *rhs); 299 | 300 | #ifdef __cplusplus 301 | } 302 | #endif 303 | -------------------------------------------------------------------------------- /examples/issue_20.rs: -------------------------------------------------------------------------------- 1 | use simdjson_rust::{ondemand, prelude::*, Result}; 2 | 3 | fn main() -> Result<()> { 4 | let data = r#"{"ingredients": [{"rose_wine": {"black_sesame_seed": 3}}, {"myrtleberry": {"black_sesame_seed": 5}}, {"grilled_beef": {"black_sesame_seed": 1}}, {"parmesan_cheese": {"black_sesame_seed": 2}}, {"strawberry_jam": {"black_sesame_seed": 1}}, {"tuna": {"black_sesame_seed": 3}}, {"black_tea": {"black_sesame_seed": 8}}, {"japanese_mint": {"black_sesame_seed": 5}}, {"pork": {"black_sesame_seed": 1}}, {"french_lavender": {"black_sesame_seed": 2}}, {"nutmeg": {"black_sesame_seed": 12}}, {"carob_fruit": {"black_sesame_seed": 2}}, {"mastic_gum_leaf_oil": {"black_sesame_seed": 2}}, {"california_pepper": {"black_sesame_seed": 5}}, {"orange_peel_oil": {"black_sesame_seed": 2}}, {"eucalyptus_oil": {"black_sesame_seed": 4}}, {"monarda_punctata": {"black_sesame_seed": 1}}, {"dried_green_tea": {"black_sesame_seed": 7}}, {"sake": {"black_sesame_seed": 2}}, {"currant": {"black_sesame_seed": 2}}, {"sweet_grass_oil": {"black_sesame_seed": 2}}, {"safflower_seed": {"black_sesame_seed": 17}}, {"sparkling_wine": {"black_sesame_seed": 3}}, {"enokidake": {"black_sesame_seed": 1}}, {"raw_peanut": {"black_sesame_seed": 1}}, {"cucumber": {"black_sesame_seed": 1}}, {"russian_cheese": {"black_sesame_seed": 1}}, {"catfish": {"black_sesame_seed": 3}}, {"origanum": {"black_sesame_seed": 2}}, {"roasted_beef": {"black_sesame_seed": 1}}, {"limburger_cheese": {"black_sesame_seed": 1}}, {"cantaloupe": {"black_sesame_seed": 3}}, {"corn_mint_oil": {"black_sesame_seed": 5}}, {"marjoram": {"black_sesame_seed": 4}}, {"herring": {"black_sesame_seed": 3}}, {"kumquat_peel_oil": {"black_sesame_seed": 1}}, {"cheese": {"black_sesame_seed": 1}}, {"lavender": {"black_sesame_seed": 1}}, {"cayenne": {"black_sesame_seed": 5}}, {"red_kidney_bean": {"black_sesame_seed": 4}}, {"mangosteen": {"black_sesame_seed": 2}}, {"lovage_root": {"black_sesame_seed": 1}}, {"mastic_gum": {"black_sesame_seed": 2}}, {"liver": {"black_sesame_seed": 1}}, {"thyme": {"black_sesame_seed": 3}}, {"oregano": {"black_sesame_seed": 1}}, {"lamb_liver": {"black_sesame_seed": 1}}, {"fruit": {"black_sesame_seed": 2}}, {"crab": {"black_sesame_seed": 1}}, {"french_peppermint": {"black_sesame_seed": 7}}, {"jamaican_rum": {"black_sesame_seed": 4}}, {"orange": {"black_sesame_seed": 7}}, {"romano_cheese": {"black_sesame_seed": 2}}, {"keta_salmon": {"black_sesame_seed": 3}}, {"juniper_berry": {"black_sesame_seed": 5}}, {"cheddar_cheese": {"black_sesame_seed": 1}}, {"calytrix_tetragona_oil": {"black_sesame_seed": 1}}, {"sea_bass": {"black_sesame_seed": 3}}, {"raw_pork": {"black_sesame_seed": 1}}, {"pear": {"black_sesame_seed": 2}}, {"pike": {"black_sesame_seed": 3}}, {"peanut": {"black_sesame_seed": 1}}, {"kumquat": {"black_sesame_seed": 2}}, {"european_cranberry": {"black_sesame_seed": 7}}, {"lemongrass": {"black_sesame_seed": 1}}, {"gin": {"black_sesame_seed": 1}}, {"orange_juice": {"black_sesame_seed": 1}}, {"dried_fig": {"black_sesame_seed": 2}}, {"sassafras": {"black_sesame_seed": 1}}, {"geranium": {"black_sesame_seed": 1}}, {"java_citronella": {"black_sesame_seed": 2}}, {"bourbon_whiskey": {"black_sesame_seed": 1}}, {"peanut_butter": {"black_sesame_seed": 1}}, {"smoked_fish": {"black_sesame_seed": 3}}, {"pennyroyal": {"black_sesame_seed": 1}}, {"roasted_shrimp": {"black_sesame_seed": 1}}, {"concord_grape": {"black_sesame_seed": 3}}, {"munster_cheese": {"black_sesame_seed": 1}}, {"sage": {"black_sesame_seed": 3}}, {"cocoa": {"black_sesame_seed": 3}}, {"champagne_wine": {"black_sesame_seed": 3}}, {"chicory_root": {"black_sesame_seed": 2}}, {"chamaecyparis_formosensis_oil": {"black_sesame_seed": 1}}, {"crowberry": {"black_sesame_seed": 5}}, {"water_apple": {"black_sesame_seed": 2}}, {"mint_oil": {"black_sesame_seed": 4}}, {"wheaten_bread": {"black_sesame_seed": 1}}, {"durian": {"black_sesame_seed": 2}}, {"cloudberry": {"black_sesame_seed": 7}}, {"japanese_star_anise": {"black_sesame_seed": 1}}, {"fried_cured_pork": {"black_sesame_seed": 1}}, {"satsuma": {"black_sesame_seed": 4}}, {"winter_savory": {"black_sesame_seed": 1}}, {"cowberry": {"black_sesame_seed": 5}}, {"snap_bean": {"black_sesame_seed": 4}}, {"cabernet_sauvignon_grape": {"black_sesame_seed": 3}}, {"port_wine": {"black_sesame_seed": 3}}, {"blackberry": {"black_sesame_seed": 7}}, {"tea_tree_oil": {"black_sesame_seed": 2}}, {"shiitake": {"black_sesame_seed": 1}}, {"cumin": {"black_sesame_seed": 6}}, {"prune": {"black_sesame_seed": 2}}, {"bog_blueberry": {"black_sesame_seed": 5}}, {"celery": {"black_sesame_seed": 4}}, {"boiled_pork": {"black_sesame_seed": 1}}, {"rooibus_tea": {"black_sesame_seed": 7}}, {"eucalyptus_globulus": {"black_sesame_seed": 1}}, {"tabasco_pepper": {"black_sesame_seed": 5}}, {"fermented_shrimp": {"black_sesame_seed": 1}}, {"jackfruit": {"black_sesame_seed": 2}}, {"gruyere_cheese": {"black_sesame_seed": 3}}, {"bread": {"black_sesame_seed": 1}}, {"pineapple": {"black_sesame_seed": 1}}, {"peppermint": {"black_sesame_seed": 7}}, {"cruciferae_seed": {"black_sesame_seed": 17}}, {"israeli_orange": {"black_sesame_seed": 7}}, {"monarda_punctata_oil": {"black_sesame_seed": 1}}, {"california_orange": {"black_sesame_seed": 7}}, {"peppermint_oil": {"black_sesame_seed": 5}}, {"ceylon_tea": {"black_sesame_seed": 7}}, {"cod": {"black_sesame_seed": 3}}, {"roasted_peanut": {"black_sesame_seed": 1}}, {"fish": {"black_sesame_seed": 3}}, {"plum": {"black_sesame_seed": 3}}, {"fennel": {"black_sesame_seed": 3}}, {"toasted_oat": {"black_sesame_seed": 1}}, {"cured_pork": {"black_sesame_seed": 4}}, {"pork_liver": {"black_sesame_seed": 1}}, {"mastic_gum_fruit_oil": {"black_sesame_seed": 1}}, {"passion_fruit": {"black_sesame_seed": 7}}, {"parsley": {"black_sesame_seed": 7}}, {"soybean": {"black_sesame_seed": 4}}, {"strawberry": {"black_sesame_seed": 6}}, {"scotch_spearmint_oil": {"black_sesame_seed": 4}}, {"pork_sausage": {"black_sesame_seed": 1}}, {"hernandia_peltata_oil": {"black_sesame_seed": 1}}, {"lemongrass_oil": {"black_sesame_seed": 1}}, {"roasted_filbert": {"black_sesame_seed": 3}}, {"wild_berry": {"black_sesame_seed": 5}}, {"wine": {"black_sesame_seed": 3}}, {"raw_lean_fish": {"black_sesame_seed": 3}}, {"roasted_lamb": {"black_sesame_seed": 2}}, {"pinto_bean": {"black_sesame_seed": 4}}, {"spearmint_oil": {"black_sesame_seed": 1}}, {"chicken": {"black_sesame_seed": 1}}, {"guarana": {"black_sesame_seed": 2}}, {"ocimum_viride": {"black_sesame_seed": 1}}, {"lantana_camara_oil": {"black_sesame_seed": 1}}, {"roman_chamomile": {"black_sesame_seed": 2}}, {"mate": {"black_sesame_seed": 1}}, {"roasted_mate": {"black_sesame_seed": 1}}, {"palmarosa": {"black_sesame_seed": 1}}, {"clary_sage": {"black_sesame_seed": 1}}, {"mint": {"black_sesame_seed": 5}}, {"myrtle": {"black_sesame_seed": 4}}, {"cabernet_sauvignon_wine": {"black_sesame_seed": 3}}, {"fermented_tea": {"black_sesame_seed": 7}}, {"brown_rice": {"black_sesame_seed": 1}}, {"mastic_gum_oil": {"black_sesame_seed": 2}}, {"vanilla": {"black_sesame_seed": 3}}, {"popcorn": {"black_sesame_seed": 2}}, {"eucalyptus_macarthurii": {"black_sesame_seed": 1}}, {"shrimp": {"black_sesame_seed": 1}}, {"malagueta_pepper": {"black_sesame_seed": 5}}, {"milk": {"black_sesame_seed": 4}}, {"muscadine_grape": {"black_sesame_seed": 3}}, {"mung_bean": {"black_sesame_seed": 4}}, {"kaffir_lime": {"black_sesame_seed": 5}}, {"huckleberry": {"black_sesame_seed": 5}}, {"tangerine_juice": {"black_sesame_seed": 1}}, {"muscat_grape": {"black_sesame_seed": 3}}, {"eucalyptus_bakeries_oil": {"black_sesame_seed": 1}}, {"smoked_pork_belly": {"black_sesame_seed": 1}}, {"camembert_cheese": {"black_sesame_seed": 1}}, {"haddock": {"black_sesame_seed": 3}}, {"cymbopogon_sennaarensis": {"black_sesame_seed": 1}}, {"whitefish": {"black_sesame_seed": 3}}, {"yellow_passion_fruit": {"black_sesame_seed": 1}}, {"calytrix_tetragona": {"black_sesame_seed": 1}}, {"roasted_malt": {"black_sesame_seed": 3}}, {"thymus": {"black_sesame_seed": 6}}, {"mandarin_peel": {"black_sesame_seed": 3}}, {"loganberry": {"black_sesame_seed": 5}}, {"tangerine_peel_oil": {"black_sesame_seed": 1}}, {"mango": {"black_sesame_seed": 6}}, {"roman_chamomile_oil": {"black_sesame_seed": 1}}, {"muskmelon": {"black_sesame_seed": 3}}, {"roasted_chicory_root": {"black_sesame_seed": 2}}, {"sherry": {"black_sesame_seed": 3}}, {"fatty_fish": {"black_sesame_seed": 3}}, {"lime_juice": {"black_sesame_seed": 1}}, {"dried_black_tea": {"black_sesame_seed": 7}}, {"malay_apple": {"black_sesame_seed": 2}}, {"navy_bean": {"black_sesame_seed": 4}}, {"smoked_pork": {"black_sesame_seed": 1}}, {"mutton_liver": {"black_sesame_seed": 1}}, {"seychelles_tea": {"black_sesame_seed": 7}}, {"lime": {"black_sesame_seed": 5}}, {"raw_fish": {"black_sesame_seed": 3}}, {"papaya": {"black_sesame_seed": 5}}, {"green_tea": {"black_sesame_seed": 7}}, {"citrus_peel_oil": {"black_sesame_seed": 7}}, {"seed": {"black_sesame_seed": 17}}, {"raw_fatty_fish": {"black_sesame_seed": 3}}, {"parsnip_fruit": {"black_sesame_seed": 2}}, {"parsnip": {"black_sesame_seed": 1}}, {"blenheim_apricot": {"black_sesame_seed": 2}}, {"buchu": {"black_sesame_seed": 4}}, {"blueberry": {"black_sesame_seed": 6}}, {"sauvignon_blanc_grape": {"black_sesame_seed": 3}}, {"kiwi": {"black_sesame_seed": 2}}, {"white_wine": {"black_sesame_seed": 4}}, {"long_pepper": {"black_sesame_seed": 5}}, {"fried_pork": {"black_sesame_seed": 1}}, {"kidney_bean": {"black_sesame_seed": 4}}, {"wild_raspberry": {"black_sesame_seed": 5}}, {"licorice": {"black_sesame_seed": 3}}, {"grapefruit": {"black_sesame_seed": 4}}, {"roasted_coconut": {"black_sesame_seed": 1}}, {"buchu_oil": {"black_sesame_seed": 4}}, {"guinea_pepper": {"black_sesame_seed": 5}}, {"burley_tobacco": {"black_sesame_seed": 1}}, {"monkey_orange": {"black_sesame_seed": 4}}, {"cooked_apple": {"black_sesame_seed": 2}}, {"roasted_cocoa": {"black_sesame_seed": 3}}, {"cream_cheese": {"black_sesame_seed": 1}}, {"smoked_fatty_fish": {"black_sesame_seed": 3}}, {"oatmeal": {"black_sesame_seed": 2}}, {"ocimum_gratissimum": {"black_sesame_seed": 1}}, {"coconut": {"black_sesame_seed": 1}}, {"roasted_pecan": {"black_sesame_seed": 3}}, {"horse_mackerel": {"black_sesame_seed": 3}}, {"peach": {"black_sesame_seed": 2}}, {"dwarf_quince": {"black_sesame_seed": 2}}, {"seed_oil": {"black_sesame_seed": 3}}, {"lingonberry": {"black_sesame_seed": 2}}, {"capsicum": {"black_sesame_seed": 5}}, {"leaf": {"black_sesame_seed": 6}}, {"jasmine_tea": {"black_sesame_seed": 7}}, {"elderberry": {"black_sesame_seed": 7}}, {"cape_gooseberry": {"black_sesame_seed": 6}}, {"roasted_spanish_peanut": {"black_sesame_seed": 1}}, {"lean_fish": {"black_sesame_seed": 3}}, {"comte_cheese": {"black_sesame_seed": 1}}, {"root": {"black_sesame_seed": 1}}, {"ginger": {"black_sesame_seed": 10}}, {"cherry": {"black_sesame_seed": 2}}, {"eucalyptus_dives": {"black_sesame_seed": 1}}, {"uncured_boiled_pork": {"black_sesame_seed": 1}}, {"raw_lamb": {"black_sesame_seed": 2}}, {"salmon": {"black_sesame_seed": 3}}, {"crownberry": {"black_sesame_seed": 5}}, {"rapeseed": {"black_sesame_seed": 17}}, {"dried_parsley": {"black_sesame_seed": 7}}, {"lemon_balm": {"black_sesame_seed": 3}}, {"roasted_barley": {"black_sesame_seed": 2}}, {"chicken_liver": {"black_sesame_seed": 1}}, {"tangerine": {"black_sesame_seed": 5}}, {"cilantro": {"black_sesame_seed": 1}}, {"fenugreek": {"black_sesame_seed": 1}}, {"swiss_cheese": {"black_sesame_seed": 1}}, {"raw_chicken": {"black_sesame_seed": 1}}, {"sheep_cheese": {"black_sesame_seed": 1}}, {"celery_seed": {"black_sesame_seed": 3}}, {"french_bean": {"black_sesame_seed": 4}}, {"whiskey": {"black_sesame_seed": 3}}, {"tuber": {"black_sesame_seed": 1}}, {"grape": {"black_sesame_seed": 3}}, {"coffee": {"black_sesame_seed": 3}}, {"filbert": {"black_sesame_seed": 2}}, {"peanut_oil": {"black_sesame_seed": 1}}, {"quince": {"black_sesame_seed": 2}}, {"spanish_sage": {"black_sesame_seed": 1}}, {"lemon_peel_oil": {"black_sesame_seed": 2}}, {"smoked_herring": {"black_sesame_seed": 3}}, {"coriander": {"black_sesame_seed": 6}}, {"rice": {"black_sesame_seed": 1}}, {"cinnamon": {"black_sesame_seed": 7}}, {"roasted_pork": {"black_sesame_seed": 1}}, {"chinese_quince": {"black_sesame_seed": 3}}, {"chive": {"black_sesame_seed": 3}}, {"grapefruit_juice": {"black_sesame_seed": 4}}, {"fried_chicken": {"black_sesame_seed": 2}}, {"emmental_cheese": {"black_sesame_seed": 1}}, {"melon": {"black_sesame_seed": 3}}, {"laurel": {"black_sesame_seed": 7}}, {"nectarine": {"black_sesame_seed": 4}}, {"wort": {"black_sesame_seed": 3}}, {"rum": {"black_sesame_seed": 4}}, {"caraway_seed": {"black_sesame_seed": 1}}, {"calamus": {"black_sesame_seed": 2}}, {"lemon_peel": {"black_sesame_seed": 2}}, {"watermelon": {"black_sesame_seed": 2}}, {"capsicum_annuum": {"black_sesame_seed": 5}}, {"hop": {"black_sesame_seed": 3}}, {"uncured_pork": {"black_sesame_seed": 1}}, {"provolone_cheese": {"black_sesame_seed": 1}}, {"boiled_beef": {"black_sesame_seed": 1}}, {"mountain_papaya": {"black_sesame_seed": 2}}, {"uncured_smoked_pork": {"black_sesame_seed": 1}}, {"spearmint": {"black_sesame_seed": 1}}, {"raw_beef": {"black_sesame_seed": 1}}, {"chinese_star_anise": {"black_sesame_seed": 1}}, {"boiled_crab": {"black_sesame_seed": 1}}, {"pawpaw": {"black_sesame_seed": 1}}, {"italian_lime": {"black_sesame_seed": 5}}, {"wheat_bread": {"black_sesame_seed": 1}}, {"calabash_nutmeg": {"black_sesame_seed": 3}}, {"yeast": {"black_sesame_seed": 1}}, {"choke_cherry": {"black_sesame_seed": 2}}, {"chokeberry": {"black_sesame_seed": 5}}, {"rice_husk": {"black_sesame_seed": 1}}, {"goat_cheese": {"black_sesame_seed": 1}}, {"finocchoi_fennel_oil": {"black_sesame_seed": 1}}, {"thai_pepper": {"black_sesame_seed": 5}}, {"sauvignon_grape": {"black_sesame_seed": 3}}, {"rose_apple": {"black_sesame_seed": 3}}, {"sour_cherry": {"black_sesame_seed": 2}}, {"crisp_bread": {"black_sesame_seed": 3}}, {"pepper": {"black_sesame_seed": 5}}, {"corn_mint": {"black_sesame_seed": 5}}, {"dried_kidney_bean": {"black_sesame_seed": 4}}, {"origanum_floribundum": {"black_sesame_seed": 1}}, {"hinoki_oil": {"black_sesame_seed": 1}}, {"prickly_pear": {"black_sesame_seed": 2}}, {"porcini": {"black_sesame_seed": 1}}, {"palm": {"black_sesame_seed": 1}}, {"cumin_fruit_oil": {"black_sesame_seed": 1}}, {"raspberry": {"black_sesame_seed": 7}}, {"pimento": {"black_sesame_seed": 2}}, {"fermented_russian_black_tea": {"black_sesame_seed": 7}}, {"ceylon_citronella": {"black_sesame_seed": 1}}, {"hinoki": {"black_sesame_seed": 1}}, {"eucalyptus": {"black_sesame_seed": 6}}, {"tarragon": {"black_sesame_seed": 1}}, {"mantis_shrimp": {"black_sesame_seed": 1}}, {"citrus_peel": {"black_sesame_seed": 7}}, {"grilled_pork": {"black_sesame_seed": 1}}, {"green_bell_pepper": {"black_sesame_seed": 5}}, {"peru_balsam": {"black_sesame_seed": 2}}, {"elderberry_fruit": {"black_sesame_seed": 2}}, {"cranberry": {"black_sesame_seed": 7}}, {"red_currant": {"black_sesame_seed": 5}}, {"orange_peel": {"black_sesame_seed": 2}}, {"raw_bean": {"black_sesame_seed": 4}}, {"corn": {"black_sesame_seed": 3}}, {"galanga": {"black_sesame_seed": 1}}, {"lima_bean": {"black_sesame_seed": 4}}, {"brewed_tea": {"black_sesame_seed": 7}}, {"feta_cheese": {"black_sesame_seed": 1}}, {"butter": {"black_sesame_seed": 3}}, {"oregano_oil": {"black_sesame_seed": 1}}, {"orthodon_citraliferum": {"black_sesame_seed": 1}}, {"satureia_thymera": {"black_sesame_seed": 1}}, {"sweetfish": {"black_sesame_seed": 3}}, {"prunus": {"black_sesame_seed": 2}}, {"turmeric": {"black_sesame_seed": 4}}, {"perovski_abrotanoides_oil": {"black_sesame_seed": 1}}, {"clove_oil": {"black_sesame_seed": 2}}, {"litchi": {"black_sesame_seed": 3}}, {"kelp": {"black_sesame_seed": 1}}, {"tahiti_vanilla": {"black_sesame_seed": 2}}, {"feijoa": {"black_sesame_seed": 4}}, {"globefish": {"black_sesame_seed": 3}}, {"caraway": {"black_sesame_seed": 2}}, {"japanese_peppermint_oil": {"black_sesame_seed": 1}}, {"lovage": {"black_sesame_seed": 7}}, {"dill": {"black_sesame_seed": 9}}, {"mackerel": {"black_sesame_seed": 3}}, {"mexican_lime": {"black_sesame_seed": 5}}, {"pecan": {"black_sesame_seed": 3}}, {"mushroom": {"black_sesame_seed": 1}}, {"lovage_leaf": {"black_sesame_seed": 1}}, {"eel": {"black_sesame_seed": 3}}, {"cognac": {"black_sesame_seed": 5}}, {"fried_beef": {"black_sesame_seed": 2}}, {"red_bean": {"black_sesame_seed": 4}}, {"star_anise": {"black_sesame_seed": 1}}, {"citrus_juice": {"black_sesame_seed": 2}}, {"neroli_bigarade": {"black_sesame_seed": 1}}, {"hop_oil": {"black_sesame_seed": 3}}, {"roasted_chicken": {"black_sesame_seed": 1}}, {"blue_cheese": {"black_sesame_seed": 1}}, {"pouching_tea": {"black_sesame_seed": 7}}, {"domiati_cheese": {"black_sesame_seed": 1}}, {"callitris": {"black_sesame_seed": 1}}, {"roasted_green_tea": {"black_sesame_seed": 7}}, {"cherimoya": {"black_sesame_seed": 1}}, {"elder_flower": {"black_sesame_seed": 1}}, {"guava": {"black_sesame_seed": 6}}, {"lime_peel_oil": {"black_sesame_seed": 3}}, {"matsutake": {"black_sesame_seed": 1}}, {"olive": {"black_sesame_seed": 2}}, {"clove": {"black_sesame_seed": 5}}, {"ceylon_tea_cinnamon_leaf": {"black_sesame_seed": 1}}, {"sperm_whale_oil": {"black_sesame_seed": 1}}, {"california_orange_peel": {"black_sesame_seed": 2}}, {"rye_bread": {"black_sesame_seed": 3}}, {"citrus": {"black_sesame_seed": 4}}, {"mozzarella_cheese": {"black_sesame_seed": 1}}, {"petitgrain": {"black_sesame_seed": 1}}, {"boiled_chicken": {"black_sesame_seed": 1}}, {"roasted_turkey": {"black_sesame_seed": 1}}, {"dill_seed": {"black_sesame_seed": 17}}, {"mandarin": {"black_sesame_seed": 4}}, {"scallop": {"black_sesame_seed": 1}}, {"corn_oil": {"black_sesame_seed": 2}}, {"carrot": {"black_sesame_seed": 4}}, {"eucalyptus_globulus_oil": {"black_sesame_seed": 1}}, {"white_bread": {"black_sesame_seed": 1}}, {"java_citronella_oil": {"black_sesame_seed": 2}}, {"rosemary": {"black_sesame_seed": 5}}, {"tamarind": {"black_sesame_seed": 4}}, {"scotch_spearmint": {"black_sesame_seed": 4}}, {"rabbiteye_blueberry": {"black_sesame_seed": 5}}, {"fennel_oil": {"black_sesame_seed": 1}}, {"tilsit_cheese": {"black_sesame_seed": 1}}, {"squid": {"black_sesame_seed": 1}}, {"cardamom": {"black_sesame_seed": 7}}, {"tea": {"black_sesame_seed": 7}}, {"pilchard": {"black_sesame_seed": 3}}, {"starfruit": {"black_sesame_seed": 4}}, {"wild_strawberry": {"black_sesame_seed": 5}}, {"malt": {"black_sesame_seed": 3}}, {"tomato": {"black_sesame_seed": 3}}, {"lemon": {"black_sesame_seed": 5}}, {"loquat": {"black_sesame_seed": 3}}, {"roquefort_cheese": {"black_sesame_seed": 1}}, {"mentha_silvestris_oil": {"black_sesame_seed": 1}}, {"palm_fruit": {"black_sesame_seed": 2}}, {"mandarin_peel_oil": {"black_sesame_seed": 3}}, {"fig": {"black_sesame_seed": 2}}, {"kola_tea": {"black_sesame_seed": 7}}, {"japanese_peppermint": {"black_sesame_seed": 7}}, {"caja_fruit": {"black_sesame_seed": 2}}, {"watercress": {"black_sesame_seed": 1}}, {"hog_plum": {"black_sesame_seed": 2}}, {"buckwheat": {"black_sesame_seed": 2}}, {"red_wine": {"black_sesame_seed": 3}}, {"botrytized_wine": {"black_sesame_seed": 3}}, {"ethiopian_pepper": {"black_sesame_seed": 5}}, {"smoked_salmon": {"black_sesame_seed": 3}}, {"lamb": {"black_sesame_seed": 2}}, {"mace": {"black_sesame_seed": 6}}, {"echinacea": {"black_sesame_seed": 1}}, {"cottage_cheese": {"black_sesame_seed": 1}}]} 5 | "#; 6 | let mut parser = ondemand::Parser::default(); 7 | let padded_string = data.to_padded_string(); 8 | let mut doc = parser.iterate(&padded_string)?; 9 | 10 | let mut arr = doc.get_object()?.at_pointer("/ingredients")?.get_array()?; 11 | 12 | for value in arr.iter()? { 13 | let mut object = value?.get_object()?; 14 | 15 | for field in object.iter()? { 16 | let mut field = field?; 17 | 18 | let key = field.unescaped_key(false)?; 19 | let mut value = field.take_value(); 20 | println!("key: {} | value: {}", key, value.get_object()?.raw_json()?); 21 | } 22 | } 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /simdjson-sys/src/simdjson_c_api.cpp: -------------------------------------------------------------------------------- 1 | #include "simdjson_c_api.h" 2 | #include "simdjson.h" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace simdjson; 8 | 9 | namespace { 10 | 11 | template inline U object_to_pointer(T &&t) { 12 | return reinterpret_cast(new T(std::move(t))); 13 | } 14 | 15 | // template 16 | // auto simdjson_result_to_struct(simdjson_result &&sr) { 17 | // T value; 18 | // const error_code error = std::move(sr).get(value); 19 | // return {static_cast(error), 20 | // reinterpret_cast(new T(std::move(value)))}; 21 | // } 22 | 23 | // template 24 | // inline int enum_result_to_number_result(simdjson_result&& enum_result) { 25 | // T inner; 26 | // auto error = std::move(enum_result).get(inner); 27 | // if (error == error_code::SUCCESS) { 28 | 29 | // } 30 | // } 31 | 32 | } // namespace 33 | 34 | #define IMPL_CLASS(name, type) \ 35 | void name##_free(name *r) { delete reinterpret_cast(r); } 36 | 37 | // Use value_unsafe because we always check the error code first. 38 | #define IMPL_RESULT(name, type) \ 39 | IMPL_CLASS(name##_result, simdjson_result) \ 40 | int name##_result_error(const name##_result *r) { \ 41 | auto code = reinterpret_cast *>(r)->error(); \ 42 | return static_cast(code); \ 43 | } \ 44 | name *name##_result_value_unsafe(name##_result *r) { \ 45 | auto result = reinterpret_cast *>(r); \ 46 | return object_to_pointer(std::move(*result).value_unsafe()); \ 47 | } 48 | 49 | #define IMPL_PRIMITIVE_RESULT(name) \ 50 | IMPL_CLASS(name##_result, simdjson_result) \ 51 | int name##_result_error(const name##_result *r) { \ 52 | auto code = reinterpret_cast *>(r)->error(); \ 53 | return static_cast(code); \ 54 | } \ 55 | name name##_result_value_unsafe(name##_result *r) { \ 56 | auto result = reinterpret_cast *>(r); \ 57 | return std::move(*result).value_unsafe(); \ 58 | } 59 | 60 | #define IMPL_AT_POINTER(self, type) \ 61 | SJ_OD_value_result *self##_at_pointer(self *r, const char *s, size_t len) { \ 62 | auto result = \ 63 | reinterpret_cast(r)->at_pointer(std::string_view(s, len)); \ 64 | return object_to_pointer(std::move(result)); \ 65 | } 66 | 67 | // IMPL_CLASS(SJ_padded_string, padded_string) 68 | // IMPL_RESULT(SJ_padded_string, padded_string) 69 | IMPL_CLASS(SJ_OD_parser, ondemand::parser) 70 | IMPL_CLASS(SJ_OD_document, ondemand::document) 71 | IMPL_RESULT(SJ_OD_document, ondemand::document) 72 | IMPL_CLASS(STD_string_view, std::string_view) 73 | IMPL_RESULT(STD_string_view, std::string_view) 74 | IMPL_CLASS(SJ_OD_value, ondemand::value) 75 | IMPL_RESULT(SJ_OD_value, ondemand::value) 76 | IMPL_CLASS(SJ_OD_array, ondemand::array) 77 | IMPL_RESULT(SJ_OD_array, ondemand::array) 78 | IMPL_CLASS(SJ_OD_object, ondemand::object) 79 | IMPL_RESULT(SJ_OD_object, ondemand::object) 80 | IMPL_CLASS(SJ_OD_raw_json_string, ondemand::raw_json_string) 81 | IMPL_RESULT(SJ_OD_raw_json_string, ondemand::raw_json_string) 82 | IMPL_CLASS(SJ_OD_array_iterator, ondemand::array_iterator) 83 | IMPL_RESULT(SJ_OD_array_iterator, ondemand::array_iterator) 84 | IMPL_CLASS(SJ_OD_object_iterator, ondemand::object_iterator) 85 | IMPL_RESULT(SJ_OD_object_iterator, ondemand::object_iterator) 86 | IMPL_CLASS(SJ_OD_field, ondemand::field) 87 | IMPL_RESULT(SJ_OD_field, ondemand::field) 88 | IMPL_CLASS(SJ_OD_number, ondemand::number) 89 | IMPL_RESULT(SJ_OD_number, ondemand::number) 90 | 91 | IMPL_PRIMITIVE_RESULT(uint64_t) 92 | IMPL_PRIMITIVE_RESULT(int64_t) 93 | IMPL_PRIMITIVE_RESULT(double) 94 | IMPL_PRIMITIVE_RESULT(bool) 95 | IMPL_PRIMITIVE_RESULT(size_t) 96 | IMPL_PRIMITIVE_RESULT(int) 97 | 98 | // SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) { 99 | // return object_to_pointer(padded_string(s, len)); 100 | // } 101 | 102 | // SJ_padded_string_result *SJ_padded_string_load(const char *path) { 103 | // return object_to_pointer( 104 | // padded_string::load(path)); 105 | // } 106 | // size_t SJ_padded_string_length(const SJ_padded_string *ps) { 107 | // return reinterpret_cast(ps)->length(); 108 | // } 109 | // const uint8_t *SJ_padded_string_u8data(const SJ_padded_string *ps) { 110 | // return reinterpret_cast(ps)->u8data(); 111 | // } 112 | 113 | SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity) { 114 | return object_to_pointer(ondemand::parser(max_capacity)); 115 | } 116 | 117 | SJ_OD_document_result * 118 | SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, 119 | const SJ_padded_string *s) { 120 | auto doc = reinterpret_cast(parser)->iterate( 121 | *reinterpret_cast(s)); 122 | // return reinterpret_cast(new 123 | // simdjson_result(std::move(doc))); 124 | return object_to_pointer(std::move(doc)); 125 | } 126 | 127 | SJ_OD_document_result * 128 | SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, 129 | size_t len, size_t allocated) { 130 | auto doc = reinterpret_cast(parser)->iterate(json, len, 131 | allocated); 132 | return object_to_pointer(std::move(doc)); 133 | } 134 | 135 | SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc) { 136 | auto value = reinterpret_cast(doc)->get_value(); 137 | return object_to_pointer(std::move(value)); 138 | } 139 | 140 | // self, self's real name, output value, how to get output value 141 | #define IMPL_GET(self, real_name, value, method) \ 142 | value##_result *self##_##method(self *r) { \ 143 | auto result = reinterpret_cast(r)->method(); \ 144 | return object_to_pointer(std::move(result)); \ 145 | } 146 | 147 | // ondemand::value 148 | IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_object, get_object) 149 | IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_array, get_array) 150 | IMPL_GET(SJ_OD_value, ondemand::value, uint64_t, get_uint64) 151 | IMPL_GET(SJ_OD_value, ondemand::value, int64_t, get_int64) 152 | IMPL_GET(SJ_OD_value, ondemand::value, double, get_double) 153 | IMPL_GET(SJ_OD_value, ondemand::value, bool, get_bool) 154 | IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_raw_json_string, 155 | get_raw_json_string) 156 | IMPL_GET(SJ_OD_value, ondemand::value, STD_string_view, get_wobbly_string) 157 | IMPL_GET(SJ_OD_value, ondemand::value, bool, is_null) 158 | IMPL_GET(SJ_OD_value, ondemand::value, int, type) 159 | IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_number, get_number) 160 | 161 | IMPL_AT_POINTER(SJ_OD_value, ondemand::value) 162 | 163 | STD_string_view_result *SJ_OD_value_get_string(SJ_OD_value *self, 164 | bool allow_replacement) { 165 | auto result = 166 | reinterpret_cast(self)->get_string(allow_replacement); 167 | return object_to_pointer(std::move(result)); 168 | } 169 | 170 | // ondemand::document 171 | IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_object, get_object) 172 | IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_array, get_array) 173 | IMPL_GET(SJ_OD_document, ondemand::document, uint64_t, get_uint64) 174 | IMPL_GET(SJ_OD_document, ondemand::document, int64_t, get_int64) 175 | IMPL_GET(SJ_OD_document, ondemand::document, double, get_double) 176 | IMPL_GET(SJ_OD_document, ondemand::document, bool, get_bool) 177 | IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_raw_json_string, 178 | get_raw_json_string) 179 | IMPL_GET(SJ_OD_document, ondemand::document, STD_string_view, get_wobbly_string) 180 | IMPL_GET(SJ_OD_document, ondemand::document, bool, is_null) 181 | IMPL_GET(SJ_OD_document, ondemand::document, int, type) 182 | IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_number, get_number) 183 | 184 | IMPL_AT_POINTER(SJ_OD_document, ondemand::document) 185 | 186 | STD_string_view_result *SJ_OD_document_get_string(SJ_OD_document *self, 187 | bool allow_replacement) { 188 | auto result = reinterpret_cast(self)->get_string( 189 | allow_replacement); 190 | return object_to_pointer(std::move(result)); 191 | } 192 | 193 | const char *STD_string_view_data(STD_string_view *sv) { 194 | return reinterpret_cast(sv)->data(); 195 | } 196 | 197 | size_t STD_string_view_size(STD_string_view *sv) { 198 | return reinterpret_cast(sv)->size(); 199 | } 200 | 201 | IMPL_GET(SJ_OD_array, ondemand::array, size_t, count_elements) 202 | IMPL_GET(SJ_OD_array, ondemand::array, bool, is_empty) 203 | IMPL_GET(SJ_OD_array, ondemand::array, bool, reset) 204 | IMPL_GET(SJ_OD_array, ondemand::array, SJ_OD_array_iterator, begin) 205 | IMPL_GET(SJ_OD_array, ondemand::array, SJ_OD_array_iterator, end) 206 | IMPL_GET(SJ_OD_array, ondemand::array, STD_string_view, raw_json) 207 | IMPL_AT_POINTER(SJ_OD_array, ondemand::array) 208 | 209 | SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index) { 210 | auto result = reinterpret_cast(array)->at(index); 211 | return object_to_pointer(std::move(result)); 212 | } 213 | 214 | // ondemand::array_iterator 215 | SJ_OD_value_result *SJ_OD_array_iterator_get(SJ_OD_array_iterator *self) { 216 | auto ptr = reinterpret_cast(self); 217 | return object_to_pointer(**ptr); 218 | } 219 | bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator *lhs, 220 | const SJ_OD_array_iterator *rhs) { 221 | return *reinterpret_cast(lhs) != 222 | *reinterpret_cast(rhs); 223 | } 224 | void SJ_OD_array_iterator_step(SJ_OD_array_iterator *self) { 225 | auto ptr = reinterpret_cast(self); 226 | ++(*ptr); 227 | } 228 | 229 | // ondemand::object 230 | IMPL_GET(SJ_OD_object, ondemand::object, SJ_OD_object_iterator, begin) 231 | IMPL_GET(SJ_OD_object, ondemand::object, SJ_OD_object_iterator, end) 232 | IMPL_GET(SJ_OD_object, ondemand::object, STD_string_view, raw_json) 233 | IMPL_GET(SJ_OD_object, ondemand::object, bool, is_empty) 234 | IMPL_GET(SJ_OD_object, ondemand::object, bool, reset) 235 | IMPL_GET(SJ_OD_object, ondemand::object, size_t, count_fields) 236 | IMPL_AT_POINTER(SJ_OD_object, ondemand::object) 237 | 238 | SJ_OD_value_result *SJ_OD_object_find_field(SJ_OD_object *object, 239 | const char *data, size_t len) { 240 | auto result = reinterpret_cast(object)->find_field( 241 | std::string_view(data, len)); 242 | return object_to_pointer(std::move(result)); 243 | } 244 | SJ_OD_value_result *SJ_OD_object_find_field_unordered(SJ_OD_object *object, 245 | const char *data, 246 | size_t len) { 247 | auto result = 248 | reinterpret_cast(object)->find_field_unordered( 249 | std::string_view(data, len)); 250 | return object_to_pointer(std::move(result)); 251 | } 252 | 253 | // ondemand::object_iterator 254 | SJ_OD_field_result *SJ_OD_object_iterator_get(SJ_OD_object_iterator *self) { 255 | auto ptr = reinterpret_cast(self); 256 | return object_to_pointer(**ptr); 257 | } 258 | bool SJ_OD_object_iterator_not_equal(const SJ_OD_object_iterator *lhs, 259 | const SJ_OD_object_iterator *rhs) { 260 | return *reinterpret_cast(lhs) != 261 | *reinterpret_cast(rhs); 262 | } 263 | void SJ_OD_object_iterator_step(SJ_OD_object_iterator *self) { 264 | auto ptr = reinterpret_cast(self); 265 | ++(*ptr); 266 | } 267 | 268 | // ondemand::field 269 | STD_string_view_result *SJ_OD_field_unescaped_key(SJ_OD_field *self, 270 | bool allow_replacement) { 271 | auto result = reinterpret_cast(self)->unescaped_key( 272 | allow_replacement); 273 | return object_to_pointer(std::move(result)); 274 | } 275 | SJ_OD_value *SJ_OD_field_value(SJ_OD_field *self) { 276 | ondemand::value &value = reinterpret_cast(self)->value(); 277 | return reinterpret_cast(&value); 278 | } 279 | SJ_OD_value *SJ_OD_field_take_value(SJ_OD_field *self) { 280 | auto field = reinterpret_cast(self); 281 | auto value = std::move(*field).value(); 282 | return object_to_pointer(std::move(value)); 283 | } 284 | 285 | // ondemand::number 286 | #define IMPL_GET_PRIMITIVE(self, real_name, value, method) \ 287 | value self##_##method(self *r) { \ 288 | return reinterpret_cast(r)->method(); \ 289 | } 290 | 291 | IMPL_GET_PRIMITIVE(SJ_OD_number, ondemand::number, uint64_t, get_uint64) 292 | IMPL_GET_PRIMITIVE(SJ_OD_number, ondemand::number, int64_t, get_int64) 293 | IMPL_GET_PRIMITIVE(SJ_OD_number, ondemand::number, double, get_double) 294 | 295 | int SJ_OD_number_get_number_type(SJ_OD_number *self) { 296 | return static_cast( 297 | reinterpret_cast(self)->get_number_type()); 298 | } 299 | 300 | // New macros for dom 301 | #define IMPL_HANDLE(name, type) \ 302 | void name##_free(name *r) { delete reinterpret_cast(r); } \ 303 | inline type *cast_to_type(name *r) { return reinterpret_cast(r); } \ 304 | inline name *move_to_handle(type &&r) { \ 305 | return object_to_pointer(std::move(r)); \ 306 | } 307 | 308 | IMPL_HANDLE(SJ_DOM_parser, dom::parser) 309 | IMPL_HANDLE(SJ_DOM_array, dom::array) 310 | IMPL_HANDLE(SJ_DOM_element, dom::element) 311 | IMPL_HANDLE(SJ_DOM_object, dom::object) 312 | IMPL_HANDLE(SJ_DOM_array_iterator, dom::array::iterator) 313 | IMPL_HANDLE(SJ_DOM_object_iterator, dom::object::iterator) 314 | IMPL_HANDLE(SJ_DOM_document, dom::document) 315 | IMPL_HANDLE(SJ_DOM_document_stream, dom::document_stream) 316 | IMPL_HANDLE(SJ_DOM_document_stream_iterator, dom::document_stream::iterator) 317 | 318 | // dom::parser 319 | SJ_DOM_parser *SJ_DOM_parser_new(size_t max_capacity) { 320 | return object_to_pointer(dom::parser(max_capacity)); 321 | } 322 | 323 | SJ_DOM_element_result SJ_DOM_parser_parse(SJ_DOM_parser *parser, 324 | const char *json, size_t len) { 325 | dom::element value; 326 | const auto error = reinterpret_cast(parser) 327 | ->parse(json, len, false) 328 | .get(value); // The string is padded, so false. 329 | return {static_cast(error), move_to_handle(std::move(value))}; 330 | } 331 | SJ_DOM_element_result SJ_DOM_parser_parse_into_document(SJ_DOM_parser *parser, 332 | SJ_DOM_document *doc, 333 | const char *json, 334 | size_t len) { 335 | dom::element value; 336 | const auto error = cast_to_type(parser) 337 | ->parse_into_document( 338 | *reinterpret_cast(doc), json, len) 339 | .get(value); 340 | return {static_cast(error), move_to_handle(std::move(value))}; 341 | } 342 | SJ_DOM_document_stream_result SJ_DOM_parser_parse_many(SJ_DOM_parser *parser, 343 | const char *json, 344 | size_t len, 345 | size_t batch_size) { 346 | dom::document_stream value; 347 | const auto error = 348 | cast_to_type(parser)->parse_many(json, len, batch_size).get(value); 349 | return {static_cast(error), move_to_handle(std::move(value))}; 350 | } 351 | 352 | // dom::element 353 | int SJ_DOM_element_type(SJ_DOM_element *self) { 354 | return static_cast(reinterpret_cast(self)->type()); 355 | } 356 | 357 | SJ_DOM_array_result SJ_DOM_element_get_array(SJ_DOM_element *self) { 358 | dom::array res; 359 | const error_code error = cast_to_type(self)->get_array().get(res); 360 | return {static_cast(error), move_to_handle(std::move(res))}; 361 | } 362 | SJ_DOM_object_result SJ_DOM_element_get_object(SJ_DOM_element *self) { 363 | dom::object res; 364 | const error_code error = cast_to_type(self)->get_object().get(res); 365 | return {static_cast(error), move_to_handle(std::move(res))}; 366 | } 367 | 368 | SJ_string_view_result SJ_DOM_element_get_string(SJ_DOM_element *self) { 369 | std::string_view res; 370 | const error_code error = cast_to_type(self)->get_string().get(res); 371 | return {static_cast(error), {.data = res.data(), .len = res.size()}}; 372 | } 373 | 374 | SJ_uint64_t_result SJ_DOM_element_get_uint64(SJ_DOM_element *self) { 375 | uint64_t res = 0; 376 | const error_code error = cast_to_type(self)->get_uint64().get(res); 377 | return {static_cast(error), res}; 378 | } 379 | SJ_int64_t_result SJ_DOM_element_get_int64(SJ_DOM_element *self) { 380 | int64_t res = 0; 381 | const error_code error = cast_to_type(self)->get_int64().get(res); 382 | return {static_cast(error), res}; 383 | } 384 | SJ_double_result SJ_DOM_element_get_double(SJ_DOM_element *self) { 385 | double res = 0.0; 386 | const error_code error = cast_to_type(self)->get_double().get(res); 387 | return {static_cast(error), res}; 388 | } 389 | SJ_bool_result SJ_DOM_element_get_bool(SJ_DOM_element *self) { 390 | bool res = false; 391 | const error_code error = cast_to_type(self)->get_bool().get(res); 392 | return {static_cast(error), res}; 393 | } 394 | SJ_DOM_element_result SJ_DOM_element_at_pointer(SJ_DOM_element *self, 395 | const char *json, size_t len) { 396 | dom::element res; 397 | const error_code error = 398 | cast_to_type(self)->at_pointer(std::string_view(json, len)).get(res); 399 | return {static_cast(error), move_to_handle(std::move(res))}; 400 | } 401 | 402 | // dom::array 403 | SJ_DOM_array_iterator *SJ_DOM_array_begin(SJ_DOM_array *self) { 404 | return move_to_handle(cast_to_type(self)->begin()); 405 | } 406 | SJ_DOM_array_iterator *SJ_DOM_array_end(SJ_DOM_array *self) { 407 | return move_to_handle(cast_to_type(self)->end()); 408 | } 409 | size_t SJ_DOM_array_size(SJ_DOM_array *self) { 410 | return cast_to_type(self)->size(); 411 | } 412 | size_t SJ_DOM_array_number_of_slots(SJ_DOM_array *self) { 413 | return cast_to_type(self)->number_of_slots(); 414 | } 415 | SJ_DOM_element_result SJ_DOM_array_at(SJ_DOM_array *self, size_t index) { 416 | dom::element res; 417 | const error_code error = cast_to_type(self)->at(index).get(res); 418 | return {static_cast(error), move_to_handle(std::move(res))}; 419 | } 420 | SJ_DOM_element_result SJ_DOM_array_at_pointer(SJ_DOM_array *self, 421 | const char *json, size_t len) { 422 | dom::element res; 423 | const error_code error = 424 | cast_to_type(self)->at_pointer(std::string_view(json, len)).get(res); 425 | return {static_cast(error), move_to_handle(std::move(res))}; 426 | } 427 | 428 | // dom::array::iterator 429 | SJ_DOM_element *SJ_DOM_array_iterator_get(SJ_DOM_array_iterator *self) { 430 | return move_to_handle(**cast_to_type(self)); 431 | } 432 | bool SJ_DOM_array_iterator_not_equal(SJ_DOM_array_iterator *lhs, 433 | SJ_DOM_array_iterator *rhs) { 434 | return *cast_to_type(lhs) != *cast_to_type(rhs); 435 | } 436 | void SJ_DOM_array_iterator_step(SJ_DOM_array_iterator *self) { 437 | ++(*cast_to_type(self)); 438 | } 439 | 440 | // dom::object 441 | SJ_DOM_object_iterator *SJ_DOM_object_begin(SJ_DOM_object *self) { 442 | return move_to_handle(cast_to_type(self)->begin()); 443 | } 444 | SJ_DOM_object_iterator *SJ_DOM_object_end(SJ_DOM_object *self) { 445 | return move_to_handle(cast_to_type(self)->end()); 446 | } 447 | size_t SJ_DOM_object_size(SJ_DOM_object *self) { 448 | return cast_to_type(self)->size(); 449 | } 450 | SJ_DOM_element_result SJ_DOM_object_at_pointer(SJ_DOM_object *self, 451 | const char *json, size_t len) { 452 | dom::element res; 453 | const error_code error = 454 | cast_to_type(self)->at_pointer(std::string_view(json, len)).get(res); 455 | return {static_cast(error), move_to_handle(std::move(res))}; 456 | } 457 | SJ_DOM_element_result SJ_DOM_object_at_key(SJ_DOM_object *self, 458 | const char *json, size_t len) { 459 | dom::element res; 460 | const error_code error = 461 | cast_to_type(self)->at_key(std::string_view(json, len)).get(res); 462 | return {static_cast(error), move_to_handle(std::move(res))}; 463 | } 464 | SJ_DOM_element_result SJ_DOM_object_at_key_case_insensitive(SJ_DOM_object *self, 465 | const char *json, 466 | size_t len) { 467 | dom::element res; 468 | const error_code error = 469 | cast_to_type(self) 470 | ->at_key_case_insensitive(std::string_view(json, len)) 471 | .get(res); 472 | return {static_cast(error), move_to_handle(std::move(res))}; 473 | } 474 | 475 | // dom::object::iterator 476 | SJ_DOM_key_value_pair SJ_DOM_object_iterator_get(SJ_DOM_object_iterator *self) { 477 | dom::key_value_pair pair = **cast_to_type(self); 478 | return {.key = {.data = pair.key.data(), .len = pair.key.size()}, 479 | .value = move_to_handle(std::move(pair.value))}; 480 | } 481 | bool SJ_DOM_object_iterator_not_equal(SJ_DOM_object_iterator *lhs, 482 | SJ_DOM_object_iterator *rhs) { 483 | return *cast_to_type(lhs) != *cast_to_type(rhs); 484 | } 485 | void SJ_DOM_object_iterator_step(SJ_DOM_object_iterator *self) { 486 | ++(*cast_to_type(self)); 487 | } 488 | 489 | // dom::document 490 | SJ_DOM_document *SJ_DOM_document_new() { 491 | return object_to_pointer(dom::document()); 492 | } 493 | 494 | SJ_DOM_element *SJ_DOM_document_root(SJ_DOM_document *self) { 495 | return move_to_handle(cast_to_type(self)->root()); 496 | } 497 | SJ_DOM_document_stream_iterator * 498 | SJ_DOM_document_stream_begin(SJ_DOM_document_stream *self) { 499 | return move_to_handle(cast_to_type(self)->begin()); 500 | } 501 | SJ_DOM_document_stream_iterator * 502 | SJ_DOM_document_stream_end(SJ_DOM_document_stream *self) { 503 | return move_to_handle(cast_to_type(self)->end()); 504 | } 505 | SJ_DOM_element_result 506 | SJ_DOM_document_stream_iterator_get(SJ_DOM_document_stream_iterator *self) { 507 | dom::element res; 508 | const error_code error = cast_to_type(self)->operator*().get(res); 509 | return {static_cast(error), move_to_handle(std::move(res))}; 510 | } 511 | void SJ_DOM_document_stream_iterator_step( 512 | SJ_DOM_document_stream_iterator *self) { 513 | ++(*cast_to_type(self)); 514 | } 515 | bool SJ_DOM_document_stream_iterator_not_equal( 516 | SJ_DOM_document_stream_iterator *lhs, 517 | SJ_DOM_document_stream_iterator *rhs) { 518 | return *cast_to_type(lhs) != *cast_to_type(rhs); 519 | } 520 | --------------------------------------------------------------------------------