From fa971ad49a9d69363d52b504e2e6d175cbd3e34f Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Wed, 23 Aug 2023 16:58:56 +0800 Subject: [PATCH 01/31] add submodule --- .gitmodules | 3 +++ simdjson | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 simdjson diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..675ef0e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "simdjson"] + path = simdjson + url = https://ghproxy.com/https://github.com/simdjson/simdjson.git diff --git a/simdjson b/simdjson new file mode 160000 index 0000000..bf849e3 --- /dev/null +++ b/simdjson @@ -0,0 +1 @@ +Subproject commit bf849e36191d4cf2442f4af57a794103df3e2979 From ee66fb61c2728427b0a09be45af4d9004e4edb7c Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Wed, 23 Aug 2023 16:59:07 +0800 Subject: [PATCH 02/31] add binding code --- Cargo.toml | 5 ++-- simdjson-sys/Cargo.toml | 12 +++++++++ simdjson-sys/build.rs | 39 +++++++++++++++++++++++++++++ simdjson-sys/src/lib.rs | 19 ++++++++++++++ simdjson-sys/src/simdjson_c_api.cpp | 23 +++++++++++++++++ simdjson-sys/src/simdjson_c_api.h | 30 ++++++++++++++++++++++ 6 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 simdjson-sys/Cargo.toml create mode 100644 simdjson-sys/build.rs create mode 100644 simdjson-sys/src/lib.rs create mode 100644 simdjson-sys/src/simdjson_c_api.cpp create mode 100644 simdjson-sys/src/simdjson_c_api.h diff --git a/Cargo.toml b/Cargo.toml index cd50e2b..3a184df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ license = "Apache-2.0" # [dependencies] # simdjson-sys = {path="simdjson-sys"} -# [workspace] -# members = ["simdjson-sys"] +[workspace] +members = ["simdjson-sys"] [dependencies] @@ -31,3 +31,4 @@ default = ["serde_impl"] # serde compatibility serde_impl = ["serde", "serde_json"] + diff --git a/simdjson-sys/Cargo.toml b/simdjson-sys/Cargo.toml new file mode 100644 index 0000000..c74b0f1 --- /dev/null +++ b/simdjson-sys/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "simdjson-sys" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +bindgen = "0.66.1" +cc = { version = "1.0.83", features = ["parallel"] } diff --git a/simdjson-sys/build.rs b/simdjson-sys/build.rs new file mode 100644 index 0000000..fcd5d13 --- /dev/null +++ b/simdjson-sys/build.rs @@ -0,0 +1,39 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + cc::Build::new() + .cpp(true) + .flag_if_supported("-std=c++17") + .include("../simdjson/singleheader") + .file("src/simdjson_c_api.cpp") + .file("../simdjson/singleheader/simdjson.cpp") + .cargo_metadata(true) + .compile("simdjson_c_api"); + + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("src/simdjson_c_api.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + println!("cargo:rerun-if-changed=src/simdjson_c_api.h"); + println!("cargo:rerun-if-changed=src/simdjson_c_api.cpp"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + // println!( + // "cargo:rustc-link-lib=static={}", + // out_path.join("libsimdjson_c_api").display() + // ); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/simdjson-sys/src/lib.rs b/simdjson-sys/src/lib.rs new file mode 100644 index 0000000..6347d78 --- /dev/null +++ b/simdjson-sys/src/lib.rs @@ -0,0 +1,19 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + unsafe { + let s = "hello world"; + let ps = SJ_padded_string_new(s.as_ptr().cast(), s.len()); + SJ_padded_string_free(ps); + } + } +} diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp new file mode 100644 index 0000000..fac94d8 --- /dev/null +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -0,0 +1,23 @@ +#include "simdjson_c_api.h" +#include "simdjson.h" + +#define IMPL_FREE(name) \ + void SJ_##name##_free(SJ_##name *r) \ + { \ + delete reinterpret_cast(r); \ + } \ + void SJ_simdjson_result_of_##name##_free(SJ_simdjson_result_of_##name *r) \ + { \ + delete reinterpret_cast *>(r); \ + } + +IMPL_FREE(padded_string) + +SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) +{ + return reinterpret_cast(new simdjson::padded_string(s, len)); +} + +// void SJ_padded_string_free(SJ_padded_string* r) { +// delete reinterpret_cast(r); +// } diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h new file mode 100644 index 0000000..529c613 --- /dev/null +++ b/simdjson-sys/src/simdjson_c_api.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#define FUNC_FOR_RESULT(name) \ + typedef struct SJ_simdjson_result_of_##name SJ_simdjson_result_of_##name; \ + int SJ_simdjson_result_of_##name##_error(const SJ_simdjson_result_of_##name *r); \ + SJ_##name *SJ_simdjson_result_of_##name##_value(const SJ_simdjson_result_of_##name *r); + +#define FUNC_FOR_OBJECT(name) \ + typedef struct SJ_##name SJ_##name; \ + void SJ_##name##_free(SJ_##name *r); + +#ifdef __cplusplus +extern "C" +{ +#endif + + // SJ = simdjson::* + FUNC_FOR_OBJECT(padded_string) + FUNC_FOR_RESULT(padded_string) + + SJ_padded_string *SJ_padded_string_new(const char *s, size_t len); + SJ_simdjson_result_of_padded_string SJ_padded_string_load(const char *path); // null terminated string. + + + +#ifdef __cplusplus +} +#endif From b485e713324ac8d6c523c1b3b286048c6a02becf Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Wed, 23 Aug 2023 20:20:59 +0800 Subject: [PATCH 03/31] macro macro macro --- simdjson-sys/src/simdjson_c_api.cpp | 92 +++++++++++++++++++++++++---- simdjson-sys/src/simdjson_c_api.h | 81 +++++++++++++++++++++---- 2 files changed, 152 insertions(+), 21 deletions(-) diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index fac94d8..d56da94 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -1,23 +1,95 @@ #include "simdjson_c_api.h" #include "simdjson.h" -#define IMPL_FREE(name) \ - void SJ_##name##_free(SJ_##name *r) \ +using namespace simdjson; + +namespace +{ + template + U *object_to_pointer(T &&t) + { + return reinterpret_cast(new T(std::move(t))); + } + +} + +#define IMPL_CLASS(name, type) \ + void name##_free(name *r) \ + { \ + delete reinterpret_cast(r); \ + } + +// Use value_unsafe because we always check the error code first. +#define IMPL_RESULT(name, type) \ + IMPL_CLASS(name##_result, simdjson_result) \ + int name##_result_error(const name##_result *r) \ { \ - delete reinterpret_cast(r); \ + auto code = reinterpret_cast *>(r)->error(); \ + return static_cast(code); \ } \ - void SJ_simdjson_result_of_##name##_free(SJ_simdjson_result_of_##name *r) \ + name *name##_result_value(name##_result *r) \ { \ - delete reinterpret_cast *>(r); \ + auto result = reinterpret_cast *>(r); \ + return object_to_pointer(std::move(*result).value_unsafe()); \ } -IMPL_FREE(padded_string) +#define IMPL_PRIMITIVE_RESULT(name) \ + IMPL_CLASS(name##_result, simdjson_result) \ + int name##_result_error(const name##_result *r) \ + { \ + auto code = reinterpret_cast *>(r)->error(); \ + return static_cast(code); \ + } \ + name name##_result_value(name##_result *r) \ + { \ + auto result = reinterpret_cast *>(r); \ + return std::move(*result).value_unsafe(); \ + } + +IMPL_CLASS(SJ_padded_string, padded_string) +IMPL_RESULT(SJ_padded_string, padded_string) +IMPL_CLASS(SJ_OD_parser, ondemand::parser) +IMPL_CLASS(SJ_OD_document, ondemand::document) +IMPL_RESULT(SJ_OD_document, ondemand::document) + +IMPL_PRIMITIVE_RESULT(uint64_t) +IMPL_PRIMITIVE_RESULT(int64_t) +IMPL_PRIMITIVE_RESULT(double) +IMPL_PRIMITIVE_RESULT(bool) +IMPL_PRIMITIVE_RESULT(SJ_string_view); SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) { - return reinterpret_cast(new simdjson::padded_string(s, len)); + // return reinterpret_cast(new simdjson::padded_string(s, len)); + return object_to_pointer(padded_string(s, len)); +} +SJ_padded_string_result *SJ_padded_string_load(const char *path) +{ + // return reinterpret_cast(new simdjson_result(padded_string::load(path))); + return object_to_pointer(padded_string::load(path)); } -// void SJ_padded_string_free(SJ_padded_string* r) { -// delete reinterpret_cast(r); -// } +SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity) +{ + // return reinterpret_cast(new ondemand::parser(max_capacity)); + return object_to_pointer(ondemand::parser(max_capacity)); +} + +SJ_OD_document_result *SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, const SJ_padded_string *s) +{ + auto doc = reinterpret_cast(parser)->iterate(*reinterpret_cast(s)); + // return reinterpret_cast(new simdjson_result(std::move(doc))); + return object_to_pointer(std::move(doc)); +} + +SJ_OD_document_result *SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, size_t len, size_t allocated) +{ + auto doc = reinterpret_cast(parser)->iterate(padded_string_view(json, len, allocated)); + return object_to_pointer(std::move(doc)); +} + +SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc) +{ + auto value = reinterpret_cast(doc)->get_value(); + return object_to_pointer(std::move(value)); +} diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index 529c613..93794a2 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -1,29 +1,88 @@ #pragma once +#include #include +#include -#define FUNC_FOR_RESULT(name) \ - typedef struct SJ_simdjson_result_of_##name SJ_simdjson_result_of_##name; \ - int SJ_simdjson_result_of_##name##_error(const SJ_simdjson_result_of_##name *r); \ - SJ_##name *SJ_simdjson_result_of_##name##_value(const SJ_simdjson_result_of_##name *r); +#define DEFINE_CLASS(name) \ + typedef struct name name; \ + void name##_free(name *r); -#define FUNC_FOR_OBJECT(name) \ - typedef struct SJ_##name SJ_##name; \ - void SJ_##name##_free(SJ_##name *r); +// `value` method will take the ownership of T. +#define DEFINE_RESULT(name) \ + DEFINE_CLASS(name##_result) \ + int name##_result_error(const name##_result *r); \ + name *name##_result_value(name##_result *r); + +// `value` method will free simdjson_result. +#define DEFINE_PRIMITIVE_RESULT(name) \ + DEFINE_CLASS(name##_result) \ + int name##_result_error(const name##_result *r); \ + name name##_result_value(name##_result *r); + +#define DEFINE_GET(self, value) \ + value##_result *self##_get_##value(self *r); #ifdef __cplusplus extern "C" { #endif - // SJ = simdjson::* - FUNC_FOR_OBJECT(padded_string) - FUNC_FOR_RESULT(padded_string) + // SJ for simdjson, OD for ondemand + DEFINE_CLASS(SJ_padded_string); + DEFINE_RESULT(SJ_padded_string); + DEFINE_CLASS(SJ_OD_parser); + DEFINE_CLASS(SJ_OD_document); + DEFINE_RESULT(SJ_OD_document); + DEFINE_CLASS(SJ_OD_value); + DEFINE_RESULT(SJ_OD_value); + DEFINE_CLASS(SJ_OD_array); + DEFINE_RESULT(SJ_OD_array); + DEFINE_CLASS(SJ_OD_object); + DEFINE_RESULT(SJ_OD_object); + DEFINE_CLASS(SJ_OD_raw_json_string); + DEFINE_RESULT(SJ_OD_raw_json_string); + + DEFINE_PRIMITIVE_RESULT(uint64_t); + DEFINE_PRIMITIVE_RESULT(int64_t); + DEFINE_PRIMITIVE_RESULT(double); + DEFINE_PRIMITIVE_RESULT(bool); SJ_padded_string *SJ_padded_string_new(const char *s, size_t len); - SJ_simdjson_result_of_padded_string SJ_padded_string_load(const char *path); // null terminated string. + SJ_padded_string_result *SJ_padded_string_load(const char *path); // null terminated string. + + SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity); + SJ_OD_document_result *SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, const SJ_padded_string *s); + SJ_OD_document_result *SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, size_t len, size_t allocated); + + SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc); + DEFINE_GET(SJ_OD_value, uint64_t); + DEFINE_GET(SJ_OD_value, int64_t); + DEFINE_GET(SJ_OD_value, double); + DEFINE_GET(SJ_OD_value, bool); + DEFINE_GET(SJ_OD_value, SJ_OD_array); + DEFINE_GET(SJ_OD_value, SJ_OD_object); + DEFINE_GET(SJ_OD_value, SJ_OD_raw_json_string); + + DEFINE_GET(SJ_OD_document, uint64_t); + DEFINE_GET(SJ_OD_document, int64_t); + DEFINE_GET(SJ_OD_document, double); + DEFINE_GET(SJ_OD_document, bool); + DEFINE_GET(SJ_OD_document, SJ_OD_array); + DEFINE_GET(SJ_OD_document, SJ_OD_object); + DEFINE_GET(SJ_OD_document, SJ_OD_raw_json_string); + typedef struct SJ_string_view + { + const char *ptr; + size_t len; + } SJ_string_view; + DEFINE_PRIMITIVE_RESULT(SJ_string_view); // Does not holding the memory. + SJ_string_view_result *SJ_OD_value_get_string(SJ_OD_value *value, bool allow_replacement); + SJ_string_view_result *SJ_OD_document_get_string(SJ_OD_document *doc); + SJ_string_view_result *SJ_OD_value_get_wobbly_string(SJ_OD_value *value); + SJ_string_view_result *SJ_OD_document_get_wobbly_string(SJ_OD_document *doc); #ifdef __cplusplus } From c85b78d2c122ee0d3c2844f35605f5b4392e7286 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Wed, 23 Aug 2023 23:29:53 +0800 Subject: [PATCH 04/31] add more methods --- .gitignore | 3 +- .gitmodules | 2 +- simdjson-sys/src/simdjson_c_api.cpp | 206 +++++++++++++++++++--------- simdjson-sys/src/simdjson_c_api.h | 161 ++++++++++++---------- 4 files changed, 234 insertions(+), 138 deletions(-) diff --git a/.gitignore b/.gitignore index 8b85e15..01d5bdb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ Cargo.lock .vscode/ .idea/ - +.cache/ +build/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 675ef0e..fa29b8a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "simdjson"] path = simdjson - url = https://ghproxy.com/https://github.com/simdjson/simdjson.git + url = https://github.com/simdjson/simdjson.git diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index d56da94..9b539e7 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -1,95 +1,173 @@ #include "simdjson_c_api.h" #include "simdjson.h" +#include +#include +#include using namespace simdjson; -namespace -{ - template - U *object_to_pointer(T &&t) - { - return reinterpret_cast(new T(std::move(t))); - } - +namespace { +template U *object_to_pointer(T &&t) { + return reinterpret_cast(new T(std::move(t))); } -#define IMPL_CLASS(name, type) \ - void name##_free(name *r) \ - { \ - delete reinterpret_cast(r); \ - } +} // namespace + +#define IMPL_CLASS(name, type) \ + void name##_free(name *r) { delete reinterpret_cast(r); } // Use value_unsafe because we always check the error code first. -#define IMPL_RESULT(name, type) \ - IMPL_CLASS(name##_result, simdjson_result) \ - int name##_result_error(const name##_result *r) \ - { \ - auto code = reinterpret_cast *>(r)->error(); \ - return static_cast(code); \ - } \ - name *name##_result_value(name##_result *r) \ - { \ - auto result = reinterpret_cast *>(r); \ - return object_to_pointer(std::move(*result).value_unsafe()); \ - } - -#define IMPL_PRIMITIVE_RESULT(name) \ - IMPL_CLASS(name##_result, simdjson_result) \ - int name##_result_error(const name##_result *r) \ - { \ - auto code = reinterpret_cast *>(r)->error(); \ - return static_cast(code); \ - } \ - name name##_result_value(name##_result *r) \ - { \ - auto result = reinterpret_cast *>(r); \ - return std::move(*result).value_unsafe(); \ - } +#define IMPL_RESULT(name, type) \ + IMPL_CLASS(name##_result, simdjson_result) \ + int name##_result_error(const name##_result *r) { \ + auto code = reinterpret_cast *>(r)->error(); \ + return static_cast(code); \ + } \ + name *name##_result_value(name##_result *r) { \ + auto result = reinterpret_cast *>(r); \ + return object_to_pointer(std::move(*result).value_unsafe()); \ + } + +#define IMPL_PRIMITIVE_RESULT(name) \ + IMPL_CLASS(name##_result, simdjson_result) \ + int name##_result_error(const name##_result *r) { \ + auto code = reinterpret_cast *>(r)->error(); \ + return static_cast(code); \ + } \ + name name##_result_value(name##_result *r) { \ + auto result = reinterpret_cast *>(r); \ + return std::move(*result).value_unsafe(); \ + } IMPL_CLASS(SJ_padded_string, padded_string) IMPL_RESULT(SJ_padded_string, padded_string) IMPL_CLASS(SJ_OD_parser, ondemand::parser) IMPL_CLASS(SJ_OD_document, ondemand::document) IMPL_RESULT(SJ_OD_document, ondemand::document) +IMPL_CLASS(STD_string_view, std::string_view) +IMPL_RESULT(STD_string_view, std::string_view) +IMPL_CLASS(SJ_OD_value, ondemand::value) +IMPL_RESULT(SJ_OD_value, ondemand::value) +IMPL_CLASS(SJ_OD_array, ondemand::array) +IMPL_RESULT(SJ_OD_array, ondemand::array) +IMPL_CLASS(SJ_OD_object, ondemand::object) +IMPL_RESULT(SJ_OD_object, ondemand::object) +IMPL_CLASS(SJ_OD_raw_json_string, ondemand::raw_json_string) +IMPL_RESULT(SJ_OD_raw_json_string, ondemand::raw_json_string) +IMPL_CLASS(SJ_OD_array_iterator, ondemand::array_iterator) +IMPL_RESULT(SJ_OD_array_iterator, ondemand::array_iterator) +IMPL_CLASS(SJ_OD_object_iterator, ondemand::object_iterator) +IMPL_RESULT(SJ_OD_object_iterator, ondemand::object_iterator) +IMPL_CLASS(SJ_OD_field, ondemand::field) +IMPL_RESULT(SJ_OD_field, ondemand::field) IMPL_PRIMITIVE_RESULT(uint64_t) IMPL_PRIMITIVE_RESULT(int64_t) IMPL_PRIMITIVE_RESULT(double) IMPL_PRIMITIVE_RESULT(bool) -IMPL_PRIMITIVE_RESULT(SJ_string_view); +IMPL_PRIMITIVE_RESULT(size_t) -SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) -{ - // return reinterpret_cast(new simdjson::padded_string(s, len)); - return object_to_pointer(padded_string(s, len)); +SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) { + // return reinterpret_cast(new simdjson::padded_string(s, + // len)); + return object_to_pointer(padded_string(s, len)); } -SJ_padded_string_result *SJ_padded_string_load(const char *path) -{ - // return reinterpret_cast(new simdjson_result(padded_string::load(path))); - return object_to_pointer(padded_string::load(path)); +SJ_padded_string_result *SJ_padded_string_load(const char *path) { + // return reinterpret_cast(new + // simdjson_result(padded_string::load(path))); + return object_to_pointer(padded_string::load(path)); } -SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity) -{ - // return reinterpret_cast(new ondemand::parser(max_capacity)); - return object_to_pointer(ondemand::parser(max_capacity)); +SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity) { + // return reinterpret_cast(new + // ondemand::parser(max_capacity)); + return object_to_pointer(ondemand::parser(max_capacity)); } -SJ_OD_document_result *SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, const SJ_padded_string *s) -{ - auto doc = reinterpret_cast(parser)->iterate(*reinterpret_cast(s)); - // return reinterpret_cast(new simdjson_result(std::move(doc))); - return object_to_pointer(std::move(doc)); +SJ_OD_document_result * +SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, + const SJ_padded_string *s) { + auto doc = reinterpret_cast(parser)->iterate( + *reinterpret_cast(s)); + // return reinterpret_cast(new + // simdjson_result(std::move(doc))); + return object_to_pointer(std::move(doc)); } -SJ_OD_document_result *SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, size_t len, size_t allocated) -{ - auto doc = reinterpret_cast(parser)->iterate(padded_string_view(json, len, allocated)); - return object_to_pointer(std::move(doc)); +SJ_OD_document_result * +SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, + size_t len, size_t allocated) { + auto doc = reinterpret_cast(parser)->iterate( + padded_string_view(json, len, allocated)); + return object_to_pointer(std::move(doc)); } -SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc) -{ - auto value = reinterpret_cast(doc)->get_value(); - return object_to_pointer(std::move(value)); +SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc) { + auto value = reinterpret_cast(doc)->get_value(); + return object_to_pointer(std::move(value)); +} + +// self, self's real name, output value, how to get output value +#define IMPL_GET(self, real_name, value, method) \ + value##_result *self##_##method(self *r) { \ + auto result = reinterpret_cast(r)->method(); \ + return object_to_pointer(std::move(result)); \ + } + +IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_object, get_object) +IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_array, get_array) +IMPL_GET(SJ_OD_value, ondemand::value, uint64_t, get_uint64) +IMPL_GET(SJ_OD_value, ondemand::value, int64_t, get_int64) +IMPL_GET(SJ_OD_value, ondemand::value, double, get_double) +IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_raw_json_string, + get_raw_json_string) + +IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_object, get_object) +IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_array, get_array) +IMPL_GET(SJ_OD_document, ondemand::document, uint64_t, get_uint64) +IMPL_GET(SJ_OD_document, ondemand::document, int64_t, get_int64) +IMPL_GET(SJ_OD_document, ondemand::document, double, get_double) +IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_raw_json_string, + get_raw_json_string) + +STD_string_view_result *SJ_OD_value_get_string(SJ_OD_value *self, + bool allow_replacement) { + auto result = + reinterpret_cast(self)->get_string(allow_replacement); + return object_to_pointer(std::move(result)); +} + +STD_string_view_result *SJ_OD_document_get_string(SJ_OD_document *self, + bool allow_replacement) { + auto result = reinterpret_cast(self)->get_string( + allow_replacement); + return object_to_pointer(std::move(result)); +} + +STD_string_view_result *SJ_OD_value_get_wobbly_string(SJ_OD_value *self) { + auto result = reinterpret_cast(self)->get_wobbly_string(); + return object_to_pointer(std::move(result)); +} + +STD_string_view_result *SJ_OD_document_get_wobbly_string(SJ_OD_document *self) { + auto result = + reinterpret_cast(self)->get_wobbly_string(); + return object_to_pointer(std::move(result)); +} + +const char *STD_string_view_data(STD_string_view *sv) { + return reinterpret_cast(sv)->data(); +} + +size_t STD_string_view_size(STD_string_view *sv) { + return reinterpret_cast(sv)->size(); +} + +IMPL_GET(SJ_OD_array, ondemand::array, size_t, count_elements) +IMPL_GET(SJ_OD_array, ondemand::array, bool, is_empty) +IMPL_GET(SJ_OD_array, ondemand::array, bool, reset) + +SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index) { + auto result = reinterpret_cast(array)->at(index); + return object_to_pointer(std::move(result)); } diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index 93794a2..bf749da 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -1,88 +1,105 @@ #pragma once -#include -#include #include +#include +#include -#define DEFINE_CLASS(name) \ - typedef struct name name; \ - void name##_free(name *r); +#define DEFINE_CLASS(name) \ + typedef struct name name; \ + void name##_free(name *r); // `value` method will take the ownership of T. -#define DEFINE_RESULT(name) \ - DEFINE_CLASS(name##_result) \ - int name##_result_error(const name##_result *r); \ - name *name##_result_value(name##_result *r); +#define DEFINE_RESULT(name) \ + DEFINE_CLASS(name##_result) \ + int name##_result_error(const name##_result *r); \ + name *name##_result_value(name##_result *r); // `value` method will free simdjson_result. -#define DEFINE_PRIMITIVE_RESULT(name) \ - DEFINE_CLASS(name##_result) \ - int name##_result_error(const name##_result *r); \ - name name##_result_value(name##_result *r); +#define DEFINE_PRIMITIVE_RESULT(name) \ + DEFINE_CLASS(name##_result) \ + int name##_result_error(const name##_result *r); \ + name name##_result_value(name##_result *r); -#define DEFINE_GET(self, value) \ - value##_result *self##_get_##value(self *r); +#define DEFINE_GET_WITH(self, value, method) \ + value##_result *self##_##method(self *r); #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - // SJ for simdjson, OD for ondemand - DEFINE_CLASS(SJ_padded_string); - DEFINE_RESULT(SJ_padded_string); - DEFINE_CLASS(SJ_OD_parser); - DEFINE_CLASS(SJ_OD_document); - DEFINE_RESULT(SJ_OD_document); - DEFINE_CLASS(SJ_OD_value); - DEFINE_RESULT(SJ_OD_value); - DEFINE_CLASS(SJ_OD_array); - DEFINE_RESULT(SJ_OD_array); - DEFINE_CLASS(SJ_OD_object); - DEFINE_RESULT(SJ_OD_object); - DEFINE_CLASS(SJ_OD_raw_json_string); - DEFINE_RESULT(SJ_OD_raw_json_string); - - DEFINE_PRIMITIVE_RESULT(uint64_t); - DEFINE_PRIMITIVE_RESULT(int64_t); - DEFINE_PRIMITIVE_RESULT(double); - DEFINE_PRIMITIVE_RESULT(bool); - - SJ_padded_string *SJ_padded_string_new(const char *s, size_t len); - SJ_padded_string_result *SJ_padded_string_load(const char *path); // null terminated string. - - SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity); - SJ_OD_document_result *SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, const SJ_padded_string *s); - SJ_OD_document_result *SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, size_t len, size_t allocated); - - SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc); - DEFINE_GET(SJ_OD_value, uint64_t); - DEFINE_GET(SJ_OD_value, int64_t); - DEFINE_GET(SJ_OD_value, double); - DEFINE_GET(SJ_OD_value, bool); - DEFINE_GET(SJ_OD_value, SJ_OD_array); - DEFINE_GET(SJ_OD_value, SJ_OD_object); - DEFINE_GET(SJ_OD_value, SJ_OD_raw_json_string); - - DEFINE_GET(SJ_OD_document, uint64_t); - DEFINE_GET(SJ_OD_document, int64_t); - DEFINE_GET(SJ_OD_document, double); - DEFINE_GET(SJ_OD_document, bool); - DEFINE_GET(SJ_OD_document, SJ_OD_array); - DEFINE_GET(SJ_OD_document, SJ_OD_object); - DEFINE_GET(SJ_OD_document, SJ_OD_raw_json_string); - - typedef struct SJ_string_view - { - const char *ptr; - size_t len; - } SJ_string_view; - DEFINE_PRIMITIVE_RESULT(SJ_string_view); // Does not holding the memory. - SJ_string_view_result *SJ_OD_value_get_string(SJ_OD_value *value, bool allow_replacement); - SJ_string_view_result *SJ_OD_document_get_string(SJ_OD_document *doc); - - SJ_string_view_result *SJ_OD_value_get_wobbly_string(SJ_OD_value *value); - SJ_string_view_result *SJ_OD_document_get_wobbly_string(SJ_OD_document *doc); +// SJ for simdjson, OD for ondemand +DEFINE_CLASS(SJ_padded_string) +DEFINE_RESULT(SJ_padded_string) +DEFINE_CLASS(SJ_OD_parser) +DEFINE_CLASS(SJ_OD_document) +DEFINE_RESULT(SJ_OD_document) +DEFINE_CLASS(SJ_OD_value) +DEFINE_RESULT(SJ_OD_value) +DEFINE_CLASS(SJ_OD_array) +DEFINE_RESULT(SJ_OD_array) +DEFINE_CLASS(SJ_OD_object) +DEFINE_RESULT(SJ_OD_object) +DEFINE_CLASS(SJ_OD_raw_json_string) +DEFINE_RESULT(SJ_OD_raw_json_string) +DEFINE_CLASS(STD_string_view) +DEFINE_RESULT(STD_string_view) +DEFINE_CLASS(SJ_OD_array_iterator) +DEFINE_RESULT(SJ_OD_array_iterator) +DEFINE_CLASS(SJ_OD_object_iterator) +DEFINE_RESULT(SJ_OD_object_iterator) +DEFINE_CLASS(SJ_OD_field) +DEFINE_RESULT(SJ_OD_field) + +DEFINE_PRIMITIVE_RESULT(uint64_t) +DEFINE_PRIMITIVE_RESULT(int64_t) +DEFINE_PRIMITIVE_RESULT(double) +DEFINE_PRIMITIVE_RESULT(bool) +DEFINE_PRIMITIVE_RESULT(size_t) + +SJ_padded_string *SJ_padded_string_new(const char *s, size_t len); +SJ_padded_string_result * +SJ_padded_string_load(const char *path); // null terminated string. + +SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity); +SJ_OD_document_result * +SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, + const SJ_padded_string *s); +SJ_OD_document_result * +SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, + size_t len, size_t allocated); + +SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc); +DEFINE_GET_WITH(SJ_OD_value, uint64_t, get_uint64) +DEFINE_GET_WITH(SJ_OD_value, int64_t, get_int64) +DEFINE_GET_WITH(SJ_OD_value, double, get_double) +DEFINE_GET_WITH(SJ_OD_value, bool, get_bool) +DEFINE_GET_WITH(SJ_OD_value, SJ_OD_array, get_array) +DEFINE_GET_WITH(SJ_OD_value, SJ_OD_object, get_object) +DEFINE_GET_WITH(SJ_OD_value, SJ_OD_raw_json_string, get_raw_json_string) + +DEFINE_GET_WITH(SJ_OD_document, uint64_t, get_uint64) +DEFINE_GET_WITH(SJ_OD_document, int64_t, get_int64) +DEFINE_GET_WITH(SJ_OD_document, double, get_double) +DEFINE_GET_WITH(SJ_OD_document, bool, get_bool) +DEFINE_GET_WITH(SJ_OD_document, SJ_OD_array, get_array) +DEFINE_GET_WITH(SJ_OD_document, SJ_OD_object, get_object) +DEFINE_GET_WITH(SJ_OD_document, SJ_OD_raw_json_string, get_raw_json_string) + +STD_string_view_result *SJ_OD_value_get_string(SJ_OD_value *value, + bool allow_replacement); +STD_string_view_result *SJ_OD_document_get_string(SJ_OD_document *doc); + +STD_string_view_result *SJ_OD_value_get_wobbly_string(SJ_OD_value *value); +STD_string_view_result *SJ_OD_document_get_wobbly_string(SJ_OD_document *doc); + +const char *STD_string_view_data(STD_string_view *sv); +size_t STD_string_view_size(STD_string_view *sv); + +DEFINE_GET_WITH(SJ_OD_array, size_t, count_elements) +DEFINE_GET_WITH(SJ_OD_array, bool, is_empty) +DEFINE_GET_WITH(SJ_OD_array, bool, reset) + +SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index); #ifdef __cplusplus } From 11020537f9f5395e65cfa06f6bff885094e154f0 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Wed, 23 Aug 2023 23:37:39 +0800 Subject: [PATCH 05/31] save --- simdjson-sys/src/simdjson_c_api.cpp | 20 ++--------- simdjson-sys/src/simdjson_c_api.h | 53 ++++++++++++++++------------- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index 9b539e7..26db745 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -68,19 +68,14 @@ IMPL_PRIMITIVE_RESULT(bool) IMPL_PRIMITIVE_RESULT(size_t) SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) { - // return reinterpret_cast(new simdjson::padded_string(s, - // len)); return object_to_pointer(padded_string(s, len)); } + SJ_padded_string_result *SJ_padded_string_load(const char *path) { - // return reinterpret_cast(new - // simdjson_result(padded_string::load(path))); return object_to_pointer(padded_string::load(path)); } SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity) { - // return reinterpret_cast(new - // ondemand::parser(max_capacity)); return object_to_pointer(ondemand::parser(max_capacity)); } @@ -121,6 +116,7 @@ IMPL_GET(SJ_OD_value, ondemand::value, int64_t, get_int64) IMPL_GET(SJ_OD_value, ondemand::value, double, get_double) IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_raw_json_string, get_raw_json_string) +IMPL_GET(SJ_OD_value, ondemand::value, STD_string_view, get_wobbly_string) IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_object, get_object) IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_array, get_array) @@ -129,6 +125,7 @@ IMPL_GET(SJ_OD_document, ondemand::document, int64_t, get_int64) IMPL_GET(SJ_OD_document, ondemand::document, double, get_double) IMPL_GET(SJ_OD_document, ondemand::document, SJ_OD_raw_json_string, get_raw_json_string) +IMPL_GET(SJ_OD_document, ondemand::document, STD_string_view, get_wobbly_string) STD_string_view_result *SJ_OD_value_get_string(SJ_OD_value *self, bool allow_replacement) { @@ -144,17 +141,6 @@ STD_string_view_result *SJ_OD_document_get_string(SJ_OD_document *self, return object_to_pointer(std::move(result)); } -STD_string_view_result *SJ_OD_value_get_wobbly_string(SJ_OD_value *self) { - auto result = reinterpret_cast(self)->get_wobbly_string(); - return object_to_pointer(std::move(result)); -} - -STD_string_view_result *SJ_OD_document_get_wobbly_string(SJ_OD_document *self) { - auto result = - reinterpret_cast(self)->get_wobbly_string(); - return object_to_pointer(std::move(result)); -} - const char *STD_string_view_data(STD_string_view *sv) { return reinterpret_cast(sv)->data(); } diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index bf749da..4faeedc 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -20,7 +20,7 @@ int name##_result_error(const name##_result *r); \ name name##_result_value(name##_result *r); -#define DEFINE_GET_WITH(self, value, method) \ +#define DEFINE_GET(self, value, method) \ value##_result *self##_##method(self *r); #ifdef __cplusplus @@ -56,10 +56,12 @@ DEFINE_PRIMITIVE_RESULT(double) DEFINE_PRIMITIVE_RESULT(bool) DEFINE_PRIMITIVE_RESULT(size_t) +// padded_string SJ_padded_string *SJ_padded_string_new(const char *s, size_t len); SJ_padded_string_result * SJ_padded_string_load(const char *path); // null terminated string. +// ondemand::parser SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity); SJ_OD_document_result * SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, @@ -68,37 +70,40 @@ SJ_OD_document_result * SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, size_t len, size_t allocated); +// ondemand::value +DEFINE_GET(SJ_OD_value, uint64_t, get_uint64) +DEFINE_GET(SJ_OD_value, int64_t, get_int64) +DEFINE_GET(SJ_OD_value, double, get_double) +DEFINE_GET(SJ_OD_value, bool, get_bool) +DEFINE_GET(SJ_OD_value, SJ_OD_array, get_array) +DEFINE_GET(SJ_OD_value, SJ_OD_object, get_object) +DEFINE_GET(SJ_OD_value, SJ_OD_raw_json_string, get_raw_json_string) +DEFINE_GET(SJ_OD_value, STD_string_view, get_wobbly_string) + +// ondemand::document SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc); -DEFINE_GET_WITH(SJ_OD_value, uint64_t, get_uint64) -DEFINE_GET_WITH(SJ_OD_value, int64_t, get_int64) -DEFINE_GET_WITH(SJ_OD_value, double, get_double) -DEFINE_GET_WITH(SJ_OD_value, bool, get_bool) -DEFINE_GET_WITH(SJ_OD_value, SJ_OD_array, get_array) -DEFINE_GET_WITH(SJ_OD_value, SJ_OD_object, get_object) -DEFINE_GET_WITH(SJ_OD_value, SJ_OD_raw_json_string, get_raw_json_string) - -DEFINE_GET_WITH(SJ_OD_document, uint64_t, get_uint64) -DEFINE_GET_WITH(SJ_OD_document, int64_t, get_int64) -DEFINE_GET_WITH(SJ_OD_document, double, get_double) -DEFINE_GET_WITH(SJ_OD_document, bool, get_bool) -DEFINE_GET_WITH(SJ_OD_document, SJ_OD_array, get_array) -DEFINE_GET_WITH(SJ_OD_document, SJ_OD_object, get_object) -DEFINE_GET_WITH(SJ_OD_document, SJ_OD_raw_json_string, get_raw_json_string) - +DEFINE_GET(SJ_OD_document, uint64_t, get_uint64) +DEFINE_GET(SJ_OD_document, int64_t, get_int64) +DEFINE_GET(SJ_OD_document, double, get_double) +DEFINE_GET(SJ_OD_document, bool, get_bool) +DEFINE_GET(SJ_OD_document, SJ_OD_array, get_array) +DEFINE_GET(SJ_OD_document, SJ_OD_object, get_object) +DEFINE_GET(SJ_OD_document, SJ_OD_raw_json_string, get_raw_json_string) +DEFINE_GET(SJ_OD_document, STD_string_view, get_wobbly_string) + +// get_string is special. STD_string_view_result *SJ_OD_value_get_string(SJ_OD_value *value, bool allow_replacement); STD_string_view_result *SJ_OD_document_get_string(SJ_OD_document *doc); -STD_string_view_result *SJ_OD_value_get_wobbly_string(SJ_OD_value *value); -STD_string_view_result *SJ_OD_document_get_wobbly_string(SJ_OD_document *doc); - +// std::string_view const char *STD_string_view_data(STD_string_view *sv); size_t STD_string_view_size(STD_string_view *sv); -DEFINE_GET_WITH(SJ_OD_array, size_t, count_elements) -DEFINE_GET_WITH(SJ_OD_array, bool, is_empty) -DEFINE_GET_WITH(SJ_OD_array, bool, reset) - +// ondemand::array +DEFINE_GET(SJ_OD_array, size_t, count_elements) +DEFINE_GET(SJ_OD_array, bool, is_empty) +DEFINE_GET(SJ_OD_array, bool, reset) SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index); #ifdef __cplusplus From 11227ed9cd09c189ba02f861312e8f788bed080d Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Wed, 23 Aug 2023 23:43:28 +0800 Subject: [PATCH 06/31] update --- Cargo.toml | 12 +-- build.rs | 22 +---- src/dom/array.rs | 116 ------------------------- src/dom/document_stream.rs | 50 ----------- src/dom/element.rs | 174 ------------------------------------- src/dom/mod.rs | 25 ------ src/dom/object.rs | 128 --------------------------- src/dom/parser.rs | 130 --------------------------- src/dom/utils.rs | 17 ---- src/lib.rs | 10 +-- 10 files changed, 9 insertions(+), 675 deletions(-) delete mode 100644 src/dom/array.rs delete mode 100644 src/dom/document_stream.rs delete mode 100644 src/dom/element.rs delete mode 100644 src/dom/mod.rs delete mode 100644 src/dom/object.rs delete mode 100644 src/dom/parser.rs delete mode 100644 src/dom/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 3a184df..0211403 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,34 +1,28 @@ [package] name = "simdjson-rust" -version = "0.2.0-alpha" +version = "0.3.0" authors = ["SunDoge <384813529@qq.com>"] edition = "2018" links = "simdjson-rust" license = "Apache-2.0" -# [dependencies] -# simdjson-sys = {path="simdjson-sys"} [workspace] members = ["simdjson-sys"] [dependencies] -cxx = "1.0.74" thiserror = "1.0" # anyhow = "1.0" +simdjson-sys = { path = "simdjson-sys" } # serde compatibilty serde = { version = "1", features = ["derive"], optional = true } serde_json = { version = "1", optional = true } -[build-dependencies] -cxx-build = "1.0.74" - [features] -default = ["serde_impl"] +default = [] # serde compatibility serde_impl = ["serde", "serde_json"] - diff --git a/build.rs b/build.rs index f24af33..f328e4d 100644 --- a/build.rs +++ b/build.rs @@ -1,21 +1 @@ -use cxx_build::CFG; -use std::env; -use std::path::Path; - -fn main() { - let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); - let simdjson_include_dir = Path::new(&manifest_dir).join("csrc").join("simdjson"); - CFG.exported_header_dirs.push(&simdjson_include_dir); - - cxx_build::bridge("src/libsimdjson.rs") // returns a cc::Build - .file("csrc/wrapper.cpp") - .file("csrc/simdjson/simdjson.cpp") - .flag_if_supported("-std=c++20") - .flag_if_supported("/std=c++latest") - .flag_if_supported("-pthread") - .flag_if_supported("-O3") - .compile("simdjson-sys"); - - println!("cargo:rerun-if-changed=csrc/wrapper.cpp"); - println!("cargo:rerun-if-changed=csrc/wrapper.h"); -} +fn main() {} diff --git a/src/dom/array.rs b/src/dom/array.rs deleted file mode 100644 index a36d420..0000000 --- a/src/dom/array.rs +++ /dev/null @@ -1,116 +0,0 @@ -use super::element::Element; -use super::parser::Parser; -use crate::error::{SimdJsonError, SimdJsonResult}; -use crate::libsimdjson::ffi; -use cxx::UniquePtr; -use std::fmt; -use std::marker::PhantomData; - -pub type ArrayIterPtr = UniquePtr; -pub type ArrayPtr = UniquePtr; - -pub struct Array<'a> { - ptr: ArrayPtr, - _phantom: PhantomData<&'a Parser>, -} - -impl<'a> Array<'a> { - pub fn new(ptr: ArrayPtr) -> Self { - Array { - ptr, - _phantom: PhantomData, - } - } - - pub fn at_pointer(&self, json_pointer: &str) -> SimdJsonResult { - let result = ffi::array_at_pointer(&self.ptr, json_pointer); - check_result!(result, Element) - } - - pub fn at(&self, index: usize) -> SimdJsonResult { - let result = ffi::array_at(&self.ptr, index); - check_result!(result, Element) - } - - pub fn minify(&self) -> String { - ffi::array_minify(&self.ptr) - } -} - -impl<'a> From for Array<'a> { - fn from(ptr: ArrayPtr) -> Self { - Array::new(ptr) - } -} - -pub struct ArrayIter<'a> { - ptr: ArrayIterPtr, - // _phantom: PhantomData<&'a Parser>, - #[allow(dead_code)] - array: &'a Array<'a>, -} - -impl<'a> ArrayIter<'a> { - pub fn new(array: &'a Array<'a>) -> Self { - let ptr = ffi::array_get_iterator(&array.ptr); - ArrayIter { - ptr, - // _phantom: PhantomData, - array, - } - } -} - -impl<'a> Iterator for ArrayIter<'a> { - type Item = Element<'a>; - - fn next(&mut self) -> Option { - let next_ptr = ffi::array_iterator_next(self.ptr.pin_mut()); - if next_ptr.is_null() { - None - } else { - Some(Element::from(next_ptr)) - } - } -} - -impl<'a> IntoIterator for &'a Array<'a> { - type Item = Element<'a>; - type IntoIter = ArrayIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - ArrayIter::new(self) - } -} - -#[cfg(test)] -mod tests { - - use crate::dom::parser::Parser; - // use super::element::GetValue; - - #[test] - fn array_iter() -> Result<(), Box> { - let mut parser = Parser::default(); - let elm = parser.parse("[true, true, true, true]")?; - let arr = elm.get_array()?; - - assert!(arr.at(3)?.get_bool()?); - - let mut c = 0; - for v in &arr { - c += 1; - println!("c={}, v={}", c, v.get_bool()?); - - assert!(v.get_bool()?); - } - - Ok(()) - } -} - -impl<'a> fmt::Display for Array<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.minify()) - } -} diff --git a/src/dom/document_stream.rs b/src/dom/document_stream.rs deleted file mode 100644 index 9470453..0000000 --- a/src/dom/document_stream.rs +++ /dev/null @@ -1,50 +0,0 @@ -use super::element::Element; -use super::parser::Parser; -use crate::error::{SimdJsonError, SimdJsonResult}; -use crate::libsimdjson::ffi; -use cxx::UniquePtr; -use std::marker::PhantomData; - -pub type DocumentStreamIterPtr = UniquePtr; -pub type DocumentStreamPtr = UniquePtr; - -pub struct DocumentStream<'a> { - ptr: DocumentStreamPtr, - _phantom: PhantomData<&'a Parser>, - iter: DocumentStreamIterPtr, -} - -impl<'a> DocumentStream<'a> { - pub fn new(ptr: DocumentStreamPtr) -> Self { - DocumentStream { - ptr, - _phantom: PhantomData, - iter: DocumentStreamIterPtr::null(), - } - } -} - -impl<'a> From for DocumentStream<'a> { - fn from(ptr: DocumentStreamPtr) -> Self { - DocumentStream::new(ptr) - } -} - -impl<'a> Iterator for DocumentStream<'a> { - type Item = SimdJsonResult>; - - fn next(&mut self) -> Option { - if self.iter.is_null() { - self.iter = ffi::document_stream_get_iterator(self.ptr.pin_mut()); - } else { - ffi::document_stream_iterator_next(self.iter.pin_mut()); - } - - let result = ffi::document_stream_iterator_deref(self.iter.pin_mut()); - if result.code == 0 && result.value.is_null() { - None - } else { - Some(check_result!(result, Element)) - } - } -} diff --git a/src/dom/element.rs b/src/dom/element.rs deleted file mode 100644 index 310c0d9..0000000 --- a/src/dom/element.rs +++ /dev/null @@ -1,174 +0,0 @@ -use super::array::Array; -use super::object::Object; -use super::parser::Parser; -use crate::error::SimdJsonError; -use crate::libsimdjson::ffi; -use cxx::UniquePtr; - -use std::fmt; -use std::marker::PhantomData; - -use std::str::{self}; - -pub type ElementPtr = UniquePtr; - -#[derive(Debug, PartialEq, Eq)] -pub enum ElementType { - ///< dom::array - Array, - ///< dom::object - Object, - ///< int64_t - Int64, - ///< uint64_t: any integer that fits in uint64_t but *not* int64_t - Uint64, - ///< double: Any number with a "." or "e" that fits in double. - Double, - ///< std::string_view - String, - ///< bool - Bool, - ///< null - NullValue, -} - -impl From for ElementType { - fn from(code: u8) -> Self { - match code as char { - '[' => Self::Array, - '{' => Self::Object, - 'l' => Self::Int64, - 'u' => Self::Uint64, - 'd' => Self::Double, - '"' => Self::String, - 't' => Self::Bool, - 'n' => Self::NullValue, - _ => unreachable!(), - } - } -} - -pub struct Element<'a> { - ptr: ElementPtr, - // ptr: &'a ffi::element, - _phantom: PhantomData<&'a Parser>, -} - -impl<'a> From for Element<'a> { - fn from(ptr: ElementPtr) -> Self { - Element::new(ptr) - } -} - -impl<'a> Element<'a> { - fn new(ptr: ElementPtr) -> Self { - Element { - ptr, - _phantom: PhantomData, - } - } - - pub fn at_pointer(&self, json_pointer: &str) -> Result { - let result = ffi::element_at_pointer(&self.ptr, json_pointer); - // if result.code < 2 { - // Ok(Element::from(result.value)) - // } else { - // Err(SimdJsonError::from(result.code)) - // } - - check_result!(result, Element) - } - - pub fn at_key(&self, key: &str) -> Result { - let result = ffi::element_at_key(&self.ptr, key); - check_result!(result, Element) - } - - pub fn at_index(&self, index: usize) -> Result { - let result = ffi::element_at_index(&self.ptr, index); - check_result!(result, Element) - } - - pub fn get_string(&self) -> Result { - let result = ffi::element_get_string(&self.ptr); - check_result!(result) - } - - pub fn get_bool(&self) -> Result { - let result = ffi::element_get_bool(&self.ptr); - check_result!(result) - } - - pub fn get_u64(&self) -> Result { - let result = ffi::element_get_u64(&self.ptr); - check_result!(result) - } - - pub fn get_i64(&self) -> Result { - let result = ffi::element_get_i64(&self.ptr); - check_result!(result) - } - - pub fn get_f64(&self) -> Result { - let result = ffi::element_get_f64(&self.ptr); - check_result!(result) - } - - pub fn get_object(&self) -> Result { - let result = ffi::element_get_object(&self.ptr); - check_result!(result, Object) - } - - pub fn get_array(&self) -> Result { - let result = ffi::element_get_array(&self.ptr); - check_result!(result, Array) - } - - pub fn get_type(&self) -> ElementType { - ElementType::from(ffi::element_get_type(&self.ptr)) - } - - pub fn is_null(&self) -> bool { - ffi::element_is_null(&self.ptr) - } - - pub fn minify(&self) -> String { - ffi::element_minify(&self.ptr) - } -} - -// pub trait GetValue { -// fn get(&self) -> Result; -// } - -// impl GetValue for Element { -// fn get(&self) -> Result { -// let result = ffi::element_get_bool(&self.ptr); -// check_result!(result) -// } -// } - -impl<'a> fmt::Display for Element<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.minify()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn element_at_pointer() { - let mut parser = Parser::default(); - let doc = parser.parse("{\"foo\": 1, \"bar\": 2}").unwrap(); - let elem = doc.at_pointer("/foo").unwrap(); - assert_eq!(elem.get_i64().unwrap(), 1); - let elem = doc.at_pointer("/bar").unwrap(); - assert_eq!(elem.get_i64().unwrap(), 2); - - let doc = parser.parse("{\"foo\": [-1, -2, -3]}").unwrap(); - let elem = doc.at_pointer("/foo/1").unwrap(); - assert_eq!(elem.get_i64().unwrap(), -2); - } -} diff --git a/src/dom/mod.rs b/src/dom/mod.rs deleted file mode 100644 index 6307e4f..0000000 --- a/src/dom/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[macro_use] -mod utils; - -pub mod array; -pub mod document_stream; -pub mod element; -pub mod object; -pub mod parser; - -pub use self::parser::Parser; - -#[cfg(test)] -mod tests { - use super::*; - // use super::element::GetValue; - - #[test] - fn it_works() { - let mut parser = parser::Parser::default(); - let elm = parser.parse("true").unwrap(); - let value: bool = elm.get_bool().unwrap(); - assert!(value); - assert_eq!(elm.get_type(), element::ElementType::Bool); - } -} diff --git a/src/dom/object.rs b/src/dom/object.rs deleted file mode 100644 index 3756744..0000000 --- a/src/dom/object.rs +++ /dev/null @@ -1,128 +0,0 @@ -use super::element::Element; -use super::parser::Parser; -use crate::error::SimdJsonError; -use crate::libsimdjson::ffi; -use cxx::UniquePtr; -use std::fmt; -use std::marker::PhantomData; - -pub type ObjectIterPtr = UniquePtr; -pub type ObjectPtr = UniquePtr; - -pub struct Object<'a> { - ptr: ObjectPtr, - _phantom: PhantomData<&'a Parser>, -} - -impl<'a> Object<'a> { - pub fn new(ptr: ObjectPtr) -> Self { - Object { - ptr, - _phantom: PhantomData, - } - } - - pub fn at_pointer(&self, json_pointer: &str) -> Result { - let result = ffi::object_at_pointer(&self.ptr, json_pointer); - // if result.code < 2 { - // Ok(Element::from(result.value)) - // } else { - // Err(SimdJsonError::from(result.code)) - // } - - check_result!(result, Element) - } - - pub fn at_key(&self, key: &str) -> Result { - let result = ffi::object_at_key(&self.ptr, key); - check_result!(result, Element) - } - - pub fn at_key_case_insensitive(&self, key: &str) -> Result { - let result = ffi::object_at_key_case_insensitive(&self.ptr, key); - check_result!(result, Element) - } - - pub fn minify(&self) -> String { - ffi::object_minify(&self.ptr) - } -} - -impl<'a> From for Object<'a> { - fn from(ptr: ObjectPtr) -> Self { - Object::new(ptr) - } -} - -pub struct ObjectIter<'a> { - pub ptr: ObjectIterPtr, - #[allow(dead_code)] - object: &'a Object<'a>, -} - -impl<'a> ObjectIter<'a> { - pub fn new(object: &'a Object<'a>) -> Self { - let ptr = ffi::object_get_iterator(&object.ptr); - ObjectIter { ptr, object } - } - - pub fn has_next(&self) -> bool { - ffi::object_iterator_has_next(&self.ptr) - } - - pub fn key(&self) -> String { - ffi::object_iterator_key(&self.ptr) - } - - pub fn value(&self) -> Element<'a> { - ffi::object_iterator_value(&self.ptr).into() - } -} - -impl<'a> Iterator for ObjectIter<'a> { - type Item = (String, Element<'a>); - - fn next(&mut self) -> Option { - ffi::object_iterator_next(self.ptr.pin_mut()); - if self.has_next() { - None - } else { - Some((self.key(), self.value())) - } - } -} - -impl<'a> IntoIterator for &'a Object<'a> { - type Item = (String, Element<'a>); - type IntoIter = ObjectIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - ObjectIter::new(self) - } -} - -#[cfg(test)] -mod tests { - - use crate::dom::parser::Parser; - // use super::element::GetValue; - - #[test] - fn object_iter() -> Result<(), Box> { - let mut parser = Parser::default(); - let elm = parser.parse(r#"{"a": true, "b": true}"#)?; - let obj = elm.get_object()?; - - for (k, v) in &obj { - println!("k={}, v={}", k, v.get_bool()?); - } - - Ok(()) - } -} - -impl<'a> fmt::Display for Object<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.minify()) - } -} diff --git a/src/dom/parser.rs b/src/dom/parser.rs deleted file mode 100644 index 5bc22cb..0000000 --- a/src/dom/parser.rs +++ /dev/null @@ -1,130 +0,0 @@ -use super::document_stream::DocumentStream; -use super::element::Element; -use crate::error::SimdJsonError; -use crate::libsimdjson::{ffi, DEFAULT_BATCH_SIZE, SIMDJSON_MAXSIZE_BYTES}; -use crate::padded_string::PaddedString; -use cxx::UniquePtr; - -use std::path::Path; - -pub struct Parser { - ptr: UniquePtr, -} - -impl Parser { - pub fn new(max_capacity: usize) -> Parser { - let parser = ffi::parser_new(max_capacity); - Parser { ptr: parser } - } - - pub fn load>(&mut self, path: P) -> Result { - let result = ffi::parser_load(self.ptr.pin_mut(), path.as_ref().to_str().unwrap()); - check_result!(result, Element) - } - - pub fn parse(&mut self, s: &str) -> Result { - let result = ffi::parser_parse(self.ptr.pin_mut(), s); - check_result!(result, Element) - } - - pub fn parse_padded(&mut self, s: &PaddedString) -> Result { - let result = ffi::parser_parse_padded(self.ptr.pin_mut(), s.as_ptr()); - check_result!(result, Element) - } - - pub fn load_many>( - &mut self, - path: P, - batch_size: usize, - ) -> Result { - let stream = ffi::parser_load_many( - self.ptr.pin_mut(), - path.as_ref() - .to_str() - .ok_or(SimdJsonError::InvalidUriFragment)?, - batch_size, - ); - check_result!(stream, DocumentStream) - } - - pub fn load_many_default>( - &mut self, - path: P, - ) -> Result { - self.load_many(path, DEFAULT_BATCH_SIZE) - } - - pub fn parse_many( - &mut self, - s: &str, - batch_size: usize, - ) -> Result { - let stream = ffi::parser_parse_many(self.ptr.pin_mut(), s, batch_size); - check_result!(stream, DocumentStream) - } - - pub fn parse_many_default(&mut self, s: &str) -> Result { - self.parse_many(s, DEFAULT_BATCH_SIZE) - } - - pub fn parse_many_padded( - &mut self, - s: &PaddedString, - batch_size: usize, - ) -> Result { - let stream = ffi::parser_parse_many_padded(self.ptr.pin_mut(), s.as_ptr(), batch_size); - check_result!(stream, DocumentStream) - } - - pub fn parse_many_padded_default( - &mut self, - s: &PaddedString, - ) -> Result { - self.parse_many_padded(s, DEFAULT_BATCH_SIZE) - } -} - -impl Default for Parser { - fn default() -> Self { - let parser = ffi::parser_new(SIMDJSON_MAXSIZE_BYTES); - Parser { ptr: parser } - } -} - -#[cfg(test)] -mod tests { - use super::*; - // use super::element::GetValue; - - #[test] - fn parse_padded_string() { - let mut parser = Parser::default(); - let value: bool = parser - .parse_padded(&"true".into()) - .unwrap() - .get_bool() - .unwrap(); - assert!(value); - } - - #[test] - fn parse_parse_many() { - let mut parser = Parser::default(); - let input = "22\n33\n\"hello world\"\n\n\"goodbye world\"\n\n\n[1, 2, 3]\n{\"a\": -0.5}"; - let mut docs = parser.parse_many_default(input).unwrap(); - assert_eq!(docs.next().unwrap().unwrap().get_u64().unwrap(), 22); - assert_eq!( - docs.nth(1).unwrap().unwrap().get_string().unwrap(), - "hello world" - ); - } - - #[test] - fn borrow_checker() { - let mut parser = Parser::default(); - let elm = parser.parse_padded(&"true".into()).unwrap(); - assert!(elm.get_bool().unwrap()); - let new_elm = parser.parse("false").unwrap(); - assert!(!new_elm.get_bool().unwrap()); - } -} diff --git a/src/dom/utils.rs b/src/dom/utils.rs deleted file mode 100644 index 73e3225..0000000 --- a/src/dom/utils.rs +++ /dev/null @@ -1,17 +0,0 @@ -macro_rules! check_result { - ($result: ident) => { - if $result.code < 2 { - Ok($result.value) - } else { - Err(SimdJsonError::from($result.code)) - } - }; - ($result: ident, $element_type: ident) => { - if $result.code < 2 { - // Ok($element_type::from(&$result.value)) - Ok($element_type::from($result.value)) - } else { - Err(SimdJsonError::from($result.code)) - } - }; -} diff --git a/src/lib.rs b/src/lib.rs index 235db73..f141fe6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,11 +9,11 @@ // pub use parsed_json::{ParsedJson, DEFUALT_MAX_DEPTH}; // pub use parsed_json_iterator::ParsedJsonIterator; -pub mod dom; -pub mod error; -pub mod libsimdjson; -pub mod padded_string; -pub mod serde; +// pub mod dom; +// pub mod error; +// pub mod libsimdjson; +// pub mod padded_string; +// pub mod serde; #[cfg(test)] mod tests { From edf8389330983564891d708f4314666bab6d961d Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Wed, 23 Aug 2023 23:54:25 +0800 Subject: [PATCH 07/31] fix cargo test --- examples/quickstart.rs | 22 ++++----- src/error.rs | 101 +++++++++++++++++++++++------------------ src/lib.rs | 2 +- tests/basic_tests.rs | 72 ++++++++++++++--------------- 4 files changed, 105 insertions(+), 92 deletions(-) diff --git a/examples/quickstart.rs b/examples/quickstart.rs index fc4ea11..169e670 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -1,14 +1,14 @@ -use simdjson_rust::dom; +use simdjson_rust::error::Result; -fn main() -> Result<(), Box> { - let mut parser = dom::Parser::default(); - let tweets = parser.load("json-examples/twitter.json")?; - println!( - "{} results.", - tweets - .at_key("search_metadata")? - .at_key("count")? - .get_u64()? - ); +fn main() -> Result<()> { + // let mut parser = dom::Parser::default(); + // let tweets = parser.load("json-examples/twitter.json")?; + // println!( + // "{} results.", + // tweets + // .at_key("search_metadata")? + // .at_key("count")? + // .get_u64()? + // ); Ok(()) } diff --git a/src/error.rs b/src/error.rs index 96bfb15..d234102 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,15 +1,9 @@ -use serde::de; -use std::fmt::Display; - use thiserror::Error; -pub type SimdJsonResult = Result; +pub type Result = std::result::Result; #[derive(Debug, Error)] pub enum SimdJsonError { - #[error("Message: {0}")] - Message(String), - #[error("This parser can't support a document that big")] Capacity, @@ -76,50 +70,69 @@ pub enum SimdJsonError { #[error("Invalid URI fragment syntax.")] InvalidUriFragment, - #[error( - "Unexpected error, consider reporting this problem as you may have found a bug in simdjson" - )] + #[error("todo")] UnexpectedError, + + #[error("todo")] + ParserInUse, + + #[error("todo")] + OutOfOrderIteration, + + #[error("todo")] + InsufficientPadding, + + #[error("todo")] + IncompleteArrayOrObject, + + #[error("todo")] + ScalarDocumentAsValue, + + #[error("todo")] + OutOfBounds, + + #[error("todo")] + TailingContent, + + #[error("todo")] + NumErrorCodes, } impl From for SimdJsonError { fn from(error_code: i32) -> Self { match error_code { - 0 => panic!("No error"), - 1 => panic!("No error and buffer still has more data"), - 2 => SimdJsonError::Capacity, - 3 => SimdJsonError::MemAlloc, - 4 => SimdJsonError::TapeError, - 5 => SimdJsonError::DepthError, - 6 => SimdJsonError::StringError, - 7 => SimdJsonError::TAtomError, - 8 => SimdJsonError::FAtomError, - 9 => SimdJsonError::NAtomError, - 10 => SimdJsonError::NumberError, - 11 => SimdJsonError::Utf8Error, - 12 => SimdJsonError::Uninitialized, - 13 => SimdJsonError::Empty, - 14 => SimdJsonError::UnescapedChars, - 15 => SimdJsonError::UnclosedString, - 16 => SimdJsonError::UnsupportedArchitecture, - 17 => SimdJsonError::IncorrectType, - 18 => SimdJsonError::NumberOutOfRange, - 19 => SimdJsonError::IndexOutOfBounds, - 20 => SimdJsonError::NoSuchField, - 21 => SimdJsonError::IoError, - 22 => SimdJsonError::InvalidJsonPointer, - 23 => SimdJsonError::InvalidUriFragment, - 24 => SimdJsonError::UnexpectedError, + 1 => SimdJsonError::Capacity, + 2 => SimdJsonError::MemAlloc, + 3 => SimdJsonError::TapeError, + 4 => SimdJsonError::DepthError, + 5 => SimdJsonError::StringError, + 6 => SimdJsonError::TAtomError, + 7 => SimdJsonError::FAtomError, + 8 => SimdJsonError::NAtomError, + 9 => SimdJsonError::NumberError, + 10 => SimdJsonError::Utf8Error, + 11 => SimdJsonError::Uninitialized, + 12 => SimdJsonError::Empty, + 13 => SimdJsonError::UnescapedChars, + 14 => SimdJsonError::UnclosedString, + 15 => SimdJsonError::UnsupportedArchitecture, + 16 => SimdJsonError::IncorrectType, + 17 => SimdJsonError::NumberOutOfRange, + 18 => SimdJsonError::IndexOutOfBounds, + 19 => SimdJsonError::NoSuchField, + 20 => SimdJsonError::IoError, + 21 => SimdJsonError::InvalidJsonPointer, + 22 => SimdJsonError::InvalidUriFragment, + 23 => SimdJsonError::UnexpectedError, + 24 => SimdJsonError::ParserInUse, + 25 => SimdJsonError::OutOfOrderIteration, + 26 => SimdJsonError::InsufficientPadding, + 27 => SimdJsonError::IncompleteArrayOrObject, + 28 => SimdJsonError::ScalarDocumentAsValue, + 29 => SimdJsonError::OutOfBounds, + 30 => SimdJsonError::TailingContent, + 31 => SimdJsonError::NumErrorCodes, x => panic!("Unknown error code: {}", x), } } } - -impl de::Error for SimdJsonError { - fn custom(msg: T) -> Self - where - T: Display, - { - Self::Message(msg.to_string()) - } -} diff --git a/src/lib.rs b/src/lib.rs index f141fe6..4044a89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ // pub use parsed_json_iterator::ParsedJsonIterator; // pub mod dom; -// pub mod error; +pub mod error; // pub mod libsimdjson; // pub mod padded_string; // pub mod serde; diff --git a/tests/basic_tests.rs b/tests/basic_tests.rs index 401f837..0d729ef 100644 --- a/tests/basic_tests.rs +++ b/tests/basic_tests.rs @@ -1,43 +1,43 @@ -#[cfg(test)] -mod number_tests { +// #[cfg(test)] +// mod number_tests { - use simdjson_rust::dom; - use simdjson_rust::error::SimdJsonError; +// use simdjson_rust::dom; +// use simdjson_rust::error::SimdJsonError; - // ulp distance - // Marc B. Reynolds, 2016-2019 - // Public Domain under http://unlicense.org, see link for details. - // adapted by D. Lemire - #[allow(dead_code)] - fn f64_ulp_dist(a: f64, b: f64) -> u64 { - // let ua: u64 = transmute(a); - // let ub: u64 = transmute(b); - let (ua, ub): (u64, u64) = (a.to_bits(), b.to_bits()); +// // ulp distance +// // Marc B. Reynolds, 2016-2019 +// // Public Domain under http://unlicense.org, see link for details. +// // adapted by D. Lemire +// #[allow(dead_code)] +// fn f64_ulp_dist(a: f64, b: f64) -> u64 { +// // let ua: u64 = transmute(a); +// // let ub: u64 = transmute(b); +// let (ua, ub): (u64, u64) = (a.to_bits(), b.to_bits()); - if (ub ^ ua) as i64 >= 0 { - if (ua - ub) as i64 >= 0 { - ua - ub - } else { - ub - ua - } - } else { - ua + ub + 0x80000000 - } - } +// if (ub ^ ua) as i64 >= 0 { +// if (ua - ub) as i64 >= 0 { +// ua - ub +// } else { +// ub - ua +// } +// } else { +// ua + ub + 0x80000000 +// } +// } - #[test] - fn small_integers() -> Result<(), SimdJsonError> { - let mut parser = dom::parser::Parser::default(); +// #[test] +// fn small_integers() -> Result<(), SimdJsonError> { +// let mut parser = dom::parser::Parser::default(); - for _m in 10..20 { - for i in -1024..1024 { - let s = i.to_string(); - let actual = parser.parse(&s)?.get_i64()?; +// for _m in 10..20 { +// for i in -1024..1024 { +// let s = i.to_string(); +// let actual = parser.parse(&s)?.get_i64()?; - assert_eq!(actual, i); - } - } +// assert_eq!(actual, i); +// } +// } - Ok(()) - } -} +// Ok(()) +// } +// } From 95da09ebe8393314de0e42b7f0ea3c804620ccbb Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 00:01:50 +0800 Subject: [PATCH 08/31] add macros --- Cargo.toml | 2 +- src/lib.rs | 5 ++++- src/macros.rs | 13 +++++++++++++ src/padded_string.rs | 25 +++++++------------------ 4 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 src/macros.rs diff --git a/Cargo.toml b/Cargo.toml index 0211403..ed479f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "simdjson-rust" version = "0.3.0" authors = ["SunDoge <384813529@qq.com>"] -edition = "2018" +edition = "2021" links = "simdjson-rust" license = "Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index 4044a89..473b147 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,10 +9,13 @@ // pub use parsed_json::{ParsedJson, DEFUALT_MAX_DEPTH}; // pub use parsed_json_iterator::ParsedJsonIterator; +mod macros; + // pub mod dom; pub mod error; + // pub mod libsimdjson; -// pub mod padded_string; +pub mod padded_string; // pub mod serde; #[cfg(test)] diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..7a532c8 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,13 @@ +macro_rules! impl_drop { + ($name:ty, $free_fn:expr) => { + impl Drop for $name { + fn drop(&mut self) { + unsafe { + $free_fn(self.ptr.as_ptr()); + } + } + } + }; +} + +pub(crate) use impl_drop; diff --git a/src/padded_string.rs b/src/padded_string.rs index 0aaf529..156c96e 100644 --- a/src/padded_string.rs +++ b/src/padded_string.rs @@ -1,24 +1,13 @@ -use super::libsimdjson::ffi; +use std::ptr::NonNull; -pub type PaddedStringPtr = cxx::UniquePtr; +use simdjson_sys as ffi; + +use crate::macros::impl_drop; pub struct PaddedString { - ptr: PaddedStringPtr, + ptr: NonNull, } -impl PaddedString { - pub fn new(ptr: PaddedStringPtr) -> Self { - PaddedString { ptr } - } - - pub fn as_ptr(&self) -> &PaddedStringPtr { - &self.ptr - } -} +impl PaddedString {} -impl From<&str> for PaddedString { - fn from(s: &str) -> Self { - let ptr = ffi::padded_string_from_string(s); - PaddedString { ptr } - } -} +impl_drop!(PaddedString, ffi::SJ_padded_string_free); From f9bdac1c450cd6bbbd2baca5394f5fbf64fa11ee Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 00:10:09 +0800 Subject: [PATCH 09/31] add example --- examples/quickstart.rs | 5 ++++- src/macros.rs | 15 +++++++++++++++ src/padded_string.rs | 19 +++++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 169e670..8922c49 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -1,4 +1,4 @@ -use simdjson_rust::error::Result; +use simdjson_rust::{error::Result, padded_string::PaddedString}; fn main() -> Result<()> { // let mut parser = dom::Parser::default(); @@ -10,5 +10,8 @@ fn main() -> Result<()> { // .at_key("count")? // .get_u64()? // ); + + let ps = PaddedString::load("json-examples/twitter.json")?; + Ok(()) } diff --git a/src/macros.rs b/src/macros.rs index 7a532c8..b7bee05 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -10,4 +10,19 @@ macro_rules! impl_drop { }; } +macro_rules! check_result { + ($func_call:expr, $get_code:expr, $get_value:expr) => { + unsafe { + let ptr = $func_call; + let code = $get_code(ptr); + if code == 0 { + Ok($get_value(ptr)) + } else { + Err(crate::error::SimdJsonError::from(code)) + } + } + }; +} + +pub(crate) use check_result; pub(crate) use impl_drop; diff --git a/src/padded_string.rs b/src/padded_string.rs index 156c96e..687b5e7 100644 --- a/src/padded_string.rs +++ b/src/padded_string.rs @@ -2,12 +2,27 @@ use std::ptr::NonNull; use simdjson_sys as ffi; -use crate::macros::impl_drop; +use crate::{ + error::Result, + macros::{check_result, impl_drop}, +}; pub struct PaddedString { ptr: NonNull, } -impl PaddedString {} +impl PaddedString { + pub fn load(path: &str) -> Result { + let c_path = std::ffi::CString::new(path).unwrap(); + check_result!( + ffi::SJ_padded_string_load(c_path.as_ptr()), + ffi::SJ_padded_string_result_error, + ffi::SJ_padded_string_result_value + ) + .map(|ptr| PaddedString { + ptr: NonNull::new(ptr).unwrap(), + }) + } +} impl_drop!(PaddedString, ffi::SJ_padded_string_free); From b23063a0a31e98cc9c2db45632137588ad055dc3 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 00:30:12 +0800 Subject: [PATCH 10/31] use value_unsafe --- simdjson-sys/src/simdjson_c_api.cpp | 4 ++-- simdjson-sys/src/simdjson_c_api.h | 4 ++-- src/padded_string.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index 26db745..20e77b4 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -23,7 +23,7 @@ template U *object_to_pointer(T &&t) { auto code = reinterpret_cast *>(r)->error(); \ return static_cast(code); \ } \ - name *name##_result_value(name##_result *r) { \ + name *name##_result_value_unsafe(name##_result *r) { \ auto result = reinterpret_cast *>(r); \ return object_to_pointer(std::move(*result).value_unsafe()); \ } @@ -34,7 +34,7 @@ template U *object_to_pointer(T &&t) { auto code = reinterpret_cast *>(r)->error(); \ return static_cast(code); \ } \ - name name##_result_value(name##_result *r) { \ + name name##_result_value_unsafe(name##_result *r) { \ auto result = reinterpret_cast *>(r); \ return std::move(*result).value_unsafe(); \ } diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index 4faeedc..44c6da5 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -12,13 +12,13 @@ #define DEFINE_RESULT(name) \ DEFINE_CLASS(name##_result) \ int name##_result_error(const name##_result *r); \ - name *name##_result_value(name##_result *r); + name *name##_result_value_unsafe(name##_result *r); // `value` method will free simdjson_result. #define DEFINE_PRIMITIVE_RESULT(name) \ DEFINE_CLASS(name##_result) \ int name##_result_error(const name##_result *r); \ - name name##_result_value(name##_result *r); + name name##_result_value_unsafe(name##_result *r); #define DEFINE_GET(self, value, method) \ value##_result *self##_##method(self *r); diff --git a/src/padded_string.rs b/src/padded_string.rs index 687b5e7..600c01d 100644 --- a/src/padded_string.rs +++ b/src/padded_string.rs @@ -17,7 +17,7 @@ impl PaddedString { check_result!( ffi::SJ_padded_string_load(c_path.as_ptr()), ffi::SJ_padded_string_result_error, - ffi::SJ_padded_string_result_value + ffi::SJ_padded_string_result_value_unsafe ) .map(|ptr| PaddedString { ptr: NonNull::new(ptr).unwrap(), From aa2177b9520e29dfefe5f26e783906e9f1f0c296 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 10:01:05 +0800 Subject: [PATCH 11/31] save --- simdjson-sys/src/simdjson_c_api.cpp | 14 ++++++--- simdjson-sys/src/simdjson_c_api.h | 2 ++ src/macros.rs | 6 ++-- src/padded_string.rs | 47 ++++++++++++++++++++++++----- 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index 20e77b4..ddde148 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -23,7 +23,7 @@ template U *object_to_pointer(T &&t) { auto code = reinterpret_cast *>(r)->error(); \ return static_cast(code); \ } \ - name *name##_result_value_unsafe(name##_result *r) { \ + name *name##_result_value_unsafe(name##_result *r) { \ auto result = reinterpret_cast *>(r); \ return object_to_pointer(std::move(*result).value_unsafe()); \ } @@ -34,7 +34,7 @@ template U *object_to_pointer(T &&t) { auto code = reinterpret_cast *>(r)->error(); \ return static_cast(code); \ } \ - name name##_result_value_unsafe(name##_result *r) { \ + name name##_result_value_unsafe(name##_result *r) { \ auto result = reinterpret_cast *>(r); \ return std::move(*result).value_unsafe(); \ } @@ -74,6 +74,12 @@ SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) { SJ_padded_string_result *SJ_padded_string_load(const char *path) { return object_to_pointer(padded_string::load(path)); } +size_t SJ_padded_string_length(const SJ_padded_string *ps) { + return reinterpret_cast(ps)->length(); +} +const uint8_t *SJ_padded_string_u8data(const SJ_padded_string *ps) { + return reinterpret_cast(ps)->u8data(); +} SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity) { return object_to_pointer(ondemand::parser(max_capacity)); @@ -92,8 +98,8 @@ SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, SJ_OD_document_result * SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, size_t len, size_t allocated) { - auto doc = reinterpret_cast(parser)->iterate( - padded_string_view(json, len, allocated)); + auto doc = reinterpret_cast(parser)->iterate(json, len, + allocated); return object_to_pointer(std::move(doc)); } diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index 44c6da5..5140675 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -60,6 +60,8 @@ DEFINE_PRIMITIVE_RESULT(size_t) SJ_padded_string *SJ_padded_string_new(const char *s, size_t len); SJ_padded_string_result * SJ_padded_string_load(const char *path); // null terminated string. +size_t SJ_padded_string_length(const SJ_padded_string *ps); +const uint8_t *SJ_padded_string_u8data(const SJ_padded_string *ps); // ondemand::parser SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity); diff --git a/src/macros.rs b/src/macros.rs index b7bee05..0da7941 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -10,13 +10,13 @@ macro_rules! impl_drop { }; } -macro_rules! check_result { +macro_rules! map_result { ($func_call:expr, $get_code:expr, $get_value:expr) => { unsafe { let ptr = $func_call; let code = $get_code(ptr); if code == 0 { - Ok($get_value(ptr)) + Ok(std::ptr::NonNull::new_unchecked($get_value(ptr))) } else { Err(crate::error::SimdJsonError::from(code)) } @@ -24,5 +24,5 @@ macro_rules! check_result { }; } -pub(crate) use check_result; pub(crate) use impl_drop; +pub(crate) use map_result; diff --git a/src/padded_string.rs b/src/padded_string.rs index 600c01d..db7b542 100644 --- a/src/padded_string.rs +++ b/src/padded_string.rs @@ -1,10 +1,10 @@ -use std::ptr::NonNull; +use std::{os::unix::prelude::OsStrExt, path::Path, ptr::NonNull}; use simdjson_sys as ffi; use crate::{ error::Result, - macros::{check_result, impl_drop}, + macros::{impl_drop, map_result}, }; pub struct PaddedString { @@ -12,17 +12,48 @@ pub struct PaddedString { } impl PaddedString { - pub fn load(path: &str) -> Result { - let c_path = std::ffi::CString::new(path).unwrap(); - check_result!( + pub fn new(s: &str) -> Self { + let ptr = unsafe { + let ptr = ffi::SJ_padded_string_new(s.as_ptr().cast(), s.len()); + NonNull::new_unchecked(ptr) + }; + Self { ptr } + } + + pub fn load>(path: P) -> Result { + let bytes = path.as_ref().as_os_str().as_bytes(); + let c_path = std::ffi::CString::new(bytes).unwrap(); + map_result!( ffi::SJ_padded_string_load(c_path.as_ptr()), ffi::SJ_padded_string_result_error, ffi::SJ_padded_string_result_value_unsafe ) - .map(|ptr| PaddedString { - ptr: NonNull::new(ptr).unwrap(), - }) + .map(|ptr| PaddedString { ptr }) + } + + pub fn len(&self) -> usize { + unsafe { ffi::SJ_padded_string_length(self.ptr.as_ref()) } + } + + pub fn as_str(&self) -> &str { + unsafe { + let data = ffi::SJ_padded_string_u8data(self.ptr.as_ref()); + let s = std::slice::from_raw_parts(data, self.len()); + std::str::from_utf8_unchecked(s) + } } } impl_drop!(PaddedString, ffi::SJ_padded_string_free); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let s = "{}"; + let ps = PaddedString::new(s); + assert_eq!(s, ps.as_str()); + } +} From 876148c8d401c3dfaa0bfe5db0c54514a4f809ce Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 11:27:49 +0800 Subject: [PATCH 12/31] update --- csrc/simdjson/simdjson.cpp | 15925 ------------------ csrc/simdjson/simdjson.h | 31436 ----------------------------------- csrc/wrapper.cpp | 431 - csrc/wrapper.h | 116 - src/constants.rs | 2 + src/lib.rs | 3 +- src/macros.rs | 20 + src/ondemand/array.rs | 24 + src/ondemand/document.rs | 90 + src/ondemand/field.rs | 0 src/ondemand/mod.rs | 6 + src/ondemand/object.rs | 24 + src/ondemand/parser.rs | 66 + src/ondemand/value.rs | 24 + src/padded_string.rs | 7 + 15 files changed, 265 insertions(+), 47909 deletions(-) delete mode 100644 csrc/simdjson/simdjson.cpp delete mode 100644 csrc/simdjson/simdjson.h delete mode 100644 csrc/wrapper.cpp delete mode 100644 csrc/wrapper.h create mode 100644 src/constants.rs create mode 100644 src/ondemand/array.rs create mode 100644 src/ondemand/document.rs create mode 100644 src/ondemand/field.rs create mode 100644 src/ondemand/mod.rs create mode 100644 src/ondemand/object.rs create mode 100644 src/ondemand/parser.rs create mode 100644 src/ondemand/value.rs diff --git a/csrc/simdjson/simdjson.cpp b/csrc/simdjson/simdjson.cpp deleted file mode 100644 index 963f712..0000000 --- a/csrc/simdjson/simdjson.cpp +++ /dev/null @@ -1,15925 +0,0 @@ -/* auto-generated on 2022-07-28 21:45:54 -0400. Do not edit! */ -/* begin file src/simdjson.cpp */ -#include "simdjson.h" - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_UNDESIRED_WARNINGS - -/* begin file src/to_chars.cpp */ -#include -#include -#include -#include - -namespace simdjson { -namespace internal { -/*! -implements the Grisu2 algorithm for binary to decimal floating-point -conversion. -Adapted from JSON for Modern C++ - -This implementation is a slightly modified version of the reference -implementation which may be obtained from -http://florian.loitsch.com/publications (bench.tar.gz). -The code is distributed under the MIT license, Copyright (c) 2009 Florian -Loitsch. For a detailed description of the algorithm see: [1] Loitsch, "Printing -Floating-Point Numbers Quickly and Accurately with Integers", Proceedings of the -ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, -PLDI 2010 [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and -Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming -Language Design and Implementation, PLDI 1996 -*/ -namespace dtoa_impl { - -template -Target reinterpret_bits(const Source source) { - static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); - - Target target; - std::memcpy(&target, &source, sizeof(Source)); - return target; -} - -struct diyfp // f * 2^e -{ - static constexpr int kPrecision = 64; // = q - - std::uint64_t f = 0; - int e = 0; - - constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} - - /*! - @brief returns x - y - @pre x.e == y.e and x.f >= y.f - */ - static diyfp sub(const diyfp &x, const diyfp &y) noexcept { - - return {x.f - y.f, x.e}; - } - - /*! - @brief returns x * y - @note The result is rounded. (Only the upper q bits are returned.) - */ - static diyfp mul(const diyfp &x, const diyfp &y) noexcept { - static_assert(kPrecision == 64, "internal error"); - - // Computes: - // f = round((x.f * y.f) / 2^q) - // e = x.e + y.e + q - - // Emulate the 64-bit * 64-bit multiplication: - // - // p = u * v - // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) - // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + - // 2^64 (u_hi v_hi ) = (p0 ) + 2^32 ((p1 ) + (p2 )) - // + 2^64 (p3 ) = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + - // 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) = - // (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + - // p2_hi + p3) = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) = (p0_lo ) + - // 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) - // - // (Since Q might be larger than 2^32 - 1) - // - // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) - // - // (Q_hi + H does not overflow a 64-bit int) - // - // = p_lo + 2^64 p_hi - - const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; - const std::uint64_t u_hi = x.f >> 32u; - const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; - const std::uint64_t v_hi = y.f >> 32u; - - const std::uint64_t p0 = u_lo * v_lo; - const std::uint64_t p1 = u_lo * v_hi; - const std::uint64_t p2 = u_hi * v_lo; - const std::uint64_t p3 = u_hi * v_hi; - - const std::uint64_t p0_hi = p0 >> 32u; - const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; - const std::uint64_t p1_hi = p1 >> 32u; - const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; - const std::uint64_t p2_hi = p2 >> 32u; - - std::uint64_t Q = p0_hi + p1_lo + p2_lo; - - // The full product might now be computed as - // - // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) - // p_lo = p0_lo + (Q << 32) - // - // But in this particular case here, the full p_lo is not required. - // Effectively we only need to add the highest bit in p_lo to p_hi (and - // Q_hi + 1 does not overflow). - - Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up - - const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); - - return {h, x.e + y.e + 64}; - } - - /*! - @brief normalize x such that the significand is >= 2^(q-1) - @pre x.f != 0 - */ - static diyfp normalize(diyfp x) noexcept { - - while ((x.f >> 63u) == 0) { - x.f <<= 1u; - x.e--; - } - - return x; - } - - /*! - @brief normalize x such that the result has the exponent E - @pre e >= x.e and the upper e - x.e bits of x.f must be zero. - */ - static diyfp normalize_to(const diyfp &x, - const int target_exponent) noexcept { - const int delta = x.e - target_exponent; - - return {x.f << delta, target_exponent}; - } -}; - -struct boundaries { - diyfp w; - diyfp minus; - diyfp plus; -}; - -/*! -Compute the (normalized) diyfp representing the input number 'value' and its -boundaries. -@pre value must be finite and positive -*/ -template boundaries compute_boundaries(FloatType value) { - - // Convert the IEEE representation into a diyfp. - // - // If v is denormal: - // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) - // If v is normalized: - // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) - - static_assert(std::numeric_limits::is_iec559, - "internal error: dtoa_short requires an IEEE-754 " - "floating-point implementation"); - - constexpr int kPrecision = - std::numeric_limits::digits; // = p (includes the hidden bit) - constexpr int kBias = - std::numeric_limits::max_exponent - 1 + (kPrecision - 1); - constexpr int kMinExp = 1 - kBias; - constexpr std::uint64_t kHiddenBit = std::uint64_t{1} - << (kPrecision - 1); // = 2^(p-1) - - using bits_type = typename std::conditional::type; - - const std::uint64_t bits = reinterpret_bits(value); - const std::uint64_t E = bits >> (kPrecision - 1); - const std::uint64_t F = bits & (kHiddenBit - 1); - - const bool is_denormal = E == 0; - const diyfp v = is_denormal - ? diyfp(F, kMinExp) - : diyfp(F + kHiddenBit, static_cast(E) - kBias); - - // Compute the boundaries m- and m+ of the floating-point value - // v = f * 2^e. - // - // Determine v- and v+, the floating-point predecessor and successor if v, - // respectively. - // - // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) - // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) - // - // v+ = v + 2^e - // - // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ - // between m- and m+ round to v, regardless of how the input rounding - // algorithm breaks ties. - // - // ---+-------------+-------------+-------------+-------------+--- (A) - // v- m- v m+ v+ - // - // -----------------+------+------+-------------+-------------+--- (B) - // v- m- v m+ v+ - - const bool lower_boundary_is_closer = F == 0 && E > 1; - const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); - const diyfp m_minus = lower_boundary_is_closer - ? diyfp(4 * v.f - 1, v.e - 2) // (B) - : diyfp(2 * v.f - 1, v.e - 1); // (A) - - // Determine the normalized w+ = m+. - const diyfp w_plus = diyfp::normalize(m_plus); - - // Determine w- = m- such that e_(w-) = e_(w+). - const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); - - return {diyfp::normalize(v), w_minus, w_plus}; -} - -// Given normalized diyfp w, Grisu needs to find a (normalized) cached -// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies -// within a certain range [alpha, gamma] (Definition 3.2 from [1]) -// -// alpha <= e = e_c + e_w + q <= gamma -// -// or -// -// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q -// <= f_c * f_w * 2^gamma -// -// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies -// -// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma -// -// or -// -// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) -// -// The choice of (alpha,gamma) determines the size of the table and the form of -// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well -// in practice: -// -// The idea is to cut the number c * w = f * 2^e into two parts, which can be -// processed independently: An integral part p1, and a fractional part p2: -// -// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e -// = (f div 2^-e) + (f mod 2^-e) * 2^e -// = p1 + p2 * 2^e -// -// The conversion of p1 into decimal form requires a series of divisions and -// modulos by (a power of) 10. These operations are faster for 32-bit than for -// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be -// achieved by choosing -// -// -e >= 32 or e <= -32 := gamma -// -// In order to convert the fractional part -// -// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... -// -// into decimal form, the fraction is repeatedly multiplied by 10 and the digits -// d[-i] are extracted in order: -// -// (10 * p2) div 2^-e = d[-1] -// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... -// -// The multiplication by 10 must not overflow. It is sufficient to choose -// -// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. -// -// Since p2 = f mod 2^-e < 2^-e, -// -// -e <= 60 or e >= -60 := alpha - -constexpr int kAlpha = -60; -constexpr int kGamma = -32; - -struct cached_power // c = f * 2^e ~= 10^k -{ - std::uint64_t f; - int e; - int k; -}; - -/*! -For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached -power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c -satisfies (Definition 3.2 from [1]) - alpha <= e_c + e + q <= gamma. -*/ -inline cached_power get_cached_power_for_binary_exponent(int e) { - // Now - // - // alpha <= e_c + e + q <= gamma (1) - // ==> f_c * 2^alpha <= c * 2^e * 2^q - // - // and since the c's are normalized, 2^(q-1) <= f_c, - // - // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) - // ==> 2^(alpha - e - 1) <= c - // - // If c were an exact power of ten, i.e. c = 10^k, one may determine k as - // - // k = ceil( log_10( 2^(alpha - e - 1) ) ) - // = ceil( (alpha - e - 1) * log_10(2) ) - // - // From the paper: - // "In theory the result of the procedure could be wrong since c is rounded, - // and the computation itself is approximated [...]. In practice, however, - // this simple function is sufficient." - // - // For IEEE double precision floating-point numbers converted into - // normalized diyfp's w = f * 2^e, with q = 64, - // - // e >= -1022 (min IEEE exponent) - // -52 (p - 1) - // -52 (p - 1, possibly normalize denormal IEEE numbers) - // -11 (normalize the diyfp) - // = -1137 - // - // and - // - // e <= +1023 (max IEEE exponent) - // -52 (p - 1) - // -11 (normalize the diyfp) - // = 960 - // - // This binary exponent range [-1137,960] results in a decimal exponent - // range [-307,324]. One does not need to store a cached power for each - // k in this range. For each such k it suffices to find a cached power - // such that the exponent of the product lies in [alpha,gamma]. - // This implies that the difference of the decimal exponents of adjacent - // table entries must be less than or equal to - // - // floor( (gamma - alpha) * log_10(2) ) = 8. - // - // (A smaller distance gamma-alpha would require a larger table.) - - // NB: - // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. - - constexpr int kCachedPowersMinDecExp = -300; - constexpr int kCachedPowersDecStep = 8; - - static constexpr std::array kCachedPowers = {{ - {0xAB70FE17C79AC6CA, -1060, -300}, {0xFF77B1FCBEBCDC4F, -1034, -292}, - {0xBE5691EF416BD60C, -1007, -284}, {0x8DD01FAD907FFC3C, -980, -276}, - {0xD3515C2831559A83, -954, -268}, {0x9D71AC8FADA6C9B5, -927, -260}, - {0xEA9C227723EE8BCB, -901, -252}, {0xAECC49914078536D, -874, -244}, - {0x823C12795DB6CE57, -847, -236}, {0xC21094364DFB5637, -821, -228}, - {0x9096EA6F3848984F, -794, -220}, {0xD77485CB25823AC7, -768, -212}, - {0xA086CFCD97BF97F4, -741, -204}, {0xEF340A98172AACE5, -715, -196}, - {0xB23867FB2A35B28E, -688, -188}, {0x84C8D4DFD2C63F3B, -661, -180}, - {0xC5DD44271AD3CDBA, -635, -172}, {0x936B9FCEBB25C996, -608, -164}, - {0xDBAC6C247D62A584, -582, -156}, {0xA3AB66580D5FDAF6, -555, -148}, - {0xF3E2F893DEC3F126, -529, -140}, {0xB5B5ADA8AAFF80B8, -502, -132}, - {0x87625F056C7C4A8B, -475, -124}, {0xC9BCFF6034C13053, -449, -116}, - {0x964E858C91BA2655, -422, -108}, {0xDFF9772470297EBD, -396, -100}, - {0xA6DFBD9FB8E5B88F, -369, -92}, {0xF8A95FCF88747D94, -343, -84}, - {0xB94470938FA89BCF, -316, -76}, {0x8A08F0F8BF0F156B, -289, -68}, - {0xCDB02555653131B6, -263, -60}, {0x993FE2C6D07B7FAC, -236, -52}, - {0xE45C10C42A2B3B06, -210, -44}, {0xAA242499697392D3, -183, -36}, - {0xFD87B5F28300CA0E, -157, -28}, {0xBCE5086492111AEB, -130, -20}, - {0x8CBCCC096F5088CC, -103, -12}, {0xD1B71758E219652C, -77, -4}, - {0x9C40000000000000, -50, 4}, {0xE8D4A51000000000, -24, 12}, - {0xAD78EBC5AC620000, 3, 20}, {0x813F3978F8940984, 30, 28}, - {0xC097CE7BC90715B3, 56, 36}, {0x8F7E32CE7BEA5C70, 83, 44}, - {0xD5D238A4ABE98068, 109, 52}, {0x9F4F2726179A2245, 136, 60}, - {0xED63A231D4C4FB27, 162, 68}, {0xB0DE65388CC8ADA8, 189, 76}, - {0x83C7088E1AAB65DB, 216, 84}, {0xC45D1DF942711D9A, 242, 92}, - {0x924D692CA61BE758, 269, 100}, {0xDA01EE641A708DEA, 295, 108}, - {0xA26DA3999AEF774A, 322, 116}, {0xF209787BB47D6B85, 348, 124}, - {0xB454E4A179DD1877, 375, 132}, {0x865B86925B9BC5C2, 402, 140}, - {0xC83553C5C8965D3D, 428, 148}, {0x952AB45CFA97A0B3, 455, 156}, - {0xDE469FBD99A05FE3, 481, 164}, {0xA59BC234DB398C25, 508, 172}, - {0xF6C69A72A3989F5C, 534, 180}, {0xB7DCBF5354E9BECE, 561, 188}, - {0x88FCF317F22241E2, 588, 196}, {0xCC20CE9BD35C78A5, 614, 204}, - {0x98165AF37B2153DF, 641, 212}, {0xE2A0B5DC971F303A, 667, 220}, - {0xA8D9D1535CE3B396, 694, 228}, {0xFB9B7CD9A4A7443C, 720, 236}, - {0xBB764C4CA7A44410, 747, 244}, {0x8BAB8EEFB6409C1A, 774, 252}, - {0xD01FEF10A657842C, 800, 260}, {0x9B10A4E5E9913129, 827, 268}, - {0xE7109BFBA19C0C9D, 853, 276}, {0xAC2820D9623BF429, 880, 284}, - {0x80444B5E7AA7CF85, 907, 292}, {0xBF21E44003ACDD2D, 933, 300}, - {0x8E679C2F5E44FF8F, 960, 308}, {0xD433179D9C8CB841, 986, 316}, - {0x9E19DB92B4E31BA9, 1013, 324}, - }}; - - // This computation gives exactly the same results for k as - // k = ceil((kAlpha - e - 1) * 0.30102999566398114) - // for |e| <= 1500, but doesn't require floating-point operations. - // NB: log_10(2) ~= 78913 / 2^18 - const int f = kAlpha - e - 1; - const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); - - const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / - kCachedPowersDecStep; - - const cached_power cached = kCachedPowers[static_cast(index)]; - - return cached; -} - -/*! -For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. -For n == 0, returns 1 and sets pow10 := 1. -*/ -inline int find_largest_pow10(const std::uint32_t n, std::uint32_t &pow10) { - // LCOV_EXCL_START - if (n >= 1000000000) { - pow10 = 1000000000; - return 10; - } - // LCOV_EXCL_STOP - else if (n >= 100000000) { - pow10 = 100000000; - return 9; - } else if (n >= 10000000) { - pow10 = 10000000; - return 8; - } else if (n >= 1000000) { - pow10 = 1000000; - return 7; - } else if (n >= 100000) { - pow10 = 100000; - return 6; - } else if (n >= 10000) { - pow10 = 10000; - return 5; - } else if (n >= 1000) { - pow10 = 1000; - return 4; - } else if (n >= 100) { - pow10 = 100; - return 3; - } else if (n >= 10) { - pow10 = 10; - return 2; - } else { - pow10 = 1; - return 1; - } -} - -inline void grisu2_round(char *buf, int len, std::uint64_t dist, - std::uint64_t delta, std::uint64_t rest, - std::uint64_t ten_k) { - - // <--------------------------- delta ----> - // <---- dist ---------> - // --------------[------------------+-------------------]-------------- - // M- w M+ - // - // ten_k - // <------> - // <---- rest ----> - // --------------[------------------+----+--------------]-------------- - // w V - // = buf * 10^k - // - // ten_k represents a unit-in-the-last-place in the decimal representation - // stored in buf. - // Decrement buf by ten_k while this takes buf closer to w. - - // The tests are written in this order to avoid overflow in unsigned - // integer arithmetic. - - while (rest < dist && delta - rest >= ten_k && - (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) { - buf[len - 1]--; - rest += ten_k; - } -} - -/*! -Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. -M- and M+ must be normalized and share the same exponent -60 <= e <= -32. -*/ -inline void grisu2_digit_gen(char *buffer, int &length, int &decimal_exponent, - diyfp M_minus, diyfp w, diyfp M_plus) { - static_assert(kAlpha >= -60, "internal error"); - static_assert(kGamma <= -32, "internal error"); - - // Generates the digits (and the exponent) of a decimal floating-point - // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's - // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= - // gamma. - // - // <--------------------------- delta ----> - // <---- dist ---------> - // --------------[------------------+-------------------]-------------- - // M- w M+ - // - // Grisu2 generates the digits of M+ from left to right and stops as soon as - // V is in [M-,M+]. - - std::uint64_t delta = - diyfp::sub(M_plus, M_minus) - .f; // (significand of (M+ - M-), implicit exponent is e) - std::uint64_t dist = - diyfp::sub(M_plus, w) - .f; // (significand of (M+ - w ), implicit exponent is e) - - // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): - // - // M+ = f * 2^e - // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e - // = ((p1 ) * 2^-e + (p2 )) * 2^e - // = p1 + p2 * 2^e - - const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); - - auto p1 = static_cast( - M_plus.f >> - -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) - std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e - - // 1) - // - // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] - - std::uint32_t pow10; - const int k = find_largest_pow10(p1, pow10); - - // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) - // - // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) - // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) - // - // M+ = p1 + p2 * 2^e - // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e - // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e - // = d[k-1] * 10^(k-1) + ( rest) * 2^e - // - // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) - // - // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] - // - // but stop as soon as - // - // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e - - int n = k; - while (n > 0) { - // Invariants: - // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) - // pow10 = 10^(n-1) <= p1 < 10^n - // - const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) - const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) - // - // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e - // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) - // - buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d - // - // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) - // - p1 = r; - n--; - // - // M+ = buffer * 10^n + (p1 + p2 * 2^e) - // pow10 = 10^n - // - - // Now check if enough digits have been generated. - // Compute - // - // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e - // - // Note: - // Since rest and delta share the same exponent e, it suffices to - // compare the significands. - const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; - if (rest <= delta) { - // V = buffer * 10^n, with M- <= V <= M+. - - decimal_exponent += n; - - // We may now just stop. But instead look if the buffer could be - // decremented to bring V closer to w. - // - // pow10 = 10^n is now 1 ulp in the decimal representation V. - // The rounding procedure works with diyfp's with an implicit - // exponent of e. - // - // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e - // - const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; - grisu2_round(buffer, length, dist, delta, rest, ten_n); - - return; - } - - pow10 /= 10; - // - // pow10 = 10^(n-1) <= p1 < 10^n - // Invariants restored. - } - - // 2) - // - // The digits of the integral part have been generated: - // - // M+ = d[k-1]...d[1]d[0] + p2 * 2^e - // = buffer + p2 * 2^e - // - // Now generate the digits of the fractional part p2 * 2^e. - // - // Note: - // No decimal point is generated: the exponent is adjusted instead. - // - // p2 actually represents the fraction - // - // p2 * 2^e - // = p2 / 2^-e - // = d[-1] / 10^1 + d[-2] / 10^2 + ... - // - // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) - // - // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m - // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) - // - // using - // - // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) - // = ( d) * 2^-e + ( r) - // - // or - // 10^m * p2 * 2^e = d + r * 2^e - // - // i.e. - // - // M+ = buffer + p2 * 2^e - // = buffer + 10^-m * (d + r * 2^e) - // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e - // - // and stop as soon as 10^-m * r * 2^e <= delta * 2^e - - int m = 0; - for (;;) { - // Invariant: - // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) - // * 2^e - // = buffer * 10^-m + 10^-m * (p2 ) - // * 2^e = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e = - // buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + - // (10*p2 mod 2^-e)) * 2^e - // - p2 *= 10; - const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e - const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e - // - // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e - // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) - // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e - // - buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d - // - // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e - // - p2 = r; - m++; - // - // M+ = buffer * 10^-m + 10^-m * p2 * 2^e - // Invariant restored. - - // Check if enough digits have been generated. - // - // 10^-m * p2 * 2^e <= delta * 2^e - // p2 * 2^e <= 10^m * delta * 2^e - // p2 <= 10^m * delta - delta *= 10; - dist *= 10; - if (p2 <= delta) { - break; - } - } - - // V = buffer * 10^-m, with M- <= V <= M+. - - decimal_exponent -= m; - - // 1 ulp in the decimal representation is now 10^-m. - // Since delta and dist are now scaled by 10^m, we need to do the - // same with ulp in order to keep the units in sync. - // - // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e - // - const std::uint64_t ten_m = one.f; - grisu2_round(buffer, length, dist, delta, p2, ten_m); - - // By construction this algorithm generates the shortest possible decimal - // number (Loitsch, Theorem 6.2) which rounds back to w. - // For an input number of precision p, at least - // - // N = 1 + ceil(p * log_10(2)) - // - // decimal digits are sufficient to identify all binary floating-point - // numbers (Matula, "In-and-Out conversions"). - // This implies that the algorithm does not produce more than N decimal - // digits. - // - // N = 17 for p = 53 (IEEE double precision) - // N = 9 for p = 24 (IEEE single precision) -} - -/*! -v = buf * 10^decimal_exponent -len is the length of the buffer (number of decimal digits) -The buffer must be large enough, i.e. >= max_digits10. -*/ -inline void grisu2(char *buf, int &len, int &decimal_exponent, diyfp m_minus, - diyfp v, diyfp m_plus) { - - // --------(-----------------------+-----------------------)-------- (A) - // m- v m+ - // - // --------------------(-----------+-----------------------)-------- (B) - // m- v m+ - // - // First scale v (and m- and m+) such that the exponent is in the range - // [alpha, gamma]. - - const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); - - const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k - - // The exponent of the products is = v.e + c_minus_k.e + q and is in the range - // [alpha,gamma] - const diyfp w = diyfp::mul(v, c_minus_k); - const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); - const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); - - // ----(---+---)---------------(---+---)---------------(---+---)---- - // w- w w+ - // = c*m- = c*v = c*m+ - // - // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and - // w+ are now off by a small amount. - // In fact: - // - // w - v * 10^k < 1 ulp - // - // To account for this inaccuracy, add resp. subtract 1 ulp. - // - // --------+---[---------------(---+---)---------------]---+-------- - // w- M- w M+ w+ - // - // Now any number in [M-, M+] (bounds included) will round to w when input, - // regardless of how the input rounding algorithm breaks ties. - // - // And digit_gen generates the shortest possible such number in [M-, M+]. - // Note that this does not mean that Grisu2 always generates the shortest - // possible number in the interval (m-, m+). - const diyfp M_minus(w_minus.f + 1, w_minus.e); - const diyfp M_plus(w_plus.f - 1, w_plus.e); - - decimal_exponent = -cached.k; // = -(-k) = k - - grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); -} - -/*! -v = buf * 10^decimal_exponent -len is the length of the buffer (number of decimal digits) -The buffer must be large enough, i.e. >= max_digits10. -*/ -template -void grisu2(char *buf, int &len, int &decimal_exponent, FloatType value) { - static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, - "internal error: not enough precision"); - - // If the neighbors (and boundaries) of 'value' are always computed for - // double-precision numbers, all float's can be recovered using strtod (and - // strtof). However, the resulting decimal representations are not exactly - // "short". - // - // The documentation for 'std::to_chars' - // (https://en.cppreference.com/w/cpp/utility/to_chars) says "value is - // converted to a string as if by std::sprintf in the default ("C") locale" - // and since sprintf promotes float's to double's, I think this is exactly - // what 'std::to_chars' does. On the other hand, the documentation for - // 'std::to_chars' requires that "parsing the representation using the - // corresponding std::from_chars function recovers value exactly". That - // indicates that single precision floating-point numbers should be recovered - // using 'std::strtof'. - // - // NB: If the neighbors are computed for single-precision numbers, there is a - // single float - // (7.0385307e-26f) which can't be recovered using strtod. The resulting - // double precision value is off by 1 ulp. -#if 0 - const boundaries w = compute_boundaries(static_cast(value)); -#else - const boundaries w = compute_boundaries(value); -#endif - - grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); -} - -/*! -@brief appends a decimal representation of e to buf -@return a pointer to the element following the exponent. -@pre -1000 < e < 1000 -*/ -inline char *append_exponent(char *buf, int e) { - - if (e < 0) { - e = -e; - *buf++ = '-'; - } else { - *buf++ = '+'; - } - - auto k = static_cast(e); - if (k < 10) { - // Always print at least two digits in the exponent. - // This is for compatibility with printf("%g"). - *buf++ = '0'; - *buf++ = static_cast('0' + k); - } else if (k < 100) { - *buf++ = static_cast('0' + k / 10); - k %= 10; - *buf++ = static_cast('0' + k); - } else { - *buf++ = static_cast('0' + k / 100); - k %= 100; - *buf++ = static_cast('0' + k / 10); - k %= 10; - *buf++ = static_cast('0' + k); - } - - return buf; -} - -/*! -@brief prettify v = buf * 10^decimal_exponent -If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point -notation. Otherwise it will be printed in exponential notation. -@pre min_exp < 0 -@pre max_exp > 0 -*/ -inline char *format_buffer(char *buf, int len, int decimal_exponent, - int min_exp, int max_exp) { - - const int k = len; - const int n = len + decimal_exponent; - - // v = buf * 10^(n-k) - // k is the length of the buffer (number of decimal digits) - // n is the position of the decimal point relative to the start of the buffer. - - if (k <= n && n <= max_exp) { - // digits[000] - // len <= max_exp + 2 - - std::memset(buf + k, '0', static_cast(n) - static_cast(k)); - // Make it look like a floating-point number (#362, #378) - // buf[n + 0] = '.'; - // buf[n + 1] = '0'; - return buf + (static_cast(n)); - } - - if (0 < n && n <= max_exp) { - // dig.its - // len <= max_digits10 + 1 - std::memmove(buf + (static_cast(n) + 1), buf + n, - static_cast(k) - static_cast(n)); - buf[n] = '.'; - return buf + (static_cast(k) + 1U); - } - - if (min_exp < n && n <= 0) { - // 0.[000]digits - // len <= 2 + (-min_exp - 1) + max_digits10 - - std::memmove(buf + (2 + static_cast(-n)), buf, - static_cast(k)); - buf[0] = '0'; - buf[1] = '.'; - std::memset(buf + 2, '0', static_cast(-n)); - return buf + (2U + static_cast(-n) + static_cast(k)); - } - - if (k == 1) { - // dE+123 - // len <= 1 + 5 - - buf += 1; - } else { - // d.igitsE+123 - // len <= max_digits10 + 1 + 5 - - std::memmove(buf + 2, buf + 1, static_cast(k) - 1); - buf[1] = '.'; - buf += 1 + static_cast(k); - } - - *buf++ = 'e'; - return append_exponent(buf, n - 1); -} - -} // namespace dtoa_impl - -/*! -The format of the resulting decimal representation is similar to printf's %g -format. Returns an iterator pointing past-the-end of the decimal representation. -@note The input number must be finite, i.e. NaN's and Inf's are not supported. -@note The buffer must be large enough. -@note The result is NOT null-terminated. -*/ -char *to_chars(char *first, const char *last, double value) { - static_cast(last); // maybe unused - fix warning - bool negative = std::signbit(value); - if (negative) { - value = -value; - *first++ = '-'; - } - - if (value == 0) // +-0 - { - *first++ = '0'; - // Make it look like a floating-point number (#362, #378) - if(negative) { - *first++ = '.'; - *first++ = '0'; - } - return first; - } - // Compute v = buffer * 10^decimal_exponent. - // The decimal digits are stored in the buffer, which needs to be interpreted - // as an unsigned decimal integer. - // len is the length of the buffer, i.e. the number of decimal digits. - int len = 0; - int decimal_exponent = 0; - dtoa_impl::grisu2(first, len, decimal_exponent, value); - // Format the buffer like printf("%.*g", prec, value) - constexpr int kMinExp = -4; - constexpr int kMaxExp = std::numeric_limits::digits10; - - return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, - kMaxExp); -} -} // namespace internal -} // namespace simdjson -/* end file src/to_chars.cpp */ -/* begin file src/from_chars.cpp */ -#include -namespace simdjson { -namespace internal { - -/** - * The code in the internal::from_chars function is meant to handle the floating-point number parsing - * when we have more than 19 digits in the decimal mantissa. This should only be seen - * in adversarial scenarios: we do not expect production systems to even produce - * such floating-point numbers. - * - * The parser is based on work by Nigel Tao (at https://github.com/google/wuffs/) - * who credits Ken Thompson for the design (via a reference to the Go source - * code). See - * https://github.com/google/wuffs/blob/aa46859ea40c72516deffa1b146121952d6dfd3b/internal/cgen/base/floatconv-submodule-data.c - * https://github.com/google/wuffs/blob/46cd8105f47ca07ae2ba8e6a7818ef9c0df6c152/internal/cgen/base/floatconv-submodule-code.c - * It is probably not very fast but it is a fallback that should almost never be - * called in real life. Google Wuffs is published under APL 2.0. - **/ - -namespace { -constexpr uint32_t max_digits = 768; -constexpr int32_t decimal_point_range = 2047; -} // namespace - -struct adjusted_mantissa { - uint64_t mantissa; - int power2; - adjusted_mantissa() : mantissa(0), power2(0) {} -}; - -struct decimal { - uint32_t num_digits; - int32_t decimal_point; - bool negative; - bool truncated; - uint8_t digits[max_digits]; -}; - -template struct binary_format { - static constexpr int mantissa_explicit_bits(); - static constexpr int minimum_exponent(); - static constexpr int infinite_power(); - static constexpr int sign_index(); -}; - -template <> constexpr int binary_format::mantissa_explicit_bits() { - return 52; -} - -template <> constexpr int binary_format::minimum_exponent() { - return -1023; -} -template <> constexpr int binary_format::infinite_power() { - return 0x7FF; -} - -template <> constexpr int binary_format::sign_index() { return 63; } - -bool is_integer(char c) noexcept { return (c >= '0' && c <= '9'); } - -// This should always succeed since it follows a call to parse_number. -decimal parse_decimal(const char *&p) noexcept { - decimal answer; - answer.num_digits = 0; - answer.decimal_point = 0; - answer.truncated = false; - answer.negative = (*p == '-'); - if ((*p == '-') || (*p == '+')) { - ++p; - } - - while (*p == '0') { - ++p; - } - while (is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; - } - if (*p == '.') { - ++p; - const char *first_after_period = p; - // if we have not yet encountered a zero, we have to skip it as well - if (answer.num_digits == 0) { - // skip zeros - while (*p == '0') { - ++p; - } - } - while (is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; - } - answer.decimal_point = int32_t(first_after_period - p); - } - if(answer.num_digits > 0) { - const char *preverse = p - 1; - int32_t trailing_zeros = 0; - while ((*preverse == '0') || (*preverse == '.')) { - if(*preverse == '0') { trailing_zeros++; }; - --preverse; - } - answer.decimal_point += int32_t(answer.num_digits); - answer.num_digits -= uint32_t(trailing_zeros); - } - if(answer.num_digits > max_digits ) { - answer.num_digits = max_digits; - answer.truncated = true; - } - if (('e' == *p) || ('E' == *p)) { - ++p; - bool neg_exp = false; - if ('-' == *p) { - neg_exp = true; - ++p; - } else if ('+' == *p) { - ++p; - } - int32_t exp_number = 0; // exponential part - while (is_integer(*p)) { - uint8_t digit = uint8_t(*p - '0'); - if (exp_number < 0x10000) { - exp_number = 10 * exp_number + digit; - } - ++p; - } - answer.decimal_point += (neg_exp ? -exp_number : exp_number); - } - return answer; -} - -// This should always succeed since it follows a call to parse_number. -// Will not read at or beyond the "end" pointer. -decimal parse_decimal(const char *&p, const char * end) noexcept { - decimal answer; - answer.num_digits = 0; - answer.decimal_point = 0; - answer.truncated = false; - if(p == end) { return answer; } // should never happen - answer.negative = (*p == '-'); - if ((*p == '-') || (*p == '+')) { - ++p; - } - - while ((p != end) && (*p == '0')) { - ++p; - } - while ((p != end) && is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; - } - if ((p != end) && (*p == '.')) { - ++p; - if(p == end) { return answer; } // should never happen - const char *first_after_period = p; - // if we have not yet encountered a zero, we have to skip it as well - if (answer.num_digits == 0) { - // skip zeros - while (*p == '0') { - ++p; - } - } - while ((p != end) && is_integer(*p)) { - if (answer.num_digits < max_digits) { - answer.digits[answer.num_digits] = uint8_t(*p - '0'); - } - answer.num_digits++; - ++p; - } - answer.decimal_point = int32_t(first_after_period - p); - } - if(answer.num_digits > 0) { - const char *preverse = p - 1; - int32_t trailing_zeros = 0; - while ((*preverse == '0') || (*preverse == '.')) { - if(*preverse == '0') { trailing_zeros++; }; - --preverse; - } - answer.decimal_point += int32_t(answer.num_digits); - answer.num_digits -= uint32_t(trailing_zeros); - } - if(answer.num_digits > max_digits ) { - answer.num_digits = max_digits; - answer.truncated = true; - } - if ((p != end) && (('e' == *p) || ('E' == *p))) { - ++p; - if(p == end) { return answer; } // should never happen - bool neg_exp = false; - if ('-' == *p) { - neg_exp = true; - ++p; - } else if ('+' == *p) { - ++p; - } - int32_t exp_number = 0; // exponential part - while ((p != end) && is_integer(*p)) { - uint8_t digit = uint8_t(*p - '0'); - if (exp_number < 0x10000) { - exp_number = 10 * exp_number + digit; - } - ++p; - } - answer.decimal_point += (neg_exp ? -exp_number : exp_number); - } - return answer; -} - -namespace { - -// remove all final zeroes -inline void trim(decimal &h) { - while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) { - h.num_digits--; - } -} - -uint32_t number_of_digits_decimal_left_shift(decimal &h, uint32_t shift) { - shift &= 63; - const static uint16_t number_of_digits_decimal_left_shift_table[65] = { - 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, - 0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, - 0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, - 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0, - 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA, - 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, - 0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, - 0x051C, 0x051C, - }; - uint32_t x_a = number_of_digits_decimal_left_shift_table[shift]; - uint32_t x_b = number_of_digits_decimal_left_shift_table[shift + 1]; - uint32_t num_new_digits = x_a >> 11; - uint32_t pow5_a = 0x7FF & x_a; - uint32_t pow5_b = 0x7FF & x_b; - const static uint8_t - number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = { - 5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, - 3, 9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, - 2, 8, 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, - 5, 6, 1, 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, - 5, 2, 5, 8, 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, - 3, 8, 1, 4, 6, 9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, - 8, 1, 2, 5, 9, 5, 3, 6, 7, 4, 3, 1, 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, - 7, 1, 5, 8, 2, 0, 3, 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 1, 0, 1, 5, - 6, 2, 5, 1, 1, 9, 2, 0, 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, - 0, 4, 6, 4, 4, 7, 7, 5, 3, 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, - 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, 4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, - 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, 0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1, 2, - 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, - 6, 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, - 2, 2, 5, 7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5, 6, 2, 5, 4, 6, 5, 6, 6, 1, - 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2, 5, 2, 3, 2, 8, 3, 0, 6, - 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, 1, 1, 6, 4, 1, 5, 3, - 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, 5, 8, 2, 0, 7, 6, - 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, 2, 9, 1, 0, 3, - 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 1, 3, 2, 8, 1, 2, 5, 1, 4, 5, - 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0, 6, 2, 5, - 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, 0, 3, - 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, 6, - 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, - 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7, - 2, 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, - 3, 5, 0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, - 2, 2, 7, 3, 7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, - 9, 7, 6, 5, 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, - 2, 9, 7, 3, 9, 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, - 8, 6, 0, 8, 0, 8, 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, - 2, 8, 4, 2, 1, 7, 0, 9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, - 9, 7, 0, 7, 0, 3, 1, 2, 5, 1, 4, 2, 1, 0, 8, 5, 4, 7, 1, 5, 2, 0, 2, - 0, 0, 3, 7, 1, 7, 4, 2, 2, 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 0, 5, - 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, - 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7, 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, - 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, 9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, - 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, 6, 7, 7, 8, 1, 0, 6, 6, 8, 9, - 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, 9, 7, 0, 0, 1, 2, 5, 2, 3, - 2, 3, 3, 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, 6, 2, 5, 4, 4, 4, 0, 8, - 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9, 4, 5, 2, 6, 6, 7, 2, - 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4, 9, 2, 5, 0, 3, 1, - 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, 0, 6, 2, 5, 1, - 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, 2, 3, 6, 3, - 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, 5, 1, 2, - 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 8, 3, 4, 0, 4, 5, 4, 1, - 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1, 3, - 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, - 3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, - 9, 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, - 9, 0, 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, - 7, 6, 2, 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, - 6, 1, 4, 1, 8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, - 6, 5, 6, 2, 5, 1, 7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, - 4, 4, 1, 1, 9, 2, 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, - 5, 8, 6, 7, 3, 6, 1, 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, - 6, 2, 2, 4, 0, 6, 9, 5, 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5, - }; - const uint8_t *pow5 = - &number_of_digits_decimal_left_shift_table_powers_of_5[pow5_a]; - uint32_t i = 0; - uint32_t n = pow5_b - pow5_a; - for (; i < n; i++) { - if (i >= h.num_digits) { - return num_new_digits - 1; - } else if (h.digits[i] == pow5[i]) { - continue; - } else if (h.digits[i] < pow5[i]) { - return num_new_digits - 1; - } else { - return num_new_digits; - } - } - return num_new_digits; -} - -} // end of anonymous namespace - -uint64_t round(decimal &h) { - if ((h.num_digits == 0) || (h.decimal_point < 0)) { - return 0; - } else if (h.decimal_point > 18) { - return UINT64_MAX; - } - // at this point, we know that h.decimal_point >= 0 - uint32_t dp = uint32_t(h.decimal_point); - uint64_t n = 0; - for (uint32_t i = 0; i < dp; i++) { - n = (10 * n) + ((i < h.num_digits) ? h.digits[i] : 0); - } - bool round_up = false; - if (dp < h.num_digits) { - round_up = h.digits[dp] >= 5; // normally, we round up - // but we may need to round to even! - if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) { - round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1])); - } - } - if (round_up) { - n++; - } - return n; -} - -// computes h * 2^-shift -void decimal_left_shift(decimal &h, uint32_t shift) { - if (h.num_digits == 0) { - return; - } - uint32_t num_new_digits = number_of_digits_decimal_left_shift(h, shift); - int32_t read_index = int32_t(h.num_digits - 1); - uint32_t write_index = h.num_digits - 1 + num_new_digits; - uint64_t n = 0; - - while (read_index >= 0) { - n += uint64_t(h.digits[read_index]) << shift; - uint64_t quotient = n / 10; - uint64_t remainder = n - (10 * quotient); - if (write_index < max_digits) { - h.digits[write_index] = uint8_t(remainder); - } else if (remainder > 0) { - h.truncated = true; - } - n = quotient; - write_index--; - read_index--; - } - while (n > 0) { - uint64_t quotient = n / 10; - uint64_t remainder = n - (10 * quotient); - if (write_index < max_digits) { - h.digits[write_index] = uint8_t(remainder); - } else if (remainder > 0) { - h.truncated = true; - } - n = quotient; - write_index--; - } - h.num_digits += num_new_digits; - if (h.num_digits > max_digits) { - h.num_digits = max_digits; - } - h.decimal_point += int32_t(num_new_digits); - trim(h); -} - -// computes h * 2^shift -void decimal_right_shift(decimal &h, uint32_t shift) { - uint32_t read_index = 0; - uint32_t write_index = 0; - - uint64_t n = 0; - - while ((n >> shift) == 0) { - if (read_index < h.num_digits) { - n = (10 * n) + h.digits[read_index++]; - } else if (n == 0) { - return; - } else { - while ((n >> shift) == 0) { - n = 10 * n; - read_index++; - } - break; - } - } - h.decimal_point -= int32_t(read_index - 1); - if (h.decimal_point < -decimal_point_range) { // it is zero - h.num_digits = 0; - h.decimal_point = 0; - h.negative = false; - h.truncated = false; - return; - } - uint64_t mask = (uint64_t(1) << shift) - 1; - while (read_index < h.num_digits) { - uint8_t new_digit = uint8_t(n >> shift); - n = (10 * (n & mask)) + h.digits[read_index++]; - h.digits[write_index++] = new_digit; - } - while (n > 0) { - uint8_t new_digit = uint8_t(n >> shift); - n = 10 * (n & mask); - if (write_index < max_digits) { - h.digits[write_index++] = new_digit; - } else if (new_digit > 0) { - h.truncated = true; - } - } - h.num_digits = write_index; - trim(h); -} - -template adjusted_mantissa compute_float(decimal &d) { - adjusted_mantissa answer; - if (d.num_digits == 0) { - // should be zero - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } - // At this point, going further, we can assume that d.num_digits > 0. - // We want to guard against excessive decimal point values because - // they can result in long running times. Indeed, we do - // shifts by at most 60 bits. We have that log(10**400)/log(2**60) ~= 22 - // which is fine, but log(10**299995)/log(2**60) ~= 16609 which is not - // fine (runs for a long time). - // - if(d.decimal_point < -324) { - // We have something smaller than 1e-324 which is always zero - // in binary64 and binary32. - // It should be zero. - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } else if(d.decimal_point >= 310) { - // We have something at least as large as 0.1e310 which is - // always infinite. - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } - - static const uint32_t max_shift = 60; - static const uint32_t num_powers = 19; - static const uint8_t powers[19] = { - 0, 3, 6, 9, 13, 16, 19, 23, 26, 29, // - 33, 36, 39, 43, 46, 49, 53, 56, 59, // - }; - int32_t exp2 = 0; - while (d.decimal_point > 0) { - uint32_t n = uint32_t(d.decimal_point); - uint32_t shift = (n < num_powers) ? powers[n] : max_shift; - decimal_right_shift(d, shift); - if (d.decimal_point < -decimal_point_range) { - // should be zero - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } - exp2 += int32_t(shift); - } - // We shift left toward [1/2 ... 1]. - while (d.decimal_point <= 0) { - uint32_t shift; - if (d.decimal_point == 0) { - if (d.digits[0] >= 5) { - break; - } - shift = (d.digits[0] < 2) ? 2 : 1; - } else { - uint32_t n = uint32_t(-d.decimal_point); - shift = (n < num_powers) ? powers[n] : max_shift; - } - decimal_left_shift(d, shift); - if (d.decimal_point > decimal_point_range) { - // we want to get infinity: - answer.power2 = 0xFF; - answer.mantissa = 0; - return answer; - } - exp2 -= int32_t(shift); - } - // We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2]. - exp2--; - constexpr int32_t minimum_exponent = binary::minimum_exponent(); - while ((minimum_exponent + 1) > exp2) { - uint32_t n = uint32_t((minimum_exponent + 1) - exp2); - if (n > max_shift) { - n = max_shift; - } - decimal_right_shift(d, n); - exp2 += int32_t(n); - } - if ((exp2 - minimum_exponent) >= binary::infinite_power()) { - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } - - const int mantissa_size_in_bits = binary::mantissa_explicit_bits() + 1; - decimal_left_shift(d, mantissa_size_in_bits); - - uint64_t mantissa = round(d); - // It is possible that we have an overflow, in which case we need - // to shift back. - if (mantissa >= (uint64_t(1) << mantissa_size_in_bits)) { - decimal_right_shift(d, 1); - exp2 += 1; - mantissa = round(d); - if ((exp2 - minimum_exponent) >= binary::infinite_power()) { - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } - } - answer.power2 = exp2 - binary::minimum_exponent(); - if (mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) { - answer.power2--; - } - answer.mantissa = - mantissa & ((uint64_t(1) << binary::mantissa_explicit_bits()) - 1); - return answer; -} - -template -adjusted_mantissa parse_long_mantissa(const char *first) { - decimal d = parse_decimal(first); - return compute_float(d); -} - -template -adjusted_mantissa parse_long_mantissa(const char *first, const char *end) { - decimal d = parse_decimal(first, end); - return compute_float(d); -} - -double from_chars(const char *first) noexcept { - bool negative = first[0] == '-'; - if (negative) { - first++; - } - adjusted_mantissa am = parse_long_mantissa>(first); - uint64_t word = am.mantissa; - word |= uint64_t(am.power2) - << binary_format::mantissa_explicit_bits(); - word = negative ? word | (uint64_t(1) << binary_format::sign_index()) - : word; - double value; - std::memcpy(&value, &word, sizeof(double)); - return value; -} - - -double from_chars(const char *first, const char *end) noexcept { - bool negative = first[0] == '-'; - if (negative) { - first++; - } - adjusted_mantissa am = parse_long_mantissa>(first, end); - uint64_t word = am.mantissa; - word |= uint64_t(am.power2) - << binary_format::mantissa_explicit_bits(); - word = negative ? word | (uint64_t(1) << binary_format::sign_index()) - : word; - double value; - std::memcpy(&value, &word, sizeof(double)); - return value; -} - -} // internal -} // simdjson -/* end file src/from_chars.cpp */ -/* begin file src/internal/error_tables.cpp */ - -namespace simdjson { -namespace internal { - - SIMDJSON_DLLIMPORTEXPORT const error_code_info error_codes[] { - { SUCCESS, "No error" }, - { CAPACITY, "This parser can't support a document that big" }, - { MEMALLOC, "Error allocating memory, we're most likely out of memory" }, - { TAPE_ERROR, "The JSON document has an improper structure: missing or superfluous commas, braces, missing keys, etc." }, - { DEPTH_ERROR, "The JSON document was too deep (too many nested objects and arrays)" }, - { STRING_ERROR, "Problem while parsing a string" }, - { T_ATOM_ERROR, "Problem while parsing an atom starting with the letter 't'" }, - { F_ATOM_ERROR, "Problem while parsing an atom starting with the letter 'f'" }, - { N_ATOM_ERROR, "Problem while parsing an atom starting with the letter 'n'" }, - { NUMBER_ERROR, "Problem while parsing a number" }, - { UTF8_ERROR, "The input is not valid UTF-8" }, - { UNINITIALIZED, "Uninitialized" }, - { EMPTY, "Empty: no JSON found" }, - { UNESCAPED_CHARS, "Within strings, some characters must be escaped, we found unescaped characters" }, - { UNCLOSED_STRING, "A string is opened, but never closed." }, - { UNSUPPORTED_ARCHITECTURE, "simdjson does not have an implementation supported by this CPU architecture (perhaps it's a non-SIMD CPU?)." }, - { INCORRECT_TYPE, "The JSON element does not have the requested type." }, - { NUMBER_OUT_OF_RANGE, "The JSON number is too large or too small to fit within the requested type." }, - { INDEX_OUT_OF_BOUNDS, "Attempted to access an element of a JSON array that is beyond its length." }, - { NO_SUCH_FIELD, "The JSON field referenced does not exist in this object." }, - { IO_ERROR, "Error reading the file." }, - { INVALID_JSON_POINTER, "Invalid JSON pointer syntax." }, - { INVALID_URI_FRAGMENT, "Invalid URI fragment syntax." }, - { UNEXPECTED_ERROR, "Unexpected error, consider reporting this problem as you may have found a bug in simdjson" }, - { PARSER_IN_USE, "Cannot parse a new document while a document is still in use." }, - { OUT_OF_ORDER_ITERATION, "Objects and arrays can only be iterated when they are first encountered." }, - { INSUFFICIENT_PADDING, "simdjson requires the input JSON string to have at least SIMDJSON_PADDING extra bytes allocated, beyond the string's length. Consider using the simdjson::padded_string class if needed." }, - { INCOMPLETE_ARRAY_OR_OBJECT, "JSON document ended early in the middle of an object or array." }, - { SCALAR_DOCUMENT_AS_VALUE, "A JSON document made of a scalar (number, Boolean, null or string) is treated as a value. Use get_bool(), get_double(), etc. on the document instead. "}, - { OUT_OF_BOUNDS, "Attempted to access location outside of document."} - }; // error_messages[] - -} // namespace internal -} // namespace simdjson -/* end file src/internal/error_tables.cpp */ -/* begin file src/internal/jsoncharutils_tables.cpp */ - -namespace simdjson { -namespace internal { - -// structural chars here are -// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL) -// we are also interested in the four whitespace characters -// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d - -SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace_negated[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - -SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -SIMDJSON_DLLIMPORTEXPORT const uint32_t digit_to_val32[886] = { - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, - 0x6, 0x7, 0x8, 0x9, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa, - 0xb, 0xc, 0xd, 0xe, 0xf, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa, 0xb, 0xc, 0xd, 0xe, - 0xf, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, - 0x60, 0x70, 0x80, 0x90, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa0, - 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, - 0xf0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x100, 0x200, 0x300, 0x400, 0x500, - 0x600, 0x700, 0x800, 0x900, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa00, - 0xb00, 0xc00, 0xd00, 0xe00, 0xf00, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, - 0xf00, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x0, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, - 0x6000, 0x7000, 0x8000, 0x9000, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xa000, - 0xb000, 0xc000, 0xd000, 0xe000, 0xf000, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, - 0xf000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}; - -} // namespace internal -} // namespace simdjson -/* end file src/internal/jsoncharutils_tables.cpp */ -/* begin file src/internal/numberparsing_tables.cpp */ - -namespace simdjson { -namespace internal { - -// Precomputed powers of ten from 10^0 to 10^22. These -// can be represented exactly using the double type. -SIMDJSON_DLLIMPORTEXPORT const double power_of_ten[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, - 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; - -/** - * When mapping numbers from decimal to binary, - * we go from w * 10^q to m * 2^p but we have - * 10^q = 5^q * 2^q, so effectively - * we are trying to match - * w * 2^q * 5^q to m * 2^p. Thus the powers of two - * are not a concern since they can be represented - * exactly using the binary notation, only the powers of five - * affect the binary significand. - */ - - -// The truncated powers of five from 5^-342 all the way to 5^308 -// The mantissa is truncated to 128 bits, and -// never rounded up. Uses about 10KB. -SIMDJSON_DLLIMPORTEXPORT const uint64_t power_of_five_128[]= { - 0xeef453d6923bd65a,0x113faa2906a13b3f, - 0x9558b4661b6565f8,0x4ac7ca59a424c507, - 0xbaaee17fa23ebf76,0x5d79bcf00d2df649, - 0xe95a99df8ace6f53,0xf4d82c2c107973dc, - 0x91d8a02bb6c10594,0x79071b9b8a4be869, - 0xb64ec836a47146f9,0x9748e2826cdee284, - 0xe3e27a444d8d98b7,0xfd1b1b2308169b25, - 0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7, - 0xb208ef855c969f4f,0xbdbd2d335e51a935, - 0xde8b2b66b3bc4723,0xad2c788035e61382, - 0x8b16fb203055ac76,0x4c3bcb5021afcc31, - 0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d, - 0xd953e8624b85dd78,0xd71d6dad34a2af0d, - 0x87d4713d6f33aa6b,0x8672648c40e5ad68, - 0xa9c98d8ccb009506,0x680efdaf511f18c2, - 0xd43bf0effdc0ba48,0x212bd1b2566def2, - 0x84a57695fe98746d,0x14bb630f7604b57, - 0xa5ced43b7e3e9188,0x419ea3bd35385e2d, - 0xcf42894a5dce35ea,0x52064cac828675b9, - 0x818995ce7aa0e1b2,0x7343efebd1940993, - 0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8, - 0xca66fa129f9b60a6,0xd41a26e077774ef6, - 0xfd00b897478238d0,0x8920b098955522b4, - 0x9e20735e8cb16382,0x55b46e5f5d5535b0, - 0xc5a890362fddbc62,0xeb2189f734aa831d, - 0xf712b443bbd52b7b,0xa5e9ec7501d523e4, - 0x9a6bb0aa55653b2d,0x47b233c92125366e, - 0xc1069cd4eabe89f8,0x999ec0bb696e840a, - 0xf148440a256e2c76,0xc00670ea43ca250d, - 0x96cd2a865764dbca,0x380406926a5e5728, - 0xbc807527ed3e12bc,0xc605083704f5ecf2, - 0xeba09271e88d976b,0xf7864a44c633682e, - 0x93445b8731587ea3,0x7ab3ee6afbe0211d, - 0xb8157268fdae9e4c,0x5960ea05bad82964, - 0xe61acf033d1a45df,0x6fb92487298e33bd, - 0x8fd0c16206306bab,0xa5d3b6d479f8e056, - 0xb3c4f1ba87bc8696,0x8f48a4899877186c, - 0xe0b62e2929aba83c,0x331acdabfe94de87, - 0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14, - 0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9, - 0xdb71e91432b1a24a,0xc9e82cd9f69d6150, - 0x892731ac9faf056e,0xbe311c083a225cd2, - 0xab70fe17c79ac6ca,0x6dbd630a48aaf406, - 0xd64d3d9db981787d,0x92cbbccdad5b108, - 0x85f0468293f0eb4e,0x25bbf56008c58ea5, - 0xa76c582338ed2621,0xaf2af2b80af6f24e, - 0xd1476e2c07286faa,0x1af5af660db4aee1, - 0x82cca4db847945ca,0x50d98d9fc890ed4d, - 0xa37fce126597973c,0xe50ff107bab528a0, - 0xcc5fc196fefd7d0c,0x1e53ed49a96272c8, - 0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a, - 0x9faacf3df73609b1,0x77b191618c54e9ac, - 0xc795830d75038c1d,0xd59df5b9ef6a2417, - 0xf97ae3d0d2446f25,0x4b0573286b44ad1d, - 0x9becce62836ac577,0x4ee367f9430aec32, - 0xc2e801fb244576d5,0x229c41f793cda73f, - 0xf3a20279ed56d48a,0x6b43527578c1110f, - 0x9845418c345644d6,0x830a13896b78aaa9, - 0xbe5691ef416bd60c,0x23cc986bc656d553, - 0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8, - 0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9, - 0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53, - 0xe858ad248f5c22c9,0xd1b3400f8f9cff68, - 0x91376c36d99995be,0x23100809b9c21fa1, - 0xb58547448ffffb2d,0xabd40a0c2832a78a, - 0xe2e69915b3fff9f9,0x16c90c8f323f516c, - 0x8dd01fad907ffc3b,0xae3da7d97f6792e3, - 0xb1442798f49ffb4a,0x99cd11cfdf41779c, - 0xdd95317f31c7fa1d,0x40405643d711d583, - 0x8a7d3eef7f1cfc52,0x482835ea666b2572, - 0xad1c8eab5ee43b66,0xda3243650005eecf, - 0xd863b256369d4a40,0x90bed43e40076a82, - 0x873e4f75e2224e68,0x5a7744a6e804a291, - 0xa90de3535aaae202,0x711515d0a205cb36, - 0xd3515c2831559a83,0xd5a5b44ca873e03, - 0x8412d9991ed58091,0xe858790afe9486c2, - 0xa5178fff668ae0b6,0x626e974dbe39a872, - 0xce5d73ff402d98e3,0xfb0a3d212dc8128f, - 0x80fa687f881c7f8e,0x7ce66634bc9d0b99, - 0xa139029f6a239f72,0x1c1fffc1ebc44e80, - 0xc987434744ac874e,0xa327ffb266b56220, - 0xfbe9141915d7a922,0x4bf1ff9f0062baa8, - 0x9d71ac8fada6c9b5,0x6f773fc3603db4a9, - 0xc4ce17b399107c22,0xcb550fb4384d21d3, - 0xf6019da07f549b2b,0x7e2a53a146606a48, - 0x99c102844f94e0fb,0x2eda7444cbfc426d, - 0xc0314325637a1939,0xfa911155fefb5308, - 0xf03d93eebc589f88,0x793555ab7eba27ca, - 0x96267c7535b763b5,0x4bc1558b2f3458de, - 0xbbb01b9283253ca2,0x9eb1aaedfb016f16, - 0xea9c227723ee8bcb,0x465e15a979c1cadc, - 0x92a1958a7675175f,0xbfacd89ec191ec9, - 0xb749faed14125d36,0xcef980ec671f667b, - 0xe51c79a85916f484,0x82b7e12780e7401a, - 0x8f31cc0937ae58d2,0xd1b2ecb8b0908810, - 0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15, - 0xdfbdcece67006ac9,0x67a791e093e1d49a, - 0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0, - 0xaecc49914078536d,0x58fae9f773886e18, - 0xda7f5bf590966848,0xaf39a475506a899e, - 0x888f99797a5e012d,0x6d8406c952429603, - 0xaab37fd7d8f58178,0xc8e5087ba6d33b83, - 0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64, - 0x855c3be0a17fcd26,0x5cf2eea09a55067f, - 0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e, - 0xd0601d8efc57b08b,0xf13b94daf124da26, - 0x823c12795db6ce57,0x76c53d08d6b70858, - 0xa2cb1717b52481ed,0x54768c4b0c64ca6e, - 0xcb7ddcdda26da268,0xa9942f5dcf7dfd09, - 0xfe5d54150b090b02,0xd3f93b35435d7c4c, - 0x9efa548d26e5a6e1,0xc47bc5014a1a6daf, - 0xc6b8e9b0709f109a,0x359ab6419ca1091b, - 0xf867241c8cc6d4c0,0xc30163d203c94b62, - 0x9b407691d7fc44f8,0x79e0de63425dcf1d, - 0xc21094364dfb5636,0x985915fc12f542e4, - 0xf294b943e17a2bc4,0x3e6f5b7b17b2939d, - 0x979cf3ca6cec5b5a,0xa705992ceecf9c42, - 0xbd8430bd08277231,0x50c6ff782a838353, - 0xece53cec4a314ebd,0xa4f8bf5635246428, - 0x940f4613ae5ed136,0x871b7795e136be99, - 0xb913179899f68584,0x28e2557b59846e3f, - 0xe757dd7ec07426e5,0x331aeada2fe589cf, - 0x9096ea6f3848984f,0x3ff0d2c85def7621, - 0xb4bca50b065abe63,0xfed077a756b53a9, - 0xe1ebce4dc7f16dfb,0xd3e8495912c62894, - 0x8d3360f09cf6e4bd,0x64712dd7abbbd95c, - 0xb080392cc4349dec,0xbd8d794d96aacfb3, - 0xdca04777f541c567,0xecf0d7a0fc5583a0, - 0x89e42caaf9491b60,0xf41686c49db57244, - 0xac5d37d5b79b6239,0x311c2875c522ced5, - 0xd77485cb25823ac7,0x7d633293366b828b, - 0x86a8d39ef77164bc,0xae5dff9c02033197, - 0xa8530886b54dbdeb,0xd9f57f830283fdfc, - 0xd267caa862a12d66,0xd072df63c324fd7b, - 0x8380dea93da4bc60,0x4247cb9e59f71e6d, - 0xa46116538d0deb78,0x52d9be85f074e608, - 0xcd795be870516656,0x67902e276c921f8b, - 0x806bd9714632dff6,0xba1cd8a3db53b6, - 0xa086cfcd97bf97f3,0x80e8a40eccd228a4, - 0xc8a883c0fdaf7df0,0x6122cd128006b2cd, - 0xfad2a4b13d1b5d6c,0x796b805720085f81, - 0x9cc3a6eec6311a63,0xcbe3303674053bb0, - 0xc3f490aa77bd60fc,0xbedbfc4411068a9c, - 0xf4f1b4d515acb93b,0xee92fb5515482d44, - 0x991711052d8bf3c5,0x751bdd152d4d1c4a, - 0xbf5cd54678eef0b6,0xd262d45a78a0635d, - 0xef340a98172aace4,0x86fb897116c87c34, - 0x9580869f0e7aac0e,0xd45d35e6ae3d4da0, - 0xbae0a846d2195712,0x8974836059cca109, - 0xe998d258869facd7,0x2bd1a438703fc94b, - 0x91ff83775423cc06,0x7b6306a34627ddcf, - 0xb67f6455292cbf08,0x1a3bc84c17b1d542, - 0xe41f3d6a7377eeca,0x20caba5f1d9e4a93, - 0x8e938662882af53e,0x547eb47b7282ee9c, - 0xb23867fb2a35b28d,0xe99e619a4f23aa43, - 0xdec681f9f4c31f31,0x6405fa00e2ec94d4, - 0x8b3c113c38f9f37e,0xde83bc408dd3dd04, - 0xae0b158b4738705e,0x9624ab50b148d445, - 0xd98ddaee19068c76,0x3badd624dd9b0957, - 0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6, - 0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c, - 0xd47487cc8470652b,0x7647c3200069671f, - 0x84c8d4dfd2c63f3b,0x29ecd9f40041e073, - 0xa5fb0a17c777cf09,0xf468107100525890, - 0xcf79cc9db955c2cc,0x7182148d4066eeb4, - 0x81ac1fe293d599bf,0xc6f14cd848405530, - 0xa21727db38cb002f,0xb8ada00e5a506a7c, - 0xca9cf1d206fdc03b,0xa6d90811f0e4851c, - 0xfd442e4688bd304a,0x908f4a166d1da663, - 0x9e4a9cec15763e2e,0x9a598e4e043287fe, - 0xc5dd44271ad3cdba,0x40eff1e1853f29fd, - 0xf7549530e188c128,0xd12bee59e68ef47c, - 0x9a94dd3e8cf578b9,0x82bb74f8301958ce, - 0xc13a148e3032d6e7,0xe36a52363c1faf01, - 0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1, - 0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9, - 0xbcb2b812db11a5de,0x7415d448f6b6f0e7, - 0xebdf661791d60f56,0x111b495b3464ad21, - 0x936b9fcebb25c995,0xcab10dd900beec34, - 0xb84687c269ef3bfb,0x3d5d514f40eea742, - 0xe65829b3046b0afa,0xcb4a5a3112a5112, - 0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab, - 0xb3f4e093db73a093,0x59ed216765690f56, - 0xe0f218b8d25088b8,0x306869c13ec3532c, - 0x8c974f7383725573,0x1e414218c73a13fb, - 0xafbd2350644eeacf,0xe5d1929ef90898fa, - 0xdbac6c247d62a583,0xdf45f746b74abf39, - 0x894bc396ce5da772,0x6b8bba8c328eb783, - 0xab9eb47c81f5114f,0x66ea92f3f326564, - 0xd686619ba27255a2,0xc80a537b0efefebd, - 0x8613fd0145877585,0xbd06742ce95f5f36, - 0xa798fc4196e952e7,0x2c48113823b73704, - 0xd17f3b51fca3a7a0,0xf75a15862ca504c5, - 0x82ef85133de648c4,0x9a984d73dbe722fb, - 0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba, - 0xcc963fee10b7d1b3,0x318df905079926a8, - 0xffbbcfe994e5c61f,0xfdf17746497f7052, - 0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633, - 0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0, - 0xf9bd690a1b68637b,0x3dfdce7aa3c673b0, - 0x9c1661a651213e2d,0x6bea10ca65c084e, - 0xc31bfa0fe5698db8,0x486e494fcff30a62, - 0xf3e2f893dec3f126,0x5a89dba3c3efccfa, - 0x986ddb5c6b3a76b7,0xf89629465a75e01c, - 0xbe89523386091465,0xf6bbb397f1135823, - 0xee2ba6c0678b597f,0x746aa07ded582e2c, - 0x94db483840b717ef,0xa8c2a44eb4571cdc, - 0xba121a4650e4ddeb,0x92f34d62616ce413, - 0xe896a0d7e51e1566,0x77b020baf9c81d17, - 0x915e2486ef32cd60,0xace1474dc1d122e, - 0xb5b5ada8aaff80b8,0xd819992132456ba, - 0xe3231912d5bf60e6,0x10e1fff697ed6c69, - 0x8df5efabc5979c8f,0xca8d3ffa1ef463c1, - 0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2, - 0xddd0467c64bce4a0,0xac7cb3f6d05ddbde, - 0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b, - 0xad4ab7112eb3929d,0x86c16c98d2c953c6, - 0xd89d64d57a607744,0xe871c7bf077ba8b7, - 0x87625f056c7c4a8b,0x11471cd764ad4972, - 0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf, - 0xd389b47879823479,0x4aff1d108d4ec2c3, - 0x843610cb4bf160cb,0xcedf722a585139ba, - 0xa54394fe1eedb8fe,0xc2974eb4ee658828, - 0xce947a3da6a9273e,0x733d226229feea32, - 0x811ccc668829b887,0x806357d5a3f525f, - 0xa163ff802a3426a8,0xca07c2dcb0cf26f7, - 0xc9bcff6034c13052,0xfc89b393dd02f0b5, - 0xfc2c3f3841f17c67,0xbbac2078d443ace2, - 0x9d9ba7832936edc0,0xd54b944b84aa4c0d, - 0xc5029163f384a931,0xa9e795e65d4df11, - 0xf64335bcf065d37d,0x4d4617b5ff4a16d5, - 0x99ea0196163fa42e,0x504bced1bf8e4e45, - 0xc06481fb9bcf8d39,0xe45ec2862f71e1d6, - 0xf07da27a82c37088,0x5d767327bb4e5a4c, - 0x964e858c91ba2655,0x3a6a07f8d510f86f, - 0xbbe226efb628afea,0x890489f70a55368b, - 0xeadab0aba3b2dbe5,0x2b45ac74ccea842e, - 0x92c8ae6b464fc96f,0x3b0b8bc90012929d, - 0xb77ada0617e3bbcb,0x9ce6ebb40173744, - 0xe55990879ddcaabd,0xcc420a6a101d0515, - 0x8f57fa54c2a9eab6,0x9fa946824a12232d, - 0xb32df8e9f3546564,0x47939822dc96abf9, - 0xdff9772470297ebd,0x59787e2b93bc56f7, - 0x8bfbea76c619ef36,0x57eb4edb3c55b65a, - 0xaefae51477a06b03,0xede622920b6b23f1, - 0xdab99e59958885c4,0xe95fab368e45eced, - 0x88b402f7fd75539b,0x11dbcb0218ebb414, - 0xaae103b5fcd2a881,0xd652bdc29f26a119, - 0xd59944a37c0752a2,0x4be76d3346f0495f, - 0x857fcae62d8493a5,0x6f70a4400c562ddb, - 0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952, - 0xd097ad07a71f26b2,0x7e2000a41346a7a7, - 0x825ecc24c873782f,0x8ed400668c0c28c8, - 0xa2f67f2dfa90563b,0x728900802f0f32fa, - 0xcbb41ef979346bca,0x4f2b40a03ad2ffb9, - 0xfea126b7d78186bc,0xe2f610c84987bfa8, - 0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9, - 0xc6ede63fa05d3143,0x91503d1c79720dbb, - 0xf8a95fcf88747d94,0x75a44c6397ce912a, - 0x9b69dbe1b548ce7c,0xc986afbe3ee11aba, - 0xc24452da229b021b,0xfbe85badce996168, - 0xf2d56790ab41c2a2,0xfae27299423fb9c3, - 0x97c560ba6b0919a5,0xdccd879fc967d41a, - 0xbdb6b8e905cb600f,0x5400e987bbc1c920, - 0xed246723473e3813,0x290123e9aab23b68, - 0x9436c0760c86e30b,0xf9a0b6720aaf6521, - 0xb94470938fa89bce,0xf808e40e8d5b3e69, - 0xe7958cb87392c2c2,0xb60b1d1230b20e04, - 0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2, - 0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3, - 0xe2280b6c20dd5232,0x25c6da63c38de1b0, - 0x8d590723948a535f,0x579c487e5a38ad0e, - 0xb0af48ec79ace837,0x2d835a9df0c6d851, - 0xdcdb1b2798182244,0xf8e431456cf88e65, - 0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff, - 0xac8b2d36eed2dac5,0xe272467e3d222f3f, - 0xd7adf884aa879177,0x5b0ed81dcc6abb0f, - 0x86ccbb52ea94baea,0x98e947129fc2b4e9, - 0xa87fea27a539e9a5,0x3f2398d747b36224, - 0xd29fe4b18e88640e,0x8eec7f0d19a03aad, - 0x83a3eeeef9153e89,0x1953cf68300424ac, - 0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7, - 0xcdb02555653131b6,0x3792f412cb06794d, - 0x808e17555f3ebf11,0xe2bbd88bbee40bd0, - 0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4, - 0xc8de047564d20a8b,0xf245825a5a445275, - 0xfb158592be068d2e,0xeed6e2f0f0d56712, - 0x9ced737bb6c4183d,0x55464dd69685606b, - 0xc428d05aa4751e4c,0xaa97e14c3c26b886, - 0xf53304714d9265df,0xd53dd99f4b3066a8, - 0x993fe2c6d07b7fab,0xe546a8038efe4029, - 0xbf8fdb78849a5f96,0xde98520472bdd033, - 0xef73d256a5c0f77c,0x963e66858f6d4440, - 0x95a8637627989aad,0xdde7001379a44aa8, - 0xbb127c53b17ec159,0x5560c018580d5d52, - 0xe9d71b689dde71af,0xaab8f01e6e10b4a6, - 0x9226712162ab070d,0xcab3961304ca70e8, - 0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22, - 0xe45c10c42a2b3b05,0x8cb89a7db77c506a, - 0x8eb98a7a9a5b04e3,0x77f3608e92adb242, - 0xb267ed1940f1c61c,0x55f038b237591ed3, - 0xdf01e85f912e37a3,0x6b6c46dec52f6688, - 0x8b61313bbabce2c6,0x2323ac4b3b3da015, - 0xae397d8aa96c1b77,0xabec975e0a0d081a, - 0xd9c7dced53c72255,0x96e7bd358c904a21, - 0x881cea14545c7575,0x7e50d64177da2e54, - 0xaa242499697392d2,0xdde50bd1d5d0b9e9, - 0xd4ad2dbfc3d07787,0x955e4ec64b44e864, - 0x84ec3c97da624ab4,0xbd5af13bef0b113e, - 0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e, - 0xcfb11ead453994ba,0x67de18eda5814af2, - 0x81ceb32c4b43fcf4,0x80eacf948770ced7, - 0xa2425ff75e14fc31,0xa1258379a94d028d, - 0xcad2f7f5359a3b3e,0x96ee45813a04330, - 0xfd87b5f28300ca0d,0x8bca9d6e188853fc, - 0x9e74d1b791e07e48,0x775ea264cf55347e, - 0xc612062576589dda,0x95364afe032a81a0, - 0xf79687aed3eec551,0x3a83ddbd83f52210, - 0x9abe14cd44753b52,0xc4926a9672793580, - 0xc16d9a0095928a27,0x75b7053c0f178400, - 0xf1c90080baf72cb1,0x5324c68b12dd6800, - 0x971da05074da7bee,0xd3f6fc16ebca8000, - 0xbce5086492111aea,0x88f4bb1ca6bd0000, - 0xec1e4a7db69561a5,0x2b31e9e3d0700000, - 0x9392ee8e921d5d07,0x3aff322e62600000, - 0xb877aa3236a4b449,0x9befeb9fad487c3, - 0xe69594bec44de15b,0x4c2ebe687989a9b4, - 0x901d7cf73ab0acd9,0xf9d37014bf60a11, - 0xb424dc35095cd80f,0x538484c19ef38c95, - 0xe12e13424bb40e13,0x2865a5f206b06fba, - 0x8cbccc096f5088cb,0xf93f87b7442e45d4, - 0xafebff0bcb24aafe,0xf78f69a51539d749, - 0xdbe6fecebdedd5be,0xb573440e5a884d1c, - 0x89705f4136b4a597,0x31680a88f8953031, - 0xabcc77118461cefc,0xfdc20d2b36ba7c3e, - 0xd6bf94d5e57a42bc,0x3d32907604691b4d, - 0x8637bd05af6c69b5,0xa63f9a49c2c1b110, - 0xa7c5ac471b478423,0xfcf80dc33721d54, - 0xd1b71758e219652b,0xd3c36113404ea4a9, - 0x83126e978d4fdf3b,0x645a1cac083126ea, - 0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4, - 0xcccccccccccccccc,0xcccccccccccccccd, - 0x8000000000000000,0x0, - 0xa000000000000000,0x0, - 0xc800000000000000,0x0, - 0xfa00000000000000,0x0, - 0x9c40000000000000,0x0, - 0xc350000000000000,0x0, - 0xf424000000000000,0x0, - 0x9896800000000000,0x0, - 0xbebc200000000000,0x0, - 0xee6b280000000000,0x0, - 0x9502f90000000000,0x0, - 0xba43b74000000000,0x0, - 0xe8d4a51000000000,0x0, - 0x9184e72a00000000,0x0, - 0xb5e620f480000000,0x0, - 0xe35fa931a0000000,0x0, - 0x8e1bc9bf04000000,0x0, - 0xb1a2bc2ec5000000,0x0, - 0xde0b6b3a76400000,0x0, - 0x8ac7230489e80000,0x0, - 0xad78ebc5ac620000,0x0, - 0xd8d726b7177a8000,0x0, - 0x878678326eac9000,0x0, - 0xa968163f0a57b400,0x0, - 0xd3c21bcecceda100,0x0, - 0x84595161401484a0,0x0, - 0xa56fa5b99019a5c8,0x0, - 0xcecb8f27f4200f3a,0x0, - 0x813f3978f8940984,0x4000000000000000, - 0xa18f07d736b90be5,0x5000000000000000, - 0xc9f2c9cd04674ede,0xa400000000000000, - 0xfc6f7c4045812296,0x4d00000000000000, - 0x9dc5ada82b70b59d,0xf020000000000000, - 0xc5371912364ce305,0x6c28000000000000, - 0xf684df56c3e01bc6,0xc732000000000000, - 0x9a130b963a6c115c,0x3c7f400000000000, - 0xc097ce7bc90715b3,0x4b9f100000000000, - 0xf0bdc21abb48db20,0x1e86d40000000000, - 0x96769950b50d88f4,0x1314448000000000, - 0xbc143fa4e250eb31,0x17d955a000000000, - 0xeb194f8e1ae525fd,0x5dcfab0800000000, - 0x92efd1b8d0cf37be,0x5aa1cae500000000, - 0xb7abc627050305ad,0xf14a3d9e40000000, - 0xe596b7b0c643c719,0x6d9ccd05d0000000, - 0x8f7e32ce7bea5c6f,0xe4820023a2000000, - 0xb35dbf821ae4f38b,0xdda2802c8a800000, - 0xe0352f62a19e306e,0xd50b2037ad200000, - 0x8c213d9da502de45,0x4526f422cc340000, - 0xaf298d050e4395d6,0x9670b12b7f410000, - 0xdaf3f04651d47b4c,0x3c0cdd765f114000, - 0x88d8762bf324cd0f,0xa5880a69fb6ac800, - 0xab0e93b6efee0053,0x8eea0d047a457a00, - 0xd5d238a4abe98068,0x72a4904598d6d880, - 0x85a36366eb71f041,0x47a6da2b7f864750, - 0xa70c3c40a64e6c51,0x999090b65f67d924, - 0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d, - 0x82818f1281ed449f,0xbff8f10e7a8921a4, - 0xa321f2d7226895c7,0xaff72d52192b6a0d, - 0xcbea6f8ceb02bb39,0x9bf4f8a69f764490, - 0xfee50b7025c36a08,0x2f236d04753d5b4, - 0x9f4f2726179a2245,0x1d762422c946590, - 0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5, - 0xf8ebad2b84e0d58b,0xd2e0898765a7deb2, - 0x9b934c3b330c8577,0x63cc55f49f88eb2f, - 0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb, - 0xf316271c7fc3908a,0x8bef464e3945ef7a, - 0x97edd871cfda3a56,0x97758bf0e3cbb5ac, - 0xbde94e8e43d0c8ec,0x3d52eeed1cbea317, - 0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd, - 0x945e455f24fb1cf8,0x8fe8caa93e74ef6a, - 0xb975d6b6ee39e436,0xb3e2fd538e122b44, - 0xe7d34c64a9c85d44,0x60dbbca87196b616, - 0x90e40fbeea1d3a4a,0xbc8955e946fe31cd, - 0xb51d13aea4a488dd,0x6babab6398bdbe41, - 0xe264589a4dcdab14,0xc696963c7eed2dd1, - 0x8d7eb76070a08aec,0xfc1e1de5cf543ca2, - 0xb0de65388cc8ada8,0x3b25a55f43294bcb, - 0xdd15fe86affad912,0x49ef0eb713f39ebe, - 0x8a2dbf142dfcc7ab,0x6e3569326c784337, - 0xacb92ed9397bf996,0x49c2c37f07965404, - 0xd7e77a8f87daf7fb,0xdc33745ec97be906, - 0x86f0ac99b4e8dafd,0x69a028bb3ded71a3, - 0xa8acd7c0222311bc,0xc40832ea0d68ce0c, - 0xd2d80db02aabd62b,0xf50a3fa490c30190, - 0x83c7088e1aab65db,0x792667c6da79e0fa, - 0xa4b8cab1a1563f52,0x577001b891185938, - 0xcde6fd5e09abcf26,0xed4c0226b55e6f86, - 0x80b05e5ac60b6178,0x544f8158315b05b4, - 0xa0dc75f1778e39d6,0x696361ae3db1c721, - 0xc913936dd571c84c,0x3bc3a19cd1e38e9, - 0xfb5878494ace3a5f,0x4ab48a04065c723, - 0x9d174b2dcec0e47b,0x62eb0d64283f9c76, - 0xc45d1df942711d9a,0x3ba5d0bd324f8394, - 0xf5746577930d6500,0xca8f44ec7ee36479, - 0x9968bf6abbe85f20,0x7e998b13cf4e1ecb, - 0xbfc2ef456ae276e8,0x9e3fedd8c321a67e, - 0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e, - 0x95d04aee3b80ece5,0xbba1f1d158724a12, - 0xbb445da9ca61281f,0x2a8a6e45ae8edc97, - 0xea1575143cf97226,0xf52d09d71a3293bd, - 0x924d692ca61be758,0x593c2626705f9c56, - 0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c, - 0xe498f455c38b997a,0xb6dfb9c0f956447, - 0x8edf98b59a373fec,0x4724bd4189bd5eac, - 0xb2977ee300c50fe7,0x58edec91ec2cb657, - 0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed, - 0x8b865b215899f46c,0xbd79e0d20082ee74, - 0xae67f1e9aec07187,0xecd8590680a3aa11, - 0xda01ee641a708de9,0xe80e6f4820cc9495, - 0x884134fe908658b2,0x3109058d147fdcdd, - 0xaa51823e34a7eede,0xbd4b46f0599fd415, - 0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a, - 0x850fadc09923329e,0x3e2cf6bc604ddb0, - 0xa6539930bf6bff45,0x84db8346b786151c, - 0xcfe87f7cef46ff16,0xe612641865679a63, - 0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e, - 0xa26da3999aef7749,0xe3be5e330f38f09d, - 0xcb090c8001ab551c,0x5cadf5bfd3072cc5, - 0xfdcb4fa002162a63,0x73d9732fc7c8f7f6, - 0x9e9f11c4014dda7e,0x2867e7fddcdd9afa, - 0xc646d63501a1511d,0xb281e1fd541501b8, - 0xf7d88bc24209a565,0x1f225a7ca91a4226, - 0x9ae757596946075f,0x3375788de9b06958, - 0xc1a12d2fc3978937,0x52d6b1641c83ae, - 0xf209787bb47d6b84,0xc0678c5dbd23a49a, - 0x9745eb4d50ce6332,0xf840b7ba963646e0, - 0xbd176620a501fbff,0xb650e5a93bc3d898, - 0xec5d3fa8ce427aff,0xa3e51f138ab4cebe, - 0x93ba47c980e98cdf,0xc66f336c36b10137, - 0xb8a8d9bbe123f017,0xb80b0047445d4184, - 0xe6d3102ad96cec1d,0xa60dc059157491e5, - 0x9043ea1ac7e41392,0x87c89837ad68db2f, - 0xb454e4a179dd1877,0x29babe4598c311fb, - 0xe16a1dc9d8545e94,0xf4296dd6fef3d67a, - 0x8ce2529e2734bb1d,0x1899e4a65f58660c, - 0xb01ae745b101e9e4,0x5ec05dcff72e7f8f, - 0xdc21a1171d42645d,0x76707543f4fa1f73, - 0x899504ae72497eba,0x6a06494a791c53a8, - 0xabfa45da0edbde69,0x487db9d17636892, - 0xd6f8d7509292d603,0x45a9d2845d3c42b6, - 0x865b86925b9bc5c2,0xb8a2392ba45a9b2, - 0xa7f26836f282b732,0x8e6cac7768d7141e, - 0xd1ef0244af2364ff,0x3207d795430cd926, - 0x8335616aed761f1f,0x7f44e6bd49e807b8, - 0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6, - 0xcd036837130890a1,0x36dba887c37a8c0f, - 0x802221226be55a64,0xc2494954da2c9789, - 0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c, - 0xc83553c5c8965d3d,0x6f92829494e5acc7, - 0xfa42a8b73abbf48c,0xcb772339ba1f17f9, - 0x9c69a97284b578d7,0xff2a760414536efb, - 0xc38413cf25e2d70d,0xfef5138519684aba, - 0xf46518c2ef5b8cd1,0x7eb258665fc25d69, - 0x98bf2f79d5993802,0xef2f773ffbd97a61, - 0xbeeefb584aff8603,0xaafb550ffacfd8fa, - 0xeeaaba2e5dbf6784,0x95ba2a53f983cf38, - 0x952ab45cfa97a0b2,0xdd945a747bf26183, - 0xba756174393d88df,0x94f971119aeef9e4, - 0xe912b9d1478ceb17,0x7a37cd5601aab85d, - 0x91abb422ccb812ee,0xac62e055c10ab33a, - 0xb616a12b7fe617aa,0x577b986b314d6009, - 0xe39c49765fdf9d94,0xed5a7e85fda0b80b, - 0x8e41ade9fbebc27d,0x14588f13be847307, - 0xb1d219647ae6b31c,0x596eb2d8ae258fc8, - 0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb, - 0x8aec23d680043bee,0x25de7bb9480d5854, - 0xada72ccc20054ae9,0xaf561aa79a10ae6a, - 0xd910f7ff28069da4,0x1b2ba1518094da04, - 0x87aa9aff79042286,0x90fb44d2f05d0842, - 0xa99541bf57452b28,0x353a1607ac744a53, - 0xd3fa922f2d1675f2,0x42889b8997915ce8, - 0x847c9b5d7c2e09b7,0x69956135febada11, - 0xa59bc234db398c25,0x43fab9837e699095, - 0xcf02b2c21207ef2e,0x94f967e45e03f4bb, - 0x8161afb94b44f57d,0x1d1be0eebac278f5, - 0xa1ba1ba79e1632dc,0x6462d92a69731732, - 0xca28a291859bbf93,0x7d7b8f7503cfdcfe, - 0xfcb2cb35e702af78,0x5cda735244c3d43e, - 0x9defbf01b061adab,0x3a0888136afa64a7, - 0xc56baec21c7a1916,0x88aaa1845b8fdd0, - 0xf6c69a72a3989f5b,0x8aad549e57273d45, - 0x9a3c2087a63f6399,0x36ac54e2f678864b, - 0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd, - 0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5, - 0x969eb7c47859e743,0x9f644ae5a4b1b325, - 0xbc4665b596706114,0x873d5d9f0dde1fee, - 0xeb57ff22fc0c7959,0xa90cb506d155a7ea, - 0x9316ff75dd87cbd8,0x9a7f12442d588f2, - 0xb7dcbf5354e9bece,0xc11ed6d538aeb2f, - 0xe5d3ef282a242e81,0x8f1668c8a86da5fa, - 0x8fa475791a569d10,0xf96e017d694487bc, - 0xb38d92d760ec4455,0x37c981dcc395a9ac, - 0xe070f78d3927556a,0x85bbe253f47b1417, - 0x8c469ab843b89562,0x93956d7478ccec8e, - 0xaf58416654a6babb,0x387ac8d1970027b2, - 0xdb2e51bfe9d0696a,0x6997b05fcc0319e, - 0x88fcf317f22241e2,0x441fece3bdf81f03, - 0xab3c2fddeeaad25a,0xd527e81cad7626c3, - 0xd60b3bd56a5586f1,0x8a71e223d8d3b074, - 0x85c7056562757456,0xf6872d5667844e49, - 0xa738c6bebb12d16c,0xb428f8ac016561db, - 0xd106f86e69d785c7,0xe13336d701beba52, - 0x82a45b450226b39c,0xecc0024661173473, - 0xa34d721642b06084,0x27f002d7f95d0190, - 0xcc20ce9bd35c78a5,0x31ec038df7b441f4, - 0xff290242c83396ce,0x7e67047175a15271, - 0x9f79a169bd203e41,0xf0062c6e984d386, - 0xc75809c42c684dd1,0x52c07b78a3e60868, - 0xf92e0c3537826145,0xa7709a56ccdf8a82, - 0x9bbcc7a142b17ccb,0x88a66076400bb691, - 0xc2abf989935ddbfe,0x6acff893d00ea435, - 0xf356f7ebf83552fe,0x583f6b8c4124d43, - 0x98165af37b2153de,0xc3727a337a8b704a, - 0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c, - 0xeda2ee1c7064130c,0x1162def06f79df73, - 0x9485d4d1c63e8be7,0x8addcb5645ac2ba8, - 0xb9a74a0637ce2ee1,0x6d953e2bd7173692, - 0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437, - 0x910ab1d4db9914a0,0x1d9c9892400a22a2, - 0xb54d5e4a127f59c8,0x2503beb6d00cab4b, - 0xe2a0b5dc971f303a,0x2e44ae64840fd61d, - 0x8da471a9de737e24,0x5ceaecfed289e5d2, - 0xb10d8e1456105dad,0x7425a83e872c5f47, - 0xdd50f1996b947518,0xd12f124e28f77719, - 0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f, - 0xace73cbfdc0bfb7b,0x636cc64d1001550b, - 0xd8210befd30efa5a,0x3c47f7e05401aa4e, - 0x8714a775e3e95c78,0x65acfaec34810a71, - 0xa8d9d1535ce3b396,0x7f1839a741a14d0d, - 0xd31045a8341ca07c,0x1ede48111209a050, - 0x83ea2b892091e44d,0x934aed0aab460432, - 0xa4e4b66b68b65d60,0xf81da84d5617853f, - 0xce1de40642e3f4b9,0x36251260ab9d668e, - 0x80d2ae83e9ce78f3,0xc1d72b7c6b426019, - 0xa1075a24e4421730,0xb24cf65b8612f81f, - 0xc94930ae1d529cfc,0xdee033f26797b627, - 0xfb9b7cd9a4a7443c,0x169840ef017da3b1, - 0x9d412e0806e88aa5,0x8e1f289560ee864e, - 0xc491798a08a2ad4e,0xf1a6f2bab92a27e2, - 0xf5b5d7ec8acb58a2,0xae10af696774b1db, - 0x9991a6f3d6bf1765,0xacca6da1e0a8ef29, - 0xbff610b0cc6edd3f,0x17fd090a58d32af3, - 0xeff394dcff8a948e,0xddfc4b4cef07f5b0, - 0x95f83d0a1fb69cd9,0x4abdaf101564f98e, - 0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1, - 0xea53df5fd18d5513,0x84c86189216dc5ed, - 0x92746b9be2f8552c,0x32fd3cf5b4e49bb4, - 0xb7118682dbb66a77,0x3fbc8c33221dc2a1, - 0xe4d5e82392a40515,0xfabaf3feaa5334a, - 0x8f05b1163ba6832d,0x29cb4d87f2a7400e, - 0xb2c71d5bca9023f8,0x743e20e9ef511012, - 0xdf78e4b2bd342cf6,0x914da9246b255416, - 0x8bab8eefb6409c1a,0x1ad089b6c2f7548e, - 0xae9672aba3d0c320,0xa184ac2473b529b1, - 0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e, - 0x8865899617fb1871,0x7e2fa67c7a658892, - 0xaa7eebfb9df9de8d,0xddbb901b98feeab7, - 0xd51ea6fa85785631,0x552a74227f3ea565, - 0x8533285c936b35de,0xd53a88958f87275f, - 0xa67ff273b8460356,0x8a892abaf368f137, - 0xd01fef10a657842c,0x2d2b7569b0432d85, - 0x8213f56a67f6b29b,0x9c3b29620e29fc73, - 0xa298f2c501f45f42,0x8349f3ba91b47b8f, - 0xcb3f2f7642717713,0x241c70a936219a73, - 0xfe0efb53d30dd4d7,0xed238cd383aa0110, - 0x9ec95d1463e8a506,0xf4363804324a40aa, - 0xc67bb4597ce2ce48,0xb143c6053edcd0d5, - 0xf81aa16fdc1b81da,0xdd94b7868e94050a, - 0x9b10a4e5e9913128,0xca7cf2b4191c8326, - 0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0, - 0xf24a01a73cf2dccf,0xbc633b39673c8cec, - 0x976e41088617ca01,0xd5be0503e085d813, - 0xbd49d14aa79dbc82,0x4b2d8644d8a74e18, - 0xec9c459d51852ba2,0xddf8e7d60ed1219e, - 0x93e1ab8252f33b45,0xcabb90e5c942b503, - 0xb8da1662e7b00a17,0x3d6a751f3b936243, - 0xe7109bfba19c0c9d,0xcc512670a783ad4, - 0x906a617d450187e2,0x27fb2b80668b24c5, - 0xb484f9dc9641e9da,0xb1f9f660802dedf6, - 0xe1a63853bbd26451,0x5e7873f8a0396973, - 0x8d07e33455637eb2,0xdb0b487b6423e1e8, - 0xb049dc016abc5e5f,0x91ce1a9a3d2cda62, - 0xdc5c5301c56b75f7,0x7641a140cc7810fb, - 0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d, - 0xac2820d9623bf429,0x546345fa9fbdcd44, - 0xd732290fbacaf133,0xa97c177947ad4095, - 0x867f59a9d4bed6c0,0x49ed8eabcccc485d, - 0xa81f301449ee8c70,0x5c68f256bfff5a74, - 0xd226fc195c6a2f8c,0x73832eec6fff3111, - 0x83585d8fd9c25db7,0xc831fd53c5ff7eab, - 0xa42e74f3d032f525,0xba3e7ca8b77f5e55, - 0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb, - 0x80444b5e7aa7cf85,0x7980d163cf5b81b3, - 0xa0555e361951c366,0xd7e105bcc332621f, - 0xc86ab5c39fa63440,0x8dd9472bf3fefaa7, - 0xfa856334878fc150,0xb14f98f6f0feb951, - 0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3, - 0xc3b8358109e84f07,0xa862f80ec4700c8, - 0xf4a642e14c6262c8,0xcd27bb612758c0fa, - 0x98e7e9cccfbd7dbd,0x8038d51cb897789c, - 0xbf21e44003acdd2c,0xe0470a63e6bd56c3, - 0xeeea5d5004981478,0x1858ccfce06cac74, - 0x95527a5202df0ccb,0xf37801e0c43ebc8, - 0xbaa718e68396cffd,0xd30560258f54e6ba, - 0xe950df20247c83fd,0x47c6b82ef32a2069, - 0x91d28b7416cdd27e,0x4cdc331d57fa5441, - 0xb6472e511c81471d,0xe0133fe4adf8e952, - 0xe3d8f9e563a198e5,0x58180fddd97723a6, - 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,}; - -} // namespace internal -} // namespace simdjson -/* end file src/internal/numberparsing_tables.cpp */ -/* begin file src/internal/simdprune_tables.cpp */ -#if SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 - -#include - -namespace simdjson { // table modified and copied from -namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable -SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256] = { - 0, 2, 2, 4, 2, 4, 4, 6, 2, 4, 4, 6, 4, 6, 6, 8, 2, 4, 4, - 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 2, 4, 4, 6, 4, 6, - 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, - 8, 8, 10, 8, 10, 10, 12, 2, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, - 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, - 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 8, - 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 2, 4, 4, 6, 4, - 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, - 6, 8, 8, 10, 8, 10, 10, 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, - 10, 8, 10, 10, 12, 6, 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, - 12, 14, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, - 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 6, 8, 8, 10, - 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 8, 10, 10, 12, 10, 12, 12, - 14, 10, 12, 12, 14, 12, 14, 14, 16}; - -SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -// 256 * 8 bytes = 2kB, easily fits in cache. -SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256] = { - 0x0706050403020100, 0x0007060504030201, 0x0007060504030200, - 0x0000070605040302, 0x0007060504030100, 0x0000070605040301, - 0x0000070605040300, 0x0000000706050403, 0x0007060504020100, - 0x0000070605040201, 0x0000070605040200, 0x0000000706050402, - 0x0000070605040100, 0x0000000706050401, 0x0000000706050400, - 0x0000000007060504, 0x0007060503020100, 0x0000070605030201, - 0x0000070605030200, 0x0000000706050302, 0x0000070605030100, - 0x0000000706050301, 0x0000000706050300, 0x0000000007060503, - 0x0000070605020100, 0x0000000706050201, 0x0000000706050200, - 0x0000000007060502, 0x0000000706050100, 0x0000000007060501, - 0x0000000007060500, 0x0000000000070605, 0x0007060403020100, - 0x0000070604030201, 0x0000070604030200, 0x0000000706040302, - 0x0000070604030100, 0x0000000706040301, 0x0000000706040300, - 0x0000000007060403, 0x0000070604020100, 0x0000000706040201, - 0x0000000706040200, 0x0000000007060402, 0x0000000706040100, - 0x0000000007060401, 0x0000000007060400, 0x0000000000070604, - 0x0000070603020100, 0x0000000706030201, 0x0000000706030200, - 0x0000000007060302, 0x0000000706030100, 0x0000000007060301, - 0x0000000007060300, 0x0000000000070603, 0x0000000706020100, - 0x0000000007060201, 0x0000000007060200, 0x0000000000070602, - 0x0000000007060100, 0x0000000000070601, 0x0000000000070600, - 0x0000000000000706, 0x0007050403020100, 0x0000070504030201, - 0x0000070504030200, 0x0000000705040302, 0x0000070504030100, - 0x0000000705040301, 0x0000000705040300, 0x0000000007050403, - 0x0000070504020100, 0x0000000705040201, 0x0000000705040200, - 0x0000000007050402, 0x0000000705040100, 0x0000000007050401, - 0x0000000007050400, 0x0000000000070504, 0x0000070503020100, - 0x0000000705030201, 0x0000000705030200, 0x0000000007050302, - 0x0000000705030100, 0x0000000007050301, 0x0000000007050300, - 0x0000000000070503, 0x0000000705020100, 0x0000000007050201, - 0x0000000007050200, 0x0000000000070502, 0x0000000007050100, - 0x0000000000070501, 0x0000000000070500, 0x0000000000000705, - 0x0000070403020100, 0x0000000704030201, 0x0000000704030200, - 0x0000000007040302, 0x0000000704030100, 0x0000000007040301, - 0x0000000007040300, 0x0000000000070403, 0x0000000704020100, - 0x0000000007040201, 0x0000000007040200, 0x0000000000070402, - 0x0000000007040100, 0x0000000000070401, 0x0000000000070400, - 0x0000000000000704, 0x0000000703020100, 0x0000000007030201, - 0x0000000007030200, 0x0000000000070302, 0x0000000007030100, - 0x0000000000070301, 0x0000000000070300, 0x0000000000000703, - 0x0000000007020100, 0x0000000000070201, 0x0000000000070200, - 0x0000000000000702, 0x0000000000070100, 0x0000000000000701, - 0x0000000000000700, 0x0000000000000007, 0x0006050403020100, - 0x0000060504030201, 0x0000060504030200, 0x0000000605040302, - 0x0000060504030100, 0x0000000605040301, 0x0000000605040300, - 0x0000000006050403, 0x0000060504020100, 0x0000000605040201, - 0x0000000605040200, 0x0000000006050402, 0x0000000605040100, - 0x0000000006050401, 0x0000000006050400, 0x0000000000060504, - 0x0000060503020100, 0x0000000605030201, 0x0000000605030200, - 0x0000000006050302, 0x0000000605030100, 0x0000000006050301, - 0x0000000006050300, 0x0000000000060503, 0x0000000605020100, - 0x0000000006050201, 0x0000000006050200, 0x0000000000060502, - 0x0000000006050100, 0x0000000000060501, 0x0000000000060500, - 0x0000000000000605, 0x0000060403020100, 0x0000000604030201, - 0x0000000604030200, 0x0000000006040302, 0x0000000604030100, - 0x0000000006040301, 0x0000000006040300, 0x0000000000060403, - 0x0000000604020100, 0x0000000006040201, 0x0000000006040200, - 0x0000000000060402, 0x0000000006040100, 0x0000000000060401, - 0x0000000000060400, 0x0000000000000604, 0x0000000603020100, - 0x0000000006030201, 0x0000000006030200, 0x0000000000060302, - 0x0000000006030100, 0x0000000000060301, 0x0000000000060300, - 0x0000000000000603, 0x0000000006020100, 0x0000000000060201, - 0x0000000000060200, 0x0000000000000602, 0x0000000000060100, - 0x0000000000000601, 0x0000000000000600, 0x0000000000000006, - 0x0000050403020100, 0x0000000504030201, 0x0000000504030200, - 0x0000000005040302, 0x0000000504030100, 0x0000000005040301, - 0x0000000005040300, 0x0000000000050403, 0x0000000504020100, - 0x0000000005040201, 0x0000000005040200, 0x0000000000050402, - 0x0000000005040100, 0x0000000000050401, 0x0000000000050400, - 0x0000000000000504, 0x0000000503020100, 0x0000000005030201, - 0x0000000005030200, 0x0000000000050302, 0x0000000005030100, - 0x0000000000050301, 0x0000000000050300, 0x0000000000000503, - 0x0000000005020100, 0x0000000000050201, 0x0000000000050200, - 0x0000000000000502, 0x0000000000050100, 0x0000000000000501, - 0x0000000000000500, 0x0000000000000005, 0x0000000403020100, - 0x0000000004030201, 0x0000000004030200, 0x0000000000040302, - 0x0000000004030100, 0x0000000000040301, 0x0000000000040300, - 0x0000000000000403, 0x0000000004020100, 0x0000000000040201, - 0x0000000000040200, 0x0000000000000402, 0x0000000000040100, - 0x0000000000000401, 0x0000000000000400, 0x0000000000000004, - 0x0000000003020100, 0x0000000000030201, 0x0000000000030200, - 0x0000000000000302, 0x0000000000030100, 0x0000000000000301, - 0x0000000000000300, 0x0000000000000003, 0x0000000000020100, - 0x0000000000000201, 0x0000000000000200, 0x0000000000000002, - 0x0000000000000100, 0x0000000000000001, 0x0000000000000000, - 0x0000000000000000, -}; //static uint64_t thintable_epi8[256] - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_IMPLEMENTATION_ARM64 || SIMDJSON_IMPLEMENTATION_ICELAKE || SIMDJSON_IMPLEMENTATION_HASWELL || SIMDJSON_IMPLEMENTATION_WESTMERE || SIMDJSON_IMPLEMENTATION_PPC64 -/* end file src/internal/simdprune_tables.cpp */ -/* begin file src/implementation.cpp */ -#include - -namespace simdjson { - -bool implementation::supported_by_runtime_system() const { - uint32_t required_instruction_sets = this->required_instruction_sets(); - uint32_t supported_instruction_sets = internal::detect_supported_architectures(); - return ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets); -} - -namespace internal { - -// Static array of known implementations. We're hoping these get baked into the executable -// without requiring a static initializer. - -#if SIMDJSON_IMPLEMENTATION_ICELAKE -static const icelake::implementation* get_icelake_singleton() { - static const icelake::implementation icelake_singleton{}; - return &icelake_singleton; -} -#endif -#if SIMDJSON_IMPLEMENTATION_HASWELL -static const haswell::implementation* get_haswell_singleton() { - static const haswell::implementation haswell_singleton{}; - return &haswell_singleton; -} -#endif -#if SIMDJSON_IMPLEMENTATION_WESTMERE -static const westmere::implementation* get_westmere_singleton() { - static const westmere::implementation westmere_singleton{}; - return &westmere_singleton; -} -#endif // SIMDJSON_IMPLEMENTATION_WESTMERE -#if SIMDJSON_IMPLEMENTATION_ARM64 -static const arm64::implementation* get_arm64_singleton() { - static const arm64::implementation arm64_singleton{}; - return &arm64_singleton; -} -#endif // SIMDJSON_IMPLEMENTATION_ARM64 -#if SIMDJSON_IMPLEMENTATION_PPC64 -static const ppc64::implementation* get_ppc64_singleton() { - static const ppc64::implementation ppc64_singleton{}; - return &ppc64_singleton; -} -#endif // SIMDJSON_IMPLEMENTATION_PPC64 -#if SIMDJSON_IMPLEMENTATION_FALLBACK -static const fallback::implementation* get_fallback_singleton() { - static const fallback::implementation fallback_singleton{}; - return &fallback_singleton; -} -#endif // SIMDJSON_IMPLEMENTATION_FALLBACK - -/** - * @private Detects best supported implementation on first use, and sets it - */ -class detect_best_supported_implementation_on_first_use final : public implementation { -public: - const std::string &name() const noexcept final { return set_best()->name(); } - const std::string &description() const noexcept final { return set_best()->description(); } - uint32_t required_instruction_sets() const noexcept final { return set_best()->required_instruction_sets(); } - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t capacity, - size_t max_length, - std::unique_ptr& dst - ) const noexcept final { - return set_best()->create_dom_parser_implementation(capacity, max_length, dst); - } - simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final { - return set_best()->minify(buf, len, dst, dst_len); - } - simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override { - return set_best()->validate_utf8(buf, len); - } - simdjson_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {} -private: - const implementation *set_best() const noexcept; -}; - -static const std::initializer_list& get_available_implementation_pointers() { - static const std::initializer_list available_implementation_pointers { -#if SIMDJSON_IMPLEMENTATION_ICELAKE - get_icelake_singleton(), -#endif -#if SIMDJSON_IMPLEMENTATION_HASWELL - get_haswell_singleton(), -#endif -#if SIMDJSON_IMPLEMENTATION_WESTMERE - get_westmere_singleton(), -#endif -#if SIMDJSON_IMPLEMENTATION_ARM64 - get_arm64_singleton(), -#endif -#if SIMDJSON_IMPLEMENTATION_PPC64 - get_ppc64_singleton(), -#endif -#if SIMDJSON_IMPLEMENTATION_FALLBACK - get_fallback_singleton(), -#endif - }; // available_implementation_pointers - return available_implementation_pointers; -} - -// So we can return UNSUPPORTED_ARCHITECTURE from the parser when there is no support -class unsupported_implementation final : public implementation { -public: - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t, - size_t, - std::unique_ptr& - ) const noexcept final { - return UNSUPPORTED_ARCHITECTURE; - } - simdjson_warn_unused error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override { - return UNSUPPORTED_ARCHITECTURE; - } - simdjson_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override { - return false; // Just refuse to validate. Given that we have a fallback implementation - // it seems unlikely that unsupported_implementation will ever be used. If it is used, - // then it will flag all strings as invalid. The alternative is to return an error_code - // from which the user has to figure out whether the string is valid UTF-8... which seems - // like a lot of work just to handle the very unlikely case that we have an unsupported - // implementation. And, when it does happen (that we have an unsupported implementation), - // what are the chances that the programmer has a fallback? Given that *we* provide the - // fallback, it implies that the programmer would need a fallback for our fallback. - } - unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {} -}; - -const unsupported_implementation* get_unsupported_singleton() { - static const unsupported_implementation unsupported_singleton{}; - return &unsupported_singleton; -} - -size_t available_implementation_list::size() const noexcept { - return internal::get_available_implementation_pointers().size(); -} -const implementation * const *available_implementation_list::begin() const noexcept { - return internal::get_available_implementation_pointers().begin(); -} -const implementation * const *available_implementation_list::end() const noexcept { - return internal::get_available_implementation_pointers().end(); -} -const implementation *available_implementation_list::detect_best_supported() const noexcept { - // They are prelisted in priority order, so we just go down the list - uint32_t supported_instruction_sets = internal::detect_supported_architectures(); - for (const implementation *impl : internal::get_available_implementation_pointers()) { - uint32_t required_instruction_sets = impl->required_instruction_sets(); - if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; } - } - return get_unsupported_singleton(); // this should never happen? -} - -const implementation *detect_best_supported_implementation_on_first_use::set_best() const noexcept { - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe - char *force_implementation_name = getenv("SIMDJSON_FORCE_IMPLEMENTATION"); - SIMDJSON_POP_DISABLE_WARNINGS - - if (force_implementation_name) { - auto force_implementation = get_available_implementations()[force_implementation_name]; - if (force_implementation) { - return get_active_implementation() = force_implementation; - } else { - // Note: abort() and stderr usage within the library is forbidden. - return get_active_implementation() = get_unsupported_singleton(); - } - } - return get_active_implementation() = get_available_implementations().detect_best_supported(); -} - -} // namespace internal - -SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() { - static const internal::available_implementation_list available_implementations{}; - return available_implementations; -} - -SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation() { - static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; - static internal::atomic_ptr active_implementation{&detect_best_supported_implementation_on_first_use_singleton}; - return active_implementation; -} - -simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept { - return get_active_implementation()->minify(reinterpret_cast(buf), len, reinterpret_cast(dst), dst_len); -} -simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept { - return get_active_implementation()->validate_utf8(buf, len); -} -const implementation * builtin_implementation() { - static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)]; - assert(builtin_impl); - return builtin_impl; -} - - -} // namespace simdjson -/* end file src/implementation.cpp */ - -#if SIMDJSON_IMPLEMENTATION_ARM64 -/* begin file src/arm64/implementation.cpp */ -/* begin file include/simdjson/arm64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "arm64" -// #define SIMDJSON_IMPLEMENTATION arm64 -/* end file include/simdjson/arm64/begin.h */ - -namespace simdjson { -namespace arm64 { - -simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst -) const noexcept { - dst.reset( new (std::nothrow) dom_parser_implementation() ); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; -} - -} // namespace arm64 -} // namespace simdjson - -/* begin file include/simdjson/arm64/end.h */ -/* end file include/simdjson/arm64/end.h */ -/* end file src/arm64/implementation.cpp */ -/* begin file src/arm64/dom_parser_implementation.cpp */ -/* begin file include/simdjson/arm64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "arm64" -// #define SIMDJSON_IMPLEMENTATION arm64 -/* end file include/simdjson/arm64/begin.h */ - -// -// Stage 1 -// -namespace simdjson { -namespace arm64 { -namespace { - -using namespace simd; - -struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); - - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - - uint64_t _whitespace; - uint64_t _op; -}; - -simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // Functional programming causes trouble with Visual Studio. - // Keeping this version in comments since it is much nicer: - // auto v = in.map([&](simd8 chunk) { - // auto nib_lo = chunk & 0xf; - // auto nib_hi = chunk.shr<4>(); - // auto shuf_lo = nib_lo.lookup_16(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); - // auto shuf_hi = nib_hi.lookup_16(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); - // return shuf_lo & shuf_hi; - // }); - const simd8 table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); - const simd8 table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); - - simd8x64 v( - (in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2), - (in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2), - (in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2), - (in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2) - ); - - - // We compute whitespace and op separately. If the code later only use one or the - // other, given the fact that all functions are aggressively inlined, we can - // hope that useless computations will be omitted. This is namely case when - // minifying (we only need whitespace). *However* if we only need spaces, - // it is likely that we will still compute 'v' above with two lookup_16: one - // could do it a bit cheaper. This is in contrast with the x64 implementations - // where we can, efficiently, do the white space and structural matching - // separately. One reason for this difference is that on ARM NEON, the table - // lookups either zero or leave unchanged the characters exceeding 0xF whereas - // on x64, the equivalent instruction (pshufb) automatically applies a mask, - // ignoring the 4 most significant bits. Thus the x64 implementation is - // optimized differently. This being said, if you use this code strictly - // just for minification (or just to identify the structural characters), - // there is a small untaken optimization opportunity here. We deliberately - // do not pick it up. - - uint64_t op = simd8x64( - v.chunks[0].any_bits_set(0x7), - v.chunks[1].any_bits_set(0x7), - v.chunks[2].any_bits_set(0x7), - v.chunks[3].any_bits_set(0x7) - ).to_bitmask(); - - uint64_t whitespace = simd8x64( - v.chunks[0].any_bits_set(0x18), - v.chunks[1].any_bits_set(0x18), - v.chunks[2].any_bits_set(0x18), - v.chunks[3].any_bits_set(0x18) - ).to_bitmask(); - - return { whitespace, op }; -} - -simdjson_inline bool is_ascii(const simd8x64& input) { - simd8 bits = input.reduce_or(); - return bits.max_val() < 0x80u; -} - -simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1 >= uint8_t(0xc0u); - simd8 is_third_byte = prev2 >= uint8_t(0xe0u); - simd8 is_fourth_byte = prev3 >= uint8_t(0xf0u); - // Use ^ instead of | for is_*_byte, because ^ is commutative, and the caller is using ^ as well. - // This will work fine because we only have to report errors for cases with 0-1 lead bytes. - // Multiple lead bytes implies 2 overlapping multibyte characters, and if that happens, there is - // guaranteed to be at least *one* lead byte that is part of only 1 other multibyte character. - // The error will be detected there. - return is_second_byte ^ is_third_byte ^ is_fourth_byte; -} - -simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2 >= uint8_t(0xe0u); - simd8 is_fourth_byte = prev3 >= uint8_t(0xf0u); - return is_third_byte ^ is_fourth_byte; -} - -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson - -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ -namespace simdjson { -namespace arm64 { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#endif - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdjson_inline void check_next_input(const simd8x64& input) { - if(simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - ||(simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -/* begin file src/generic/stage1/json_structural_indexer.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -/* begin file src/generic/stage1/buf_block_reader.h */ -namespace simdjson { -namespace arm64 { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t *dst) const; - simdjson_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text_64(const uint8_t *text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdjson_unused static char * format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdjson_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdjson_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ -namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { - -struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} - - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } - - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; -}; - -// Scans blocks for string characters, storing the state necessary to do so -class json_string_scanner { -public: - simdjson_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); - - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; -}; - -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; - - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. - - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; -} - -// -// Return a mask of all string characters plus end quotes. -// -// prev_escaped is overflow saying whether the next character is escaped. -// prev_in_string is overflow saying whether we're still in a string. -// -// Backslash sequences outside of quotes will be detected in stage 2. -// -simdjson_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - const uint64_t quote = in.eq('"') & ~escaped; - - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - - // - // Check if we're still in a string at the end of the box so the next block will know - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); - - // Use ^ to turn the beginning quote off, and the end quote on. - - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block( - backslash, - escaped, - quote, - in_string - ); -} - -simdjson_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; -} - -} // namespace stage1 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ -namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { - -/** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ -struct json_block { -public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - - // Helpers - - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; -private: - // Potential structurals (i.e. disregarding strings) - - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } -}; - -/** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ -class json_scanner { -public: - json_scanner() {} - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; -}; - - -// -// Check if the current character immediately follows a matching character. -// -// For example, this checks for quotes with backslashes in front of them: -// -// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); -// -simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; -} - -simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); -} - -simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { - -class json_minifier { -public: - template - static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - -private: - simdjson_inline json_minifier(uint8_t *_dst) - : dst{_dst} - {} - template - simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); - json_scanner scanner{}; - uint8_t *dst; -}; - -simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); -} - -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; -} - -template<> -simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); -} - -template<> -simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); -} - -template -error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); - - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } - - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t * const guarded_dst{minifier.dst}; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if(to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ -namespace simdjson { -namespace arm64 { -namespace { - -/** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ -simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { - // Variant: do not count separately, just figure out depth - if(parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; -} - -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ - -namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { - -class bit_indexer { -public: - uint32_t *tail; - - simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} - - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); -#else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; -#if defined(SIMDJSON_PREFER_REVERSE_BITS) - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - - uint64_t rev_bits = reverse_bits(bits); - int cnt = static_cast(count_ones(bits)); - int i = 0; - // Do the first 8 all together - for (; i<8; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - i = 8; - for (; i<16; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - i = 16; - while (rev_bits != 0) { - int lz = leading_zeroes(rev_bits); - this->tail[i++] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - } - } - this->tail += cnt; -#else // SIMDJSON_PREFER_REVERSE_BITS - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ - - int cnt = static_cast(count_ones(bits)); - // Do the first 8 all together - for (int i=0; i<8; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - for (int i=8; i<16; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - int i = 16; - do { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - i++; - } while (i < cnt); - } - } - - this->tail += cnt; -#endif - } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER - -}; - -class json_structural_indexer { -public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - -private: - simdjson_inline json_structural_indexer(uint32_t *structural_indexes); - template - simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; -}; - -simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - -// Skip the last character if it is partial -simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left - return len; -} - -// -// PERF NOTES: -// We pipe 2 inputs through these stages: -// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load -// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. -// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. -// The output of step 1 depends entirely on this information. These functions don't quite use -// up enough CPU: the second half of the functions is highly serial, only using 1 execution core -// at a time. The second input's scans has some dependency on the first ones finishing it, but -// they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that -// to finish: utf-8 checks and generating the output from the last iteration. -// -// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all -// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough -// workout. -// -template -error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if(len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); - - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); -} - -template<> -simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index()+64); - reader.advance(); -} - -template<> -simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); -} - -simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); - checker.check_next_input(in); - indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); -} - -simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx-64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } - - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if(have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if(parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } - - parser.n_structural_indexes = new_structural_indexes; - } else if (partial == stage1_mode::streaming_final) { - if(have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -/* begin file src/generic/stage1/utf8_validator.h */ -namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ - -// -// Stage 2 -// - -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times - -namespace simdjson { -namespace arm64 { -namespace { -/// @private -namespace stringparsing { - -// begin copypasta -// These chars yield themselves: " \ / -// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab -// u not handled in this table as it's complex -static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -// handle a unicode codepoint -// write appropriate values into dest -// src will advance 6 bytes or 12 bytes -// dest will advance a variable amount (return via pointer) -// return true if the unicode codepoint was valid -// We work in little-endian then swap at write time -simdjson_warn_unused -simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, - uint8_t **dst_ptr) { - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion isn't valid; we defer the check for this to inside the - // multilingual plane check - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') { - return false; - } - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - - // if the first code point is invalid we will get here, as we will go past - // the check for being outside the Basic Multilingual plane. If we don't - // find a \u immediately afterwards we fail out anyhow, but if we do, - // this check catches both the case of the first code point being invalid - // or the second code point being invalid. - if ((code_point | code_point_2) >> 16) { - return false; - } - - code_point = - (((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000; - *src_ptr += 6; - } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - return false; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} - -/** - * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There - * must be an unescaped quote terminating the string. It returns the final output - * position as pointer. In case of error (e.g., the string has bad escaped codes), - * then null_nullptrptr is returned. It is assumed that the output buffer is large - * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + - * SIMDJSON_PADDING bytes. - */ -simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst)) { - return nullptr; - } - } else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - /* can't be reached */ - return nullptr; -} - -} // namespace stringparsing -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! -namespace simdjson { -namespace arm64 { -namespace { -namespace logger { - - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; - -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; -#else - static constexpr const bool LOG_ENABLED = false; -#endif - static constexpr const int LOG_EVENT_LEN = 20; - static constexpr const int LOG_BUFFER_LEN = 30; - static constexpr const int LOG_SMALL_BUFFER_LEN = 10; - static constexpr const int LOG_INDEX_LEN = 5; - - static int log_depth; // Not threadsafe. Log only. - - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } - - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); - } - } - - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } - - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; - - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char *type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char *type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char *type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char *error) const noexcept; - - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; -}; - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { - logger::log_start(); - - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY( visitor.visit_document_start(*this) ); - - // - // Read first value - // - { - auto value = advance(); - - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } - - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; - } - } - goto document_end; - -// -// Object parser states -// -object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY( visitor.visit_object_start(*this) ); - - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.increment_count(*this) ); - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - -object_field: - if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY( visitor.increment_count(*this) ); - { - auto key = advance(); - if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } - -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; - -// -// Array parser states -// -array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY( visitor.visit_array_start(*this) ); - SIMDJSON_TRY( visitor.increment_count(*this) ); - -array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } - -document_end: - log_end_value("document"); - SIMDJSON_TRY( visitor.visit_document_end(*this) ); - - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - - // If we didn't make it to the end, it's an error - if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } - - return SUCCESS; - -} // walk_document() - -simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) - : buf{_dom_parser.buf}, - next_structural{&_dom_parser.structural_indexes[start_structural_index]}, - dom_parser{_dom_parser} { -} - -simdjson_inline const uint8_t *json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; -} -simdjson_inline const uint8_t *json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; -} -simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural-1); -} - -simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; -} -simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); -} -simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; -} - -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); -} - -simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } -} - -simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); -} - -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); -} - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } -} -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_string(*this, value); - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } -} - -} // namespace stage2 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ -namespace simdjson { -namespace arm64 { -namespace { -namespace stage2 { - -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; - - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; - - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; - - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; - - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; - - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; - - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; - - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - -private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; -}; // struct number_writer - -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} - -simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; -} - -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); -} - -simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; -} - -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; -} - -template -simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; -} - -simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); -} - -} // namespace stage2 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ - -namespace simdjson { -namespace arm64 { -namespace { -namespace stage2 { - -struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept; - - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - - /** Next location to write to tape */ - tape_writer tape; -private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t *current_string_buf_loc; - - simdjson_inline tape_builder(dom::document &doc) noexcept; - - simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; - simdjson_inline void start_container(json_iterator &iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; - simdjson_inline void on_end_string(uint8_t *dst) noexcept; -}; // class tape_builder - -template -simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_root_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { - return visit_string(iter, key, true); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; -} - -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t *dst = on_start_string(iter); - dst = stringparsing::parse_string(value+1, dst); - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { - return visit_string(iter, value); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -// private: - -simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index+2, start); - tape.append(start_index, end); - return SUCCESS; -} - -simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; -} - -simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); -} - -simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; -} - -} // namespace stage2 -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ - -// -// Implementation-specific overrides -// -namespace simdjson { -namespace arm64 { -namespace { -namespace stage1 { - -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - // On ARM, we don't short-circuit this if there are no backslashes, because the branch gives us no - // benefit and therefore makes things worse. - // if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); -} - -} // namespace stage1 -} // unnamed namespace - -simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { - return arm64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return arm64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); -} - -simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return arm64::stage1::generic_validate_utf8(buf,len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept { - return arm64::stringparsing::parse_string(src, dst); -} - -simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); -} - -} // namespace arm64 -} // namespace simdjson - -/* begin file include/simdjson/arm64/end.h */ -/* end file include/simdjson/arm64/end.h */ -/* end file src/arm64/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_FALLBACK -/* begin file src/fallback/implementation.cpp */ -/* begin file include/simdjson/fallback/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "fallback" -// #define SIMDJSON_IMPLEMENTATION fallback -/* end file include/simdjson/fallback/begin.h */ - -namespace simdjson { -namespace fallback { - -simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst -) const noexcept { - dst.reset( new (std::nothrow) dom_parser_implementation() ); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; -} - -} // namespace fallback -} // namespace simdjson - -/* begin file include/simdjson/fallback/end.h */ -/* end file include/simdjson/fallback/end.h */ -/* end file src/fallback/implementation.cpp */ -/* begin file src/fallback/dom_parser_implementation.cpp */ -/* begin file include/simdjson/fallback/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "fallback" -// #define SIMDJSON_IMPLEMENTATION fallback -/* end file include/simdjson/fallback/begin.h */ - -// -// Stage 1 -// -/* begin file src/generic/stage1/find_next_document_index.h */ -namespace simdjson { -namespace fallback { -namespace { - -/** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ -simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { - // Variant: do not count separately, just figure out depth - if(parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; -} - -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ - -namespace simdjson { -namespace fallback { -namespace { -namespace stage1 { - -class structural_scanner { -public: - -simdjson_inline structural_scanner(dom_parser_implementation &_parser, stage1_mode _partial) - : buf{_parser.buf}, - next_structural_index{_parser.structural_indexes.get()}, - parser{_parser}, - len{static_cast(_parser.len)}, - partial{_partial} { -} - -simdjson_inline void add_structural() { - *next_structural_index = idx; - next_structural_index++; -} - -simdjson_inline bool is_continuation(uint8_t c) { - return (c & 0xc0) == 0x80; -} - -simdjson_inline void validate_utf8_character() { - // Continuation - if (simdjson_unlikely((buf[idx] & 0x40) == 0)) { - // extra continuation - error = UTF8_ERROR; - idx++; - return; - } - - // 2-byte - if ((buf[idx] & 0x20) == 0) { - // missing continuation - if (simdjson_unlikely(idx+1 > len || !is_continuation(buf[idx+1]))) { - if (idx+1 > len && is_streaming(partial)) { idx = len; return; } - error = UTF8_ERROR; - idx++; - return; - } - // overlong: 1100000_ 10______ - if (buf[idx] <= 0xc1) { error = UTF8_ERROR; } - idx += 2; - return; - } - - // 3-byte - if ((buf[idx] & 0x10) == 0) { - // missing continuation - if (simdjson_unlikely(idx+2 > len || !is_continuation(buf[idx+1]) || !is_continuation(buf[idx+2]))) { - if (idx+2 > len && is_streaming(partial)) { idx = len; return; } - error = UTF8_ERROR; - idx++; - return; - } - // overlong: 11100000 100_____ ________ - if (buf[idx] == 0xe0 && buf[idx+1] <= 0x9f) { error = UTF8_ERROR; } - // surrogates: U+D800-U+DFFF 11101101 101_____ - if (buf[idx] == 0xed && buf[idx+1] >= 0xa0) { error = UTF8_ERROR; } - idx += 3; - return; - } - - // 4-byte - // missing continuation - if (simdjson_unlikely(idx+3 > len || !is_continuation(buf[idx+1]) || !is_continuation(buf[idx+2]) || !is_continuation(buf[idx+3]))) { - if (idx+2 > len && is_streaming(partial)) { idx = len; return; } - error = UTF8_ERROR; - idx++; - return; - } - // overlong: 11110000 1000____ ________ ________ - if (buf[idx] == 0xf0 && buf[idx+1] <= 0x8f) { error = UTF8_ERROR; } - // too large: > U+10FFFF: - // 11110100 (1001|101_)____ - // 1111(1___|011_|0101) 10______ - // also includes 5, 6, 7 and 8 byte characters: - // 11111___ - if (buf[idx] == 0xf4 && buf[idx+1] >= 0x90) { error = UTF8_ERROR; } - if (buf[idx] >= 0xf5) { error = UTF8_ERROR; } - idx += 4; -} - -// Returns true if the string is unclosed. -simdjson_inline bool validate_string() { - idx++; // skip first quote - while (idx < len && buf[idx] != '"') { - if (buf[idx] == '\\') { - idx += 2; - } else if (simdjson_unlikely(buf[idx] & 0x80)) { - validate_utf8_character(); - } else { - if (buf[idx] < 0x20) { error = UNESCAPED_CHARS; } - idx++; - } - } - if (idx >= len) { return true; } - return false; -} - -simdjson_inline bool is_whitespace_or_operator(uint8_t c) { - switch (c) { - case '{': case '}': case '[': case ']': case ',': case ':': - case ' ': case '\r': case '\n': case '\t': - return true; - default: - return false; - } -} - -// -// Parse the entire input in STEP_SIZE-byte chunks. -// -simdjson_inline error_code scan() { - bool unclosed_string = false; - for (;idx 0) { - if(parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } - parser.n_structural_indexes = new_structural_indexes; - } else if(partial == stage1_mode::streaming_final) { - if(unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (parser.n_structural_indexes == 0) { return EMPTY; } - } else if(unclosed_string) { error = UNCLOSED_STRING; } - return error; -} - -private: - const uint8_t *buf; - uint32_t *next_structural_index; - dom_parser_implementation &parser; - uint32_t len; - uint32_t idx{0}; - error_code error{SUCCESS}; - stage1_mode partial; -}; // structural_scanner - -} // namespace stage1 -} // unnamed namespace - -simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode partial) noexcept { - this->buf = _buf; - this->len = _len; - stage1::structural_scanner scanner(*this, partial); - return scanner.scan(); -} - -// big table for the minifier -static uint8_t jump_table[256 * 3] = { - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, - 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, - 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, - 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, - 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, -}; - -simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { - size_t i = 0, pos = 0; - uint8_t quote = 0; - uint8_t nonescape = 1; - - while (i < len) { - unsigned char c = buf[i]; - uint8_t *meta = jump_table + 3 * c; - - quote = quote ^ (meta[0] & nonescape); - dst[pos] = c; - pos += meta[2] | quote; - - i += 1; - nonescape = uint8_t(~nonescape) | (meta[1]); - } - dst_len = pos; // we intentionally do not work with a reference - // for fear of aliasing - return quote ? UNCLOSED_STRING : SUCCESS; -} - -// credit: based on code from Google Fuchsia (Apache Licensed) -simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - const uint8_t *data = reinterpret_cast(buf); - uint64_t pos = 0; - uint32_t code_point = 0; - while (pos < len) { - // check of the next 8 bytes are ascii. - uint64_t next_pos = pos + 16; - if (next_pos <= len) { // if it is safe to read 8 more bytes, check that they are ascii - uint64_t v1; - memcpy(&v1, data + pos, sizeof(uint64_t)); - uint64_t v2; - memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); - uint64_t v{v1 | v2}; - if ((v & 0x8080808080808080) == 0) { - pos = next_pos; - continue; - } - } - unsigned char byte = data[pos]; - if (byte < 0x80) { - pos++; - continue; - } else if ((byte & 0xe0) == 0xc0) { - next_pos = pos + 2; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0xc0) != 0x80) { return false; } - // range check - code_point = (byte & 0x1f) << 6 | (data[pos + 1] & 0x3f); - if (code_point < 0x80 || 0x7ff < code_point) { return false; } - } else if ((byte & 0xf0) == 0xe0) { - next_pos = pos + 3; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0xc0) != 0x80) { return false; } - if ((data[pos + 2] & 0xc0) != 0x80) { return false; } - // range check - code_point = (byte & 0x0f) << 12 | - (data[pos + 1] & 0x3f) << 6 | - (data[pos + 2] & 0x3f); - if (code_point < 0x800 || 0xffff < code_point || - (0xd7ff < code_point && code_point < 0xe000)) { - return false; - } - } else if ((byte & 0xf8) == 0xf0) { // 0b11110000 - next_pos = pos + 4; - if (next_pos > len) { return false; } - if ((data[pos + 1] & 0xc0) != 0x80) { return false; } - if ((data[pos + 2] & 0xc0) != 0x80) { return false; } - if ((data[pos + 3] & 0xc0) != 0x80) { return false; } - // range check - code_point = - (byte & 0x07) << 18 | (data[pos + 1] & 0x3f) << 12 | - (data[pos + 2] & 0x3f) << 6 | (data[pos + 3] & 0x3f); - if (code_point <= 0xffff || 0x10ffff < code_point) { return false; } - } else { - // we may have a continuation - return false; - } - pos = next_pos; - } - return true; -} - -} // namespace fallback -} // namespace simdjson - -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times - -namespace simdjson { -namespace fallback { -namespace { -/// @private -namespace stringparsing { - -// begin copypasta -// These chars yield themselves: " \ / -// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab -// u not handled in this table as it's complex -static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -// handle a unicode codepoint -// write appropriate values into dest -// src will advance 6 bytes or 12 bytes -// dest will advance a variable amount (return via pointer) -// return true if the unicode codepoint was valid -// We work in little-endian then swap at write time -simdjson_warn_unused -simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, - uint8_t **dst_ptr) { - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion isn't valid; we defer the check for this to inside the - // multilingual plane check - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') { - return false; - } - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - - // if the first code point is invalid we will get here, as we will go past - // the check for being outside the Basic Multilingual plane. If we don't - // find a \u immediately afterwards we fail out anyhow, but if we do, - // this check catches both the case of the first code point being invalid - // or the second code point being invalid. - if ((code_point | code_point_2) >> 16) { - return false; - } - - code_point = - (((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000; - *src_ptr += 6; - } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - return false; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} - -/** - * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There - * must be an unescaped quote terminating the string. It returns the final output - * position as pointer. In case of error (e.g., the string has bad escaped codes), - * then null_nullptrptr is returned. It is assumed that the output buffer is large - * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + - * SIMDJSON_PADDING bytes. - */ -simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst)) { - return nullptr; - } - } else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - /* can't be reached */ - return nullptr; -} - -} // namespace stringparsing -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! -namespace simdjson { -namespace fallback { -namespace { -namespace logger { - - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; - -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; -#else - static constexpr const bool LOG_ENABLED = false; -#endif - static constexpr const int LOG_EVENT_LEN = 20; - static constexpr const int LOG_BUFFER_LEN = 30; - static constexpr const int LOG_SMALL_BUFFER_LEN = 10; - static constexpr const int LOG_INDEX_LEN = 5; - - static int log_depth; // Not threadsafe. Log only. - - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } - - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); - } - } - - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } - - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; - - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char *type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char *type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char *type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char *error) const noexcept; - - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; -}; - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { - logger::log_start(); - - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY( visitor.visit_document_start(*this) ); - - // - // Read first value - // - { - auto value = advance(); - - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } - - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; - } - } - goto document_end; - -// -// Object parser states -// -object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY( visitor.visit_object_start(*this) ); - - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.increment_count(*this) ); - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - -object_field: - if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY( visitor.increment_count(*this) ); - { - auto key = advance(); - if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } - -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; - -// -// Array parser states -// -array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY( visitor.visit_array_start(*this) ); - SIMDJSON_TRY( visitor.increment_count(*this) ); - -array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } - -document_end: - log_end_value("document"); - SIMDJSON_TRY( visitor.visit_document_end(*this) ); - - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - - // If we didn't make it to the end, it's an error - if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } - - return SUCCESS; - -} // walk_document() - -simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) - : buf{_dom_parser.buf}, - next_structural{&_dom_parser.structural_indexes[start_structural_index]}, - dom_parser{_dom_parser} { -} - -simdjson_inline const uint8_t *json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; -} -simdjson_inline const uint8_t *json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; -} -simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural-1); -} - -simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; -} -simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); -} -simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; -} - -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); -} - -simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } -} - -simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); -} - -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); -} - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } -} -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_string(*this, value); - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } -} - -} // namespace stage2 -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ -namespace simdjson { -namespace fallback { -namespace { -namespace stage2 { - -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; - - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; - - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; - - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; - - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; - - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; - - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; - - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - -private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; -}; // struct number_writer - -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} - -simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; -} - -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); -} - -simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; -} - -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; -} - -template -simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; -} - -simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); -} - -} // namespace stage2 -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ - -namespace simdjson { -namespace fallback { -namespace { -namespace stage2 { - -struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept; - - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - - /** Next location to write to tape */ - tape_writer tape; -private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t *current_string_buf_loc; - - simdjson_inline tape_builder(dom::document &doc) noexcept; - - simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; - simdjson_inline void start_container(json_iterator &iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; - simdjson_inline void on_end_string(uint8_t *dst) noexcept; -}; // class tape_builder - -template -simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_root_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { - return visit_string(iter, key, true); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; -} - -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t *dst = on_start_string(iter); - dst = stringparsing::parse_string(value+1, dst); - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { - return visit_string(iter, value); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -// private: - -simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index+2, start); - tape.append(start_index, end); - return SUCCESS; -} - -simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; -} - -simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); -} - -simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; -} - -} // namespace stage2 -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ - -namespace simdjson { -namespace fallback { - -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept { - return fallback::stringparsing::parse_string(src, dst); -} - -simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); -} - -} // namespace fallback -} // namespace simdjson - -/* begin file include/simdjson/fallback/end.h */ -/* end file include/simdjson/fallback/end.h */ -/* end file src/fallback/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_ICELAKE -/* begin file src/icelake/implementation.cpp */ -/* begin file include/simdjson/icelake/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "icelake" -// #define SIMDJSON_IMPLEMENTATION icelake -SIMDJSON_TARGET_ICELAKE -/* end file include/simdjson/icelake/begin.h */ - -namespace simdjson { -namespace icelake { - -simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst -) const noexcept { - dst.reset( new (std::nothrow) dom_parser_implementation() ); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; -} - -} // namespace icelake -} // namespace simdjson - -/* begin file include/simdjson/icelake/end.h */ -SIMDJSON_UNTARGET_ICELAKE -/* end file include/simdjson/icelake/end.h */ - -/* end file src/icelake/implementation.cpp */ -/* begin file src/icelake/dom_parser_implementation.cpp */ -/* begin file include/simdjson/icelake/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "icelake" -// #define SIMDJSON_IMPLEMENTATION icelake -SIMDJSON_TARGET_ICELAKE -/* end file include/simdjson/icelake/begin.h */ - -// -// Stage 1 -// - -namespace simdjson { -namespace icelake { -namespace { - -using namespace simd; - -struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); - // ASCII white-space ('\r','\n','\t',' ') - simdjson_inline uint64_t whitespace() const noexcept; - // non-quote structural characters (comma, colon, braces, brackets) - simdjson_inline uint64_t op() const noexcept; - // neither a structural character nor a white-space, so letters, numbers and quotes - simdjson_inline uint64_t scalar() const noexcept; - - uint64_t _whitespace; // ASCII white-space ('\r','\n','\t',' ') - uint64_t _op; // structural characters (comma, colon, braces, brackets but not quotes) -}; - -simdjson_inline uint64_t json_character_block::whitespace() const noexcept { return _whitespace; } -simdjson_inline uint64_t json_character_block::op() const noexcept { return _op; } -simdjson_inline uint64_t json_character_block::scalar() const noexcept { return ~(op() | whitespace()); } - -// This identifies structural characters (comma, colon, braces, brackets), -// and ASCII white-space ('\r','\n','\t',' '). -simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why - // we can't use the generic lookup_16. - const auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); - - // The 6 operators (:,[]{}) have these values: - // - // , 2C - // : 3A - // [ 5B - // { 7B - // ] 5D - // } 7D - // - // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. - // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then - // match it (against | 0x20). - // - // To prevent recognizing other characters, everything else gets compared with 0, which cannot - // match due to the | 0x20. - // - // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , - // and :. This gets caught in stage 2, which checks the actual character to ensure the right - // operators are in the right places. - const auto op_table = simd8::repeat_16( - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B - ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D - ); - - // We compute whitespace and op separately. If later code only uses one or the - // other, given the fact that all functions are aggressively inlined, we can - // hope that useless computations will be omitted. This is namely case when - // minifying (we only need whitespace). - - const uint64_t whitespace = in.eq({ - _mm512_shuffle_epi8(whitespace_table, in.chunks[0]) - }); - // Turn [ and ] into { and } - const simd8x64 curlified{ - in.chunks[0] | 0x20 - }; - const uint64_t op = curlified.eq({ - _mm512_shuffle_epi8(op_table, in.chunks[0]) - }); - - return { whitespace, op }; -} - -simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); -} - -simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); -} - -simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_third_byte | is_fourth_byte) > int8_t(0); -} - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson - -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ -namespace simdjson { -namespace icelake { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#endif - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdjson_inline void check_next_input(const simd8x64& input) { - if(simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - ||(simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -// defining SIMDJSON_CUSTOM_BIT_INDEXER allows us to provide our own bit_indexer::write -#define SIMDJSON_CUSTOM_BIT_INDEXER -/* begin file src/generic/stage1/json_structural_indexer.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -/* begin file src/generic/stage1/buf_block_reader.h */ -namespace simdjson { -namespace icelake { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t *dst) const; - simdjson_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text_64(const uint8_t *text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdjson_unused static char * format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdjson_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdjson_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ -namespace simdjson { -namespace icelake { -namespace { -namespace stage1 { - -struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} - - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } - - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; -}; - -// Scans blocks for string characters, storing the state necessary to do so -class json_string_scanner { -public: - simdjson_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); - - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; -}; - -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; - - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. - - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; -} - -// -// Return a mask of all string characters plus end quotes. -// -// prev_escaped is overflow saying whether the next character is escaped. -// prev_in_string is overflow saying whether we're still in a string. -// -// Backslash sequences outside of quotes will be detected in stage 2. -// -simdjson_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - const uint64_t quote = in.eq('"') & ~escaped; - - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - - // - // Check if we're still in a string at the end of the box so the next block will know - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); - - // Use ^ to turn the beginning quote off, and the end quote on. - - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block( - backslash, - escaped, - quote, - in_string - ); -} - -simdjson_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; -} - -} // namespace stage1 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ -namespace simdjson { -namespace icelake { -namespace { -namespace stage1 { - -/** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ -struct json_block { -public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - - // Helpers - - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; -private: - // Potential structurals (i.e. disregarding strings) - - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } -}; - -/** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ -class json_scanner { -public: - json_scanner() {} - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; -}; - - -// -// Check if the current character immediately follows a matching character. -// -// For example, this checks for quotes with backslashes in front of them: -// -// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); -// -simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; -} - -simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); -} - -simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -namespace simdjson { -namespace icelake { -namespace { -namespace stage1 { - -class json_minifier { -public: - template - static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - -private: - simdjson_inline json_minifier(uint8_t *_dst) - : dst{_dst} - {} - template - simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); - json_scanner scanner{}; - uint8_t *dst; -}; - -simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); -} - -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; -} - -template<> -simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); -} - -template<> -simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); -} - -template -error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); - - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } - - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t * const guarded_dst{minifier.dst}; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if(to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ -namespace simdjson { -namespace icelake { -namespace { - -/** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ -simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { - // Variant: do not count separately, just figure out depth - if(parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; -} - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ - -namespace simdjson { -namespace icelake { -namespace { -namespace stage1 { - -class bit_indexer { -public: - uint32_t *tail; - - simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} - - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); -#else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; -#if defined(SIMDJSON_PREFER_REVERSE_BITS) - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - - uint64_t rev_bits = reverse_bits(bits); - int cnt = static_cast(count_ones(bits)); - int i = 0; - // Do the first 8 all together - for (; i<8; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - i = 8; - for (; i<16; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - i = 16; - while (rev_bits != 0) { - int lz = leading_zeroes(rev_bits); - this->tail[i++] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - } - } - this->tail += cnt; -#else // SIMDJSON_PREFER_REVERSE_BITS - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ - - int cnt = static_cast(count_ones(bits)); - // Do the first 8 all together - for (int i=0; i<8; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - for (int i=8; i<16; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - int i = 16; - do { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - i++; - } while (i < cnt); - } - } - - this->tail += cnt; -#endif - } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER - -}; - -class json_structural_indexer { -public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - -private: - simdjson_inline json_structural_indexer(uint32_t *structural_indexes); - template - simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; -}; - -simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - -// Skip the last character if it is partial -simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left - return len; -} - -// -// PERF NOTES: -// We pipe 2 inputs through these stages: -// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load -// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. -// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. -// The output of step 1 depends entirely on this information. These functions don't quite use -// up enough CPU: the second half of the functions is highly serial, only using 1 execution core -// at a time. The second input's scans has some dependency on the first ones finishing it, but -// they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that -// to finish: utf-8 checks and generating the output from the last iteration. -// -// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all -// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough -// workout. -// -template -error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if(len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); - - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); -} - -template<> -simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index()+64); - reader.advance(); -} - -template<> -simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); -} - -simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); - checker.check_next_input(in); - indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); -} - -simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx-64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } - - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if(have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if(parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } - - parser.n_structural_indexes = new_structural_indexes; - } else if (partial == stage1_mode::streaming_final) { - if(have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -// We must not forget to undefine it now: -#undef SIMDJSON_CUSTOM_BIT_INDEXER - -/** - * We provide a custom version of bit_indexer::write using - * naked intrinsics. - * TODO: make this code more elegant. - */ -// Under GCC 12, the intrinsic _mm512_extracti32x4_epi32 may generate 'maybe uninitialized'. -// as a workaround, we disable warnings within the following function. -SIMDJSON_PUSH_DISABLE_ALL_WARNINGS -namespace simdjson { namespace icelake { namespace { namespace stage1 { -simdjson_inline void bit_indexer::write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) { return; } - - const __m512i indexes = _mm512_maskz_compress_epi8(bits, _mm512_set_epi32( - 0x3f3e3d3c, 0x3b3a3938, 0x37363534, 0x33323130, - 0x2f2e2d2c, 0x2b2a2928, 0x27262524, 0x23222120, - 0x1f1e1d1c, 0x1b1a1918, 0x17161514, 0x13121110, - 0x0f0e0d0c, 0x0b0a0908, 0x07060504, 0x03020100 - )); - const __m512i start_index = _mm512_set1_epi32(idx); - - const auto count = count_ones(bits); - __m512i t0 = _mm512_cvtepu8_epi32(_mm512_castsi512_si128(indexes)); - _mm512_storeu_si512(this->tail, _mm512_add_epi32(t0, start_index)); - - if(count > 16) { - const __m512i t1 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 1)); - _mm512_storeu_si512(this->tail + 16, _mm512_add_epi32(t1, start_index)); - if(count > 32) { - const __m512i t2 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 2)); - _mm512_storeu_si512(this->tail + 32, _mm512_add_epi32(t2, start_index)); - if(count > 48) { - const __m512i t3 = _mm512_cvtepu8_epi32(_mm512_extracti32x4_epi32(indexes, 3)); - _mm512_storeu_si512(this->tail + 48, _mm512_add_epi32(t3, start_index)); - } - } - } - this->tail += count; -} -}}}} -SIMDJSON_POP_DISABLE_WARNINGS - -/* begin file src/generic/stage1/utf8_validator.h */ -namespace simdjson { -namespace icelake { -namespace { -namespace stage1 { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ - -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times - -namespace simdjson { -namespace icelake { -namespace { -/// @private -namespace stringparsing { - -// begin copypasta -// These chars yield themselves: " \ / -// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab -// u not handled in this table as it's complex -static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -// handle a unicode codepoint -// write appropriate values into dest -// src will advance 6 bytes or 12 bytes -// dest will advance a variable amount (return via pointer) -// return true if the unicode codepoint was valid -// We work in little-endian then swap at write time -simdjson_warn_unused -simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, - uint8_t **dst_ptr) { - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion isn't valid; we defer the check for this to inside the - // multilingual plane check - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') { - return false; - } - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - - // if the first code point is invalid we will get here, as we will go past - // the check for being outside the Basic Multilingual plane. If we don't - // find a \u immediately afterwards we fail out anyhow, but if we do, - // this check catches both the case of the first code point being invalid - // or the second code point being invalid. - if ((code_point | code_point_2) >> 16) { - return false; - } - - code_point = - (((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000; - *src_ptr += 6; - } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - return false; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} - -/** - * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There - * must be an unescaped quote terminating the string. It returns the final output - * position as pointer. In case of error (e.g., the string has bad escaped codes), - * then null_nullptrptr is returned. It is assumed that the output buffer is large - * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + - * SIMDJSON_PADDING bytes. - */ -simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst)) { - return nullptr; - } - } else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - /* can't be reached */ - return nullptr; -} - -} // namespace stringparsing -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! -namespace simdjson { -namespace icelake { -namespace { -namespace logger { - - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; - -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; -#else - static constexpr const bool LOG_ENABLED = false; -#endif - static constexpr const int LOG_EVENT_LEN = 20; - static constexpr const int LOG_BUFFER_LEN = 30; - static constexpr const int LOG_SMALL_BUFFER_LEN = 10; - static constexpr const int LOG_INDEX_LEN = 5; - - static int log_depth; // Not threadsafe. Log only. - - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } - - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); - } - } - - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } - - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; - - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char *type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char *type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char *type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char *error) const noexcept; - - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; -}; - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { - logger::log_start(); - - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY( visitor.visit_document_start(*this) ); - - // - // Read first value - // - { - auto value = advance(); - - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } - - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; - } - } - goto document_end; - -// -// Object parser states -// -object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY( visitor.visit_object_start(*this) ); - - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.increment_count(*this) ); - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - -object_field: - if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY( visitor.increment_count(*this) ); - { - auto key = advance(); - if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } - -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; - -// -// Array parser states -// -array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY( visitor.visit_array_start(*this) ); - SIMDJSON_TRY( visitor.increment_count(*this) ); - -array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } - -document_end: - log_end_value("document"); - SIMDJSON_TRY( visitor.visit_document_end(*this) ); - - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - - // If we didn't make it to the end, it's an error - if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } - - return SUCCESS; - -} // walk_document() - -simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) - : buf{_dom_parser.buf}, - next_structural{&_dom_parser.structural_indexes[start_structural_index]}, - dom_parser{_dom_parser} { -} - -simdjson_inline const uint8_t *json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; -} -simdjson_inline const uint8_t *json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; -} -simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural-1); -} - -simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; -} -simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); -} -simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; -} - -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); -} - -simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } -} - -simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); -} - -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); -} - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } -} -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_string(*this, value); - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } -} - -} // namespace stage2 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ -namespace simdjson { -namespace icelake { -namespace { -namespace stage2 { - -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; - - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; - - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; - - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; - - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; - - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; - - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; - - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - -private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; -}; // struct number_writer - -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} - -simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; -} - -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); -} - -simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; -} - -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; -} - -template -simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; -} - -simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); -} - -} // namespace stage2 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ - -namespace simdjson { -namespace icelake { -namespace { -namespace stage2 { - -struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept; - - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - - /** Next location to write to tape */ - tape_writer tape; -private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t *current_string_buf_loc; - - simdjson_inline tape_builder(dom::document &doc) noexcept; - - simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; - simdjson_inline void start_container(json_iterator &iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; - simdjson_inline void on_end_string(uint8_t *dst) noexcept; -}; // class tape_builder - -template -simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_root_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { - return visit_string(iter, key, true); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; -} - -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t *dst = on_start_string(iter); - dst = stringparsing::parse_string(value+1, dst); - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { - return visit_string(iter, value); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -// private: - -simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index+2, start); - tape.append(start_index, end); - return SUCCESS; -} - -simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; -} - -simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); -} - -simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; -} - -} // namespace stage2 -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ - -// -// Implementation-specific overrides -// -namespace simdjson { -namespace icelake { -namespace { -namespace stage1 { - -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); -} - -} // namespace stage1 -} // unnamed namespace - -simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { - return icelake::stage1::json_minifier::minify<128>(buf, len, dst, dst_len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return icelake::stage1::json_structural_indexer::index<128>(_buf, _len, *this, streaming); -} - -simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return icelake::stage1::generic_validate_utf8(buf,len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept { - return icelake::stringparsing::parse_string(src, dst); -} - -simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); -} - -} // namespace icelake -} // namespace simdjson - -/* begin file include/simdjson/icelake/end.h */ -SIMDJSON_UNTARGET_ICELAKE -/* end file include/simdjson/icelake/end.h */ -/* end file src/icelake/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_HASWELL -/* begin file src/haswell/implementation.cpp */ -/* begin file include/simdjson/haswell/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "haswell" -// #define SIMDJSON_IMPLEMENTATION haswell -SIMDJSON_TARGET_HASWELL -/* end file include/simdjson/haswell/begin.h */ - -namespace simdjson { -namespace haswell { - -simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst -) const noexcept { - dst.reset( new (std::nothrow) dom_parser_implementation() ); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; -} - -} // namespace haswell -} // namespace simdjson - -/* begin file include/simdjson/haswell/end.h */ -SIMDJSON_UNTARGET_HASWELL -/* end file include/simdjson/haswell/end.h */ - -/* end file src/haswell/implementation.cpp */ -/* begin file src/haswell/dom_parser_implementation.cpp */ -/* begin file include/simdjson/haswell/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "haswell" -// #define SIMDJSON_IMPLEMENTATION haswell -SIMDJSON_TARGET_HASWELL -/* end file include/simdjson/haswell/begin.h */ - -// -// Stage 1 -// - -namespace simdjson { -namespace haswell { -namespace { - -using namespace simd; - -struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); - // ASCII white-space ('\r','\n','\t',' ') - simdjson_inline uint64_t whitespace() const noexcept; - // non-quote structural characters (comma, colon, braces, brackets) - simdjson_inline uint64_t op() const noexcept; - // neither a structural character nor a white-space, so letters, numbers and quotes - simdjson_inline uint64_t scalar() const noexcept; - - uint64_t _whitespace; // ASCII white-space ('\r','\n','\t',' ') - uint64_t _op; // structural characters (comma, colon, braces, brackets but not quotes) -}; - -simdjson_inline uint64_t json_character_block::whitespace() const noexcept { return _whitespace; } -simdjson_inline uint64_t json_character_block::op() const noexcept { return _op; } -simdjson_inline uint64_t json_character_block::scalar() const noexcept { return ~(op() | whitespace()); } - -// This identifies structural characters (comma, colon, braces, brackets), -// and ASCII white-space ('\r','\n','\t',' '). -simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why - // we can't use the generic lookup_16. - const auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); - - // The 6 operators (:,[]{}) have these values: - // - // , 2C - // : 3A - // [ 5B - // { 7B - // ] 5D - // } 7D - // - // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. - // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then - // match it (against | 0x20). - // - // To prevent recognizing other characters, everything else gets compared with 0, which cannot - // match due to the | 0x20. - // - // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , - // and :. This gets caught in stage 2, which checks the actual character to ensure the right - // operators are in the right places. - const auto op_table = simd8::repeat_16( - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B - ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D - ); - - // We compute whitespace and op separately. If later code only uses one or the - // other, given the fact that all functions are aggressively inlined, we can - // hope that useless computations will be omitted. This is namely case when - // minifying (we only need whitespace). - - const uint64_t whitespace = in.eq({ - _mm256_shuffle_epi8(whitespace_table, in.chunks[0]), - _mm256_shuffle_epi8(whitespace_table, in.chunks[1]) - }); - // Turn [ and ] into { and } - const simd8x64 curlified{ - in.chunks[0] | 0x20, - in.chunks[1] | 0x20 - }; - const uint64_t op = curlified.eq({ - _mm256_shuffle_epi8(op_table, in.chunks[0]), - _mm256_shuffle_epi8(op_table, in.chunks[1]) - }); - - return { whitespace, op }; -} - -simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); -} - -simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); -} - -simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_third_byte | is_fourth_byte) > int8_t(0); -} - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson - -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ -namespace simdjson { -namespace haswell { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#endif - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdjson_inline void check_next_input(const simd8x64& input) { - if(simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - ||(simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -/* begin file src/generic/stage1/json_structural_indexer.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -/* begin file src/generic/stage1/buf_block_reader.h */ -namespace simdjson { -namespace haswell { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t *dst) const; - simdjson_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text_64(const uint8_t *text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdjson_unused static char * format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdjson_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdjson_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ -namespace simdjson { -namespace haswell { -namespace { -namespace stage1 { - -struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} - - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } - - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; -}; - -// Scans blocks for string characters, storing the state necessary to do so -class json_string_scanner { -public: - simdjson_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); - - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; -}; - -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; - - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. - - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; -} - -// -// Return a mask of all string characters plus end quotes. -// -// prev_escaped is overflow saying whether the next character is escaped. -// prev_in_string is overflow saying whether we're still in a string. -// -// Backslash sequences outside of quotes will be detected in stage 2. -// -simdjson_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - const uint64_t quote = in.eq('"') & ~escaped; - - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - - // - // Check if we're still in a string at the end of the box so the next block will know - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); - - // Use ^ to turn the beginning quote off, and the end quote on. - - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block( - backslash, - escaped, - quote, - in_string - ); -} - -simdjson_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; -} - -} // namespace stage1 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ -namespace simdjson { -namespace haswell { -namespace { -namespace stage1 { - -/** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ -struct json_block { -public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - - // Helpers - - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; -private: - // Potential structurals (i.e. disregarding strings) - - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } -}; - -/** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ -class json_scanner { -public: - json_scanner() {} - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; -}; - - -// -// Check if the current character immediately follows a matching character. -// -// For example, this checks for quotes with backslashes in front of them: -// -// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); -// -simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; -} - -simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); -} - -simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -namespace simdjson { -namespace haswell { -namespace { -namespace stage1 { - -class json_minifier { -public: - template - static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - -private: - simdjson_inline json_minifier(uint8_t *_dst) - : dst{_dst} - {} - template - simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); - json_scanner scanner{}; - uint8_t *dst; -}; - -simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); -} - -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; -} - -template<> -simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); -} - -template<> -simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); -} - -template -error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); - - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } - - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t * const guarded_dst{minifier.dst}; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if(to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ -namespace simdjson { -namespace haswell { -namespace { - -/** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ -simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { - // Variant: do not count separately, just figure out depth - if(parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; -} - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ - -namespace simdjson { -namespace haswell { -namespace { -namespace stage1 { - -class bit_indexer { -public: - uint32_t *tail; - - simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} - - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); -#else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; -#if defined(SIMDJSON_PREFER_REVERSE_BITS) - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - - uint64_t rev_bits = reverse_bits(bits); - int cnt = static_cast(count_ones(bits)); - int i = 0; - // Do the first 8 all together - for (; i<8; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - i = 8; - for (; i<16; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - i = 16; - while (rev_bits != 0) { - int lz = leading_zeroes(rev_bits); - this->tail[i++] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - } - } - this->tail += cnt; -#else // SIMDJSON_PREFER_REVERSE_BITS - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ - - int cnt = static_cast(count_ones(bits)); - // Do the first 8 all together - for (int i=0; i<8; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - for (int i=8; i<16; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - int i = 16; - do { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - i++; - } while (i < cnt); - } - } - - this->tail += cnt; -#endif - } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER - -}; - -class json_structural_indexer { -public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - -private: - simdjson_inline json_structural_indexer(uint32_t *structural_indexes); - template - simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; -}; - -simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - -// Skip the last character if it is partial -simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left - return len; -} - -// -// PERF NOTES: -// We pipe 2 inputs through these stages: -// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load -// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. -// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. -// The output of step 1 depends entirely on this information. These functions don't quite use -// up enough CPU: the second half of the functions is highly serial, only using 1 execution core -// at a time. The second input's scans has some dependency on the first ones finishing it, but -// they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that -// to finish: utf-8 checks and generating the output from the last iteration. -// -// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all -// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough -// workout. -// -template -error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if(len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); - - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); -} - -template<> -simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index()+64); - reader.advance(); -} - -template<> -simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); -} - -simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); - checker.check_next_input(in); - indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); -} - -simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx-64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } - - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if(have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if(parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } - - parser.n_structural_indexes = new_structural_indexes; - } else if (partial == stage1_mode::streaming_final) { - if(have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -/* begin file src/generic/stage1/utf8_validator.h */ -namespace simdjson { -namespace haswell { -namespace { -namespace stage1 { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ - -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times - -namespace simdjson { -namespace haswell { -namespace { -/// @private -namespace stringparsing { - -// begin copypasta -// These chars yield themselves: " \ / -// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab -// u not handled in this table as it's complex -static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -// handle a unicode codepoint -// write appropriate values into dest -// src will advance 6 bytes or 12 bytes -// dest will advance a variable amount (return via pointer) -// return true if the unicode codepoint was valid -// We work in little-endian then swap at write time -simdjson_warn_unused -simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, - uint8_t **dst_ptr) { - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion isn't valid; we defer the check for this to inside the - // multilingual plane check - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') { - return false; - } - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - - // if the first code point is invalid we will get here, as we will go past - // the check for being outside the Basic Multilingual plane. If we don't - // find a \u immediately afterwards we fail out anyhow, but if we do, - // this check catches both the case of the first code point being invalid - // or the second code point being invalid. - if ((code_point | code_point_2) >> 16) { - return false; - } - - code_point = - (((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000; - *src_ptr += 6; - } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - return false; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} - -/** - * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There - * must be an unescaped quote terminating the string. It returns the final output - * position as pointer. In case of error (e.g., the string has bad escaped codes), - * then null_nullptrptr is returned. It is assumed that the output buffer is large - * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + - * SIMDJSON_PADDING bytes. - */ -simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst)) { - return nullptr; - } - } else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - /* can't be reached */ - return nullptr; -} - -} // namespace stringparsing -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! -namespace simdjson { -namespace haswell { -namespace { -namespace logger { - - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; - -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; -#else - static constexpr const bool LOG_ENABLED = false; -#endif - static constexpr const int LOG_EVENT_LEN = 20; - static constexpr const int LOG_BUFFER_LEN = 30; - static constexpr const int LOG_SMALL_BUFFER_LEN = 10; - static constexpr const int LOG_INDEX_LEN = 5; - - static int log_depth; // Not threadsafe. Log only. - - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } - - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); - } - } - - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } - - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; - - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char *type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char *type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char *type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char *error) const noexcept; - - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; -}; - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { - logger::log_start(); - - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY( visitor.visit_document_start(*this) ); - - // - // Read first value - // - { - auto value = advance(); - - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } - - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; - } - } - goto document_end; - -// -// Object parser states -// -object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY( visitor.visit_object_start(*this) ); - - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.increment_count(*this) ); - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - -object_field: - if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY( visitor.increment_count(*this) ); - { - auto key = advance(); - if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } - -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; - -// -// Array parser states -// -array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY( visitor.visit_array_start(*this) ); - SIMDJSON_TRY( visitor.increment_count(*this) ); - -array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } - -document_end: - log_end_value("document"); - SIMDJSON_TRY( visitor.visit_document_end(*this) ); - - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - - // If we didn't make it to the end, it's an error - if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } - - return SUCCESS; - -} // walk_document() - -simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) - : buf{_dom_parser.buf}, - next_structural{&_dom_parser.structural_indexes[start_structural_index]}, - dom_parser{_dom_parser} { -} - -simdjson_inline const uint8_t *json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; -} -simdjson_inline const uint8_t *json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; -} -simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural-1); -} - -simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; -} -simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); -} -simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; -} - -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); -} - -simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } -} - -simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); -} - -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); -} - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } -} -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_string(*this, value); - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } -} - -} // namespace stage2 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ -namespace simdjson { -namespace haswell { -namespace { -namespace stage2 { - -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; - - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; - - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; - - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; - - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; - - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; - - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; - - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - -private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; -}; // struct number_writer - -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} - -simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; -} - -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); -} - -simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; -} - -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; -} - -template -simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; -} - -simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); -} - -} // namespace stage2 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ - -namespace simdjson { -namespace haswell { -namespace { -namespace stage2 { - -struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept; - - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - - /** Next location to write to tape */ - tape_writer tape; -private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t *current_string_buf_loc; - - simdjson_inline tape_builder(dom::document &doc) noexcept; - - simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; - simdjson_inline void start_container(json_iterator &iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; - simdjson_inline void on_end_string(uint8_t *dst) noexcept; -}; // class tape_builder - -template -simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_root_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { - return visit_string(iter, key, true); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; -} - -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t *dst = on_start_string(iter); - dst = stringparsing::parse_string(value+1, dst); - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { - return visit_string(iter, value); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -// private: - -simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index+2, start); - tape.append(start_index, end); - return SUCCESS; -} - -simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; -} - -simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); -} - -simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; -} - -} // namespace stage2 -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ - -// -// Implementation-specific overrides -// -namespace simdjson { -namespace haswell { -namespace { -namespace stage1 { - -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); -} - -} // namespace stage1 -} // unnamed namespace - -simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { - return haswell::stage1::json_minifier::minify<128>(buf, len, dst, dst_len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return haswell::stage1::json_structural_indexer::index<128>(_buf, _len, *this, streaming); -} - -simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return haswell::stage1::generic_validate_utf8(buf,len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept { - return haswell::stringparsing::parse_string(src, dst); -} - -simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); -} - -} // namespace haswell -} // namespace simdjson - -/* begin file include/simdjson/haswell/end.h */ -SIMDJSON_UNTARGET_HASWELL -/* end file include/simdjson/haswell/end.h */ -/* end file src/haswell/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_PPC64 -/* begin file src/ppc64/implementation.cpp */ -/* begin file include/simdjson/ppc64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "ppc64" -// #define SIMDJSON_IMPLEMENTATION ppc64 -/* end file include/simdjson/ppc64/begin.h */ - -namespace simdjson { -namespace ppc64 { - -simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst -) const noexcept { - dst.reset( new (std::nothrow) dom_parser_implementation() ); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; -} - -} // namespace ppc64 -} // namespace simdjson - -/* begin file include/simdjson/ppc64/end.h */ -/* end file include/simdjson/ppc64/end.h */ -/* end file src/ppc64/implementation.cpp */ -/* begin file src/ppc64/dom_parser_implementation.cpp */ -/* begin file include/simdjson/ppc64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "ppc64" -// #define SIMDJSON_IMPLEMENTATION ppc64 -/* end file include/simdjson/ppc64/begin.h */ - -// -// Stage 1 -// -namespace simdjson { -namespace ppc64 { -namespace { - -using namespace simd; - -struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); - - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - - uint64_t _whitespace; - uint64_t _op; -}; - -simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - const simd8 table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); - const simd8 table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); - - simd8x64 v( - (in.chunks[0] & 0xf).lookup_16(table1) & (in.chunks[0].shr<4>()).lookup_16(table2), - (in.chunks[1] & 0xf).lookup_16(table1) & (in.chunks[1].shr<4>()).lookup_16(table2), - (in.chunks[2] & 0xf).lookup_16(table1) & (in.chunks[2].shr<4>()).lookup_16(table2), - (in.chunks[3] & 0xf).lookup_16(table1) & (in.chunks[3].shr<4>()).lookup_16(table2) - ); - - uint64_t op = simd8x64( - v.chunks[0].any_bits_set(0x7), - v.chunks[1].any_bits_set(0x7), - v.chunks[2].any_bits_set(0x7), - v.chunks[3].any_bits_set(0x7) - ).to_bitmask(); - - uint64_t whitespace = simd8x64( - v.chunks[0].any_bits_set(0x18), - v.chunks[1].any_bits_set(0x18), - v.chunks[2].any_bits_set(0x18), - v.chunks[3].any_bits_set(0x18) - ).to_bitmask(); - - return { whitespace, op }; -} - -simdjson_inline bool is_ascii(const simd8x64& input) { - // careful: 0x80 is not ascii. - return input.reduce_or().saturating_sub(0x7fu).bits_not_set_anywhere(); -} - -simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); -} - -simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_third_byte | is_fourth_byte) > int8_t(0); -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson - -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ -namespace simdjson { -namespace ppc64 { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#endif - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdjson_inline void check_next_input(const simd8x64& input) { - if(simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - ||(simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -/* begin file src/generic/stage1/json_structural_indexer.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -/* begin file src/generic/stage1/buf_block_reader.h */ -namespace simdjson { -namespace ppc64 { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t *dst) const; - simdjson_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text_64(const uint8_t *text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdjson_unused static char * format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdjson_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdjson_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage1 { - -struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} - - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } - - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; -}; - -// Scans blocks for string characters, storing the state necessary to do so -class json_string_scanner { -public: - simdjson_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); - - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; -}; - -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; - - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. - - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; -} - -// -// Return a mask of all string characters plus end quotes. -// -// prev_escaped is overflow saying whether the next character is escaped. -// prev_in_string is overflow saying whether we're still in a string. -// -// Backslash sequences outside of quotes will be detected in stage 2. -// -simdjson_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - const uint64_t quote = in.eq('"') & ~escaped; - - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - - // - // Check if we're still in a string at the end of the box so the next block will know - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); - - // Use ^ to turn the beginning quote off, and the end quote on. - - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block( - backslash, - escaped, - quote, - in_string - ); -} - -simdjson_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; -} - -} // namespace stage1 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage1 { - -/** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ -struct json_block { -public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - - // Helpers - - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; -private: - // Potential structurals (i.e. disregarding strings) - - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } -}; - -/** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ -class json_scanner { -public: - json_scanner() {} - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; -}; - - -// -// Check if the current character immediately follows a matching character. -// -// For example, this checks for quotes with backslashes in front of them: -// -// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); -// -simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; -} - -simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); -} - -simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage1 { - -class json_minifier { -public: - template - static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - -private: - simdjson_inline json_minifier(uint8_t *_dst) - : dst{_dst} - {} - template - simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); - json_scanner scanner{}; - uint8_t *dst; -}; - -simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); -} - -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; -} - -template<> -simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); -} - -template<> -simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); -} - -template -error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); - - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } - - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t * const guarded_dst{minifier.dst}; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if(to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ -namespace simdjson { -namespace ppc64 { -namespace { - -/** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ -simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { - // Variant: do not count separately, just figure out depth - if(parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ - -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage1 { - -class bit_indexer { -public: - uint32_t *tail; - - simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} - - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); -#else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; -#if defined(SIMDJSON_PREFER_REVERSE_BITS) - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - - uint64_t rev_bits = reverse_bits(bits); - int cnt = static_cast(count_ones(bits)); - int i = 0; - // Do the first 8 all together - for (; i<8; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - i = 8; - for (; i<16; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - i = 16; - while (rev_bits != 0) { - int lz = leading_zeroes(rev_bits); - this->tail[i++] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - } - } - this->tail += cnt; -#else // SIMDJSON_PREFER_REVERSE_BITS - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ - - int cnt = static_cast(count_ones(bits)); - // Do the first 8 all together - for (int i=0; i<8; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - for (int i=8; i<16; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - int i = 16; - do { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - i++; - } while (i < cnt); - } - } - - this->tail += cnt; -#endif - } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER - -}; - -class json_structural_indexer { -public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - -private: - simdjson_inline json_structural_indexer(uint32_t *structural_indexes); - template - simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; -}; - -simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - -// Skip the last character if it is partial -simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left - return len; -} - -// -// PERF NOTES: -// We pipe 2 inputs through these stages: -// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load -// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. -// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. -// The output of step 1 depends entirely on this information. These functions don't quite use -// up enough CPU: the second half of the functions is highly serial, only using 1 execution core -// at a time. The second input's scans has some dependency on the first ones finishing it, but -// they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that -// to finish: utf-8 checks and generating the output from the last iteration. -// -// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all -// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough -// workout. -// -template -error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if(len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); - - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); -} - -template<> -simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index()+64); - reader.advance(); -} - -template<> -simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); -} - -simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); - checker.check_next_input(in); - indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); -} - -simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx-64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } - - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if(have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if(parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } - - parser.n_structural_indexes = new_structural_indexes; - } else if (partial == stage1_mode::streaming_final) { - if(have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -/* begin file src/generic/stage1/utf8_validator.h */ -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage1 { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ - -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times - -namespace simdjson { -namespace ppc64 { -namespace { -/// @private -namespace stringparsing { - -// begin copypasta -// These chars yield themselves: " \ / -// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab -// u not handled in this table as it's complex -static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -// handle a unicode codepoint -// write appropriate values into dest -// src will advance 6 bytes or 12 bytes -// dest will advance a variable amount (return via pointer) -// return true if the unicode codepoint was valid -// We work in little-endian then swap at write time -simdjson_warn_unused -simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, - uint8_t **dst_ptr) { - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion isn't valid; we defer the check for this to inside the - // multilingual plane check - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') { - return false; - } - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - - // if the first code point is invalid we will get here, as we will go past - // the check for being outside the Basic Multilingual plane. If we don't - // find a \u immediately afterwards we fail out anyhow, but if we do, - // this check catches both the case of the first code point being invalid - // or the second code point being invalid. - if ((code_point | code_point_2) >> 16) { - return false; - } - - code_point = - (((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000; - *src_ptr += 6; - } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - return false; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} - -/** - * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There - * must be an unescaped quote terminating the string. It returns the final output - * position as pointer. In case of error (e.g., the string has bad escaped codes), - * then null_nullptrptr is returned. It is assumed that the output buffer is large - * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + - * SIMDJSON_PADDING bytes. - */ -simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst)) { - return nullptr; - } - } else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - /* can't be reached */ - return nullptr; -} - -} // namespace stringparsing -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! -namespace simdjson { -namespace ppc64 { -namespace { -namespace logger { - - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; - -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; -#else - static constexpr const bool LOG_ENABLED = false; -#endif - static constexpr const int LOG_EVENT_LEN = 20; - static constexpr const int LOG_BUFFER_LEN = 30; - static constexpr const int LOG_SMALL_BUFFER_LEN = 10; - static constexpr const int LOG_INDEX_LEN = 5; - - static int log_depth; // Not threadsafe. Log only. - - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } - - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); - } - } - - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } - - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; - - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char *type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char *type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char *type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char *error) const noexcept; - - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; -}; - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { - logger::log_start(); - - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY( visitor.visit_document_start(*this) ); - - // - // Read first value - // - { - auto value = advance(); - - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } - - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; - } - } - goto document_end; - -// -// Object parser states -// -object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY( visitor.visit_object_start(*this) ); - - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.increment_count(*this) ); - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - -object_field: - if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY( visitor.increment_count(*this) ); - { - auto key = advance(); - if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } - -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; - -// -// Array parser states -// -array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY( visitor.visit_array_start(*this) ); - SIMDJSON_TRY( visitor.increment_count(*this) ); - -array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } - -document_end: - log_end_value("document"); - SIMDJSON_TRY( visitor.visit_document_end(*this) ); - - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - - // If we didn't make it to the end, it's an error - if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } - - return SUCCESS; - -} // walk_document() - -simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) - : buf{_dom_parser.buf}, - next_structural{&_dom_parser.structural_indexes[start_structural_index]}, - dom_parser{_dom_parser} { -} - -simdjson_inline const uint8_t *json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; -} -simdjson_inline const uint8_t *json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; -} -simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural-1); -} - -simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; -} -simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); -} -simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; -} - -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); -} - -simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } -} - -simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); -} - -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); -} - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } -} -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_string(*this, value); - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } -} - -} // namespace stage2 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage2 { - -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; - - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; - - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; - - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; - - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; - - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; - - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; - - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - -private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; -}; // struct number_writer - -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} - -simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; -} - -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); -} - -simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; -} - -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; -} - -template -simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; -} - -simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); -} - -} // namespace stage2 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ - -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage2 { - -struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept; - - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - - /** Next location to write to tape */ - tape_writer tape; -private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t *current_string_buf_loc; - - simdjson_inline tape_builder(dom::document &doc) noexcept; - - simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; - simdjson_inline void start_container(json_iterator &iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; - simdjson_inline void on_end_string(uint8_t *dst) noexcept; -}; // class tape_builder - -template -simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_root_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { - return visit_string(iter, key, true); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; -} - -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t *dst = on_start_string(iter); - dst = stringparsing::parse_string(value+1, dst); - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { - return visit_string(iter, value); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -// private: - -simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index+2, start); - tape.append(start_index, end); - return SUCCESS; -} - -simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; -} - -simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); -} - -simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; -} - -} // namespace stage2 -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ - -// -// Implementation-specific overrides -// -namespace simdjson { -namespace ppc64 { -namespace { -namespace stage1 { - -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - // On PPC, we don't short-circuit this if there are no backslashes, because the branch gives us no - // benefit and therefore makes things worse. - // if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); -} - -} // namespace stage1 -} // unnamed namespace - -simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { - return ppc64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return ppc64::stage1::json_structural_indexer::index<64>(buf, len, *this, streaming); -} - -simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return ppc64::stage1::generic_validate_utf8(buf,len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept { - return ppc64::stringparsing::parse_string(src, dst); -} - -simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); -} - -} // namespace ppc64 -} // namespace simdjson - -/* begin file include/simdjson/ppc64/end.h */ -/* end file include/simdjson/ppc64/end.h */ -/* end file src/ppc64/dom_parser_implementation.cpp */ -#endif -#if SIMDJSON_IMPLEMENTATION_WESTMERE -/* begin file src/westmere/implementation.cpp */ -/* begin file include/simdjson/westmere/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "westmere" -// #define SIMDJSON_IMPLEMENTATION westmere -SIMDJSON_TARGET_WESTMERE -/* end file include/simdjson/westmere/begin.h */ - -namespace simdjson { -namespace westmere { - -simdjson_warn_unused error_code implementation::create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr& dst -) const noexcept { - dst.reset( new (std::nothrow) dom_parser_implementation() ); - if (!dst) { return MEMALLOC; } - if (auto err = dst->set_capacity(capacity)) - return err; - if (auto err = dst->set_max_depth(max_depth)) - return err; - return SUCCESS; -} - -} // namespace westmere -} // namespace simdjson - -/* begin file include/simdjson/westmere/end.h */ -SIMDJSON_UNTARGET_WESTMERE -/* end file include/simdjson/westmere/end.h */ -/* end file src/westmere/implementation.cpp */ -/* begin file src/westmere/dom_parser_implementation.cpp */ -/* begin file include/simdjson/westmere/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "westmere" -// #define SIMDJSON_IMPLEMENTATION westmere -SIMDJSON_TARGET_WESTMERE -/* end file include/simdjson/westmere/begin.h */ - -// -// Stage 1 -// - -namespace simdjson { -namespace westmere { -namespace { - -using namespace simd; - -struct json_character_block { - static simdjson_inline json_character_block classify(const simd::simd8x64& in); - - simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } - simdjson_inline uint64_t op() const noexcept { return _op; } - simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } - - uint64_t _whitespace; - uint64_t _op; -}; - -simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64& in) { - // These lookups rely on the fact that anything < 127 will match the lower 4 bits, which is why - // we can't use the generic lookup_16. - auto whitespace_table = simd8::repeat_16(' ', 100, 100, 100, 17, 100, 113, 2, 100, '\t', '\n', 112, 100, '\r', 100, 100); - - // The 6 operators (:,[]{}) have these values: - // - // , 2C - // : 3A - // [ 5B - // { 7B - // ] 5D - // } 7D - // - // If you use | 0x20 to turn [ and ] into { and }, the lower 4 bits of each character is unique. - // We exploit this, using a simd 4-bit lookup to tell us which character match against, and then - // match it (against | 0x20). - // - // To prevent recognizing other characters, everything else gets compared with 0, which cannot - // match due to the | 0x20. - // - // NOTE: Due to the | 0x20, this ALSO treats and (control characters 0C and 1A) like , - // and :. This gets caught in stage 2, which checks the actual character to ensure the right - // operators are in the right places. - const auto op_table = simd8::repeat_16( - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, ':', '{', // : = 3A, [ = 5B, { = 7B - ',', '}', 0, 0 // , = 2C, ] = 5D, } = 7D - ); - - // We compute whitespace and op separately. If the code later only use one or the - // other, given the fact that all functions are aggressively inlined, we can - // hope that useless computations will be omitted. This is namely case when - // minifying (we only need whitespace). - - - const uint64_t whitespace = in.eq({ - _mm_shuffle_epi8(whitespace_table, in.chunks[0]), - _mm_shuffle_epi8(whitespace_table, in.chunks[1]), - _mm_shuffle_epi8(whitespace_table, in.chunks[2]), - _mm_shuffle_epi8(whitespace_table, in.chunks[3]) - }); - // Turn [ and ] into { and } - const simd8x64 curlified{ - in.chunks[0] | 0x20, - in.chunks[1] | 0x20, - in.chunks[2] | 0x20, - in.chunks[3] | 0x20 - }; - const uint64_t op = curlified.eq({ - _mm_shuffle_epi8(op_table, in.chunks[0]), - _mm_shuffle_epi8(op_table, in.chunks[1]), - _mm_shuffle_epi8(op_table, in.chunks[2]), - _mm_shuffle_epi8(op_table, in.chunks[3]) - }); - return { whitespace, op }; -} - -simdjson_inline bool is_ascii(const simd8x64& input) { - return input.reduce_or().is_ascii(); -} - -simdjson_unused simdjson_inline simd8 must_be_continuation(const simd8 prev1, const simd8 prev2, const simd8 prev3) { - simd8 is_second_byte = prev1.saturating_sub(0xc0u-1); // Only 11______ will be > 0 - simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0); -} - -simdjson_inline simd8 must_be_2_3_continuation(const simd8 prev2, const simd8 prev3) { - simd8 is_third_byte = prev2.saturating_sub(0xe0u-1); // Only 111_____ will be > 0 - simd8 is_fourth_byte = prev3.saturating_sub(0xf0u-1); // Only 1111____ will be > 0 - // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine. - return simd8(is_third_byte | is_fourth_byte) > int8_t(0); -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson - -/* begin file src/generic/stage1/utf8_lookup4_algorithm.h */ -namespace simdjson { -namespace westmere { -namespace { -namespace utf8_validation { - -using namespace simd; - - simdjson_inline simd8 check_special_cases(const simd8 input, const simd8 prev1) { -// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII) -// Bit 1 = Too Long (ASCII followed by continuation) -// Bit 2 = Overlong 3-byte -// Bit 4 = Surrogate -// Bit 5 = Overlong 2-byte -// Bit 7 = Two Continuations - constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______ - // 11______ 11______ - constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______ - constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____ - constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____ - constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______ - constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______ - constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____ - // 11110100 101_____ - // 11110101 1001____ - // 11110101 101_____ - // 1111011_ 1001____ - // 1111011_ 101_____ - // 11111___ 1001____ - // 11111___ 101_____ - constexpr const uint8_t TOO_LARGE_1000 = 1<<6; - // 11110101 1000____ - // 1111011_ 1000____ - // 11111___ 1000____ - constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____ - - const simd8 byte_1_high = prev1.shr<4>().lookup_16( - // 0_______ ________ - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG, - // 10______ ________ - TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS, - // 1100____ ________ - TOO_SHORT | OVERLONG_2, - // 1101____ ________ - TOO_SHORT, - // 1110____ ________ - TOO_SHORT | OVERLONG_3 | SURROGATE, - // 1111____ ________ - TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4 - ); - constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 . - const simd8 byte_1_low = (prev1 & 0x0F).lookup_16( - // ____0000 ________ - CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4, - // ____0001 ________ - CARRY | OVERLONG_2, - // ____001_ ________ - CARRY, - CARRY, - - // ____0100 ________ - CARRY | TOO_LARGE, - // ____0101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____011_ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - - // ____1___ ________ - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000, - // ____1101 ________ - CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE, - CARRY | TOO_LARGE | TOO_LARGE_1000, - CARRY | TOO_LARGE | TOO_LARGE_1000 - ); - const simd8 byte_2_high = input.shr<4>().lookup_16( - // ________ 0_______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT, - - // ________ 1000____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4, - // ________ 1001____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE, - // ________ 101_____ - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE, - - // ________ 11______ - TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT - ); - return (byte_1_high & byte_1_low & byte_2_high); - } - simdjson_inline simd8 check_multibyte_lengths(const simd8 input, - const simd8 prev_input, const simd8 sc) { - simd8 prev2 = input.prev<2>(prev_input); - simd8 prev3 = input.prev<3>(prev_input); - simd8 must23 = simd8(must_be_2_3_continuation(prev2, prev3)); - simd8 must23_80 = must23 & uint8_t(0x80); - return must23_80 ^ sc; - } - - // - // Return nonzero if there are incomplete multibyte characters at the end of the block: - // e.g. if there is a 4-byte character, but it's 3 bytes from the end. - // - simdjson_inline simd8 is_incomplete(const simd8 input) { - // If the previous input's last 3 bytes match this, they're too short (they ended at EOF): - // ... 1111____ 111_____ 11______ -#if SIMDJSON_IMPLEMENTATION_ICELAKE - static const uint8_t max_array[64] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#else - static const uint8_t max_array[32] = { - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 0xf0u-1, 0xe0u-1, 0xc0u-1 - }; -#endif - const simd8 max_value(&max_array[sizeof(max_array)-sizeof(simd8)]); - return input.gt_bits(max_value); - } - - struct utf8_checker { - // If this is nonzero, there has been a UTF-8 error. - simd8 error; - // The last input we received - simd8 prev_input_block; - // Whether the last input we received was incomplete (used for ASCII fast path) - simd8 prev_incomplete; - - // - // Check whether the current bytes are valid UTF-8. - // - simdjson_inline void check_utf8_bytes(const simd8 input, const simd8 prev_input) { - // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes - // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers) - simd8 prev1 = input.prev<1>(prev_input); - simd8 sc = check_special_cases(input, prev1); - this->error |= check_multibyte_lengths(input, prev_input, sc); - } - - // The only problem that can happen at EOF is that a multibyte character is too short - // or a byte value too large in the last bytes: check_special_cases only checks for bytes - // too large in the first of two bytes. - simdjson_inline void check_eof() { - // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't - // possibly finish them. - this->error |= this->prev_incomplete; - } - - simdjson_inline void check_next_input(const simd8x64& input) { - if(simdjson_likely(is_ascii(input))) { - this->error |= this->prev_incomplete; - } else { - // you might think that a for-loop would work, but under Visual Studio, it is not good enough. - static_assert((simd8x64::NUM_CHUNKS == 1) - ||(simd8x64::NUM_CHUNKS == 2) - || (simd8x64::NUM_CHUNKS == 4), - "We support one, two or four chunks per 64-byte block."); - if(simd8x64::NUM_CHUNKS == 1) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - } if(simd8x64::NUM_CHUNKS == 2) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - } else if(simd8x64::NUM_CHUNKS == 4) { - this->check_utf8_bytes(input.chunks[0], this->prev_input_block); - this->check_utf8_bytes(input.chunks[1], input.chunks[0]); - this->check_utf8_bytes(input.chunks[2], input.chunks[1]); - this->check_utf8_bytes(input.chunks[3], input.chunks[2]); - } - this->prev_incomplete = is_incomplete(input.chunks[simd8x64::NUM_CHUNKS-1]); - this->prev_input_block = input.chunks[simd8x64::NUM_CHUNKS-1]; - } - } - // do not forget to call check_eof! - simdjson_inline error_code errors() { - return this->error.any_bits_set_anywhere() ? error_code::UTF8_ERROR : error_code::SUCCESS; - } - - }; // struct utf8_checker -} // namespace utf8_validation - -using utf8_validation::utf8_checker; - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/utf8_lookup4_algorithm.h */ -/* begin file src/generic/stage1/json_structural_indexer.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -/* begin file src/generic/stage1/buf_block_reader.h */ -namespace simdjson { -namespace westmere { -namespace { - -// Walks through a buffer in block-sized increments, loading the last part with spaces -template -struct buf_block_reader { -public: - simdjson_inline buf_block_reader(const uint8_t *_buf, size_t _len); - simdjson_inline size_t block_index(); - simdjson_inline bool has_full_block() const; - simdjson_inline const uint8_t *full_block() const; - /** - * Get the last block, padded with spaces. - * - * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this - * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there - * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding. - * - * @return the number of effective characters in the last block. - */ - simdjson_inline size_t get_remainder(uint8_t *dst) const; - simdjson_inline void advance(); -private: - const uint8_t *buf; - const size_t len; - const size_t lenminusstep; - size_t idx; -}; - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text_64(const uint8_t *text) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdjson_unused static char * format_input_text(const simd8x64& in) { - static char buf[sizeof(simd8x64) + 1]; - in.store(reinterpret_cast(buf)); - for (size_t i=0; i); i++) { - if (buf[i] < ' ') { buf[i] = '_'; } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdjson_unused static char * format_mask(uint64_t mask) { - static char buf[sizeof(simd8x64) + 1]; - for (size_t i=0; i<64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - -template -simdjson_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {} - -template -simdjson_inline size_t buf_block_reader::block_index() { return idx; } - -template -simdjson_inline bool buf_block_reader::has_full_block() const { - return idx < lenminusstep; -} - -template -simdjson_inline const uint8_t *buf_block_reader::full_block() const { - return &buf[idx]; -} - -template -simdjson_inline size_t buf_block_reader::get_remainder(uint8_t *dst) const { - if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers - std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once. - std::memcpy(dst, buf + idx, len - idx); - return len - idx; -} - -template -simdjson_inline void buf_block_reader::advance() { - idx += STEP_SIZE; -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/buf_block_reader.h */ -/* begin file src/generic/stage1/json_string_scanner.h */ -namespace simdjson { -namespace westmere { -namespace { -namespace stage1 { - -struct json_string_block { - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_string_block(uint64_t backslash, uint64_t escaped, uint64_t quote, uint64_t in_string) : - _backslash(backslash), _escaped(escaped), _quote(quote), _in_string(in_string) {} - - // Escaped characters (characters following an escape() character) - simdjson_inline uint64_t escaped() const { return _escaped; } - // Escape characters (backslashes that are not escaped--i.e. in \\, includes only the first \) - simdjson_inline uint64_t escape() const { return _backslash & ~_escaped; } - // Real (non-backslashed) quotes - simdjson_inline uint64_t quote() const { return _quote; } - // Start quotes of strings - simdjson_inline uint64_t string_start() const { return _quote & _in_string; } - // End quotes of strings - simdjson_inline uint64_t string_end() const { return _quote & ~_in_string; } - // Only characters inside the string (not including the quotes) - simdjson_inline uint64_t string_content() const { return _in_string & ~_quote; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const { return mask & _in_string; } - // Return a mask of whether the given characters are inside a string (only works on non-quotes) - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const { return mask & ~_in_string; } - // Tail of string (everything except the start quote) - simdjson_inline uint64_t string_tail() const { return _in_string ^ _quote; } - - // backslash characters - uint64_t _backslash; - // escaped characters (backslashed--does not include the hex characters after \u) - uint64_t _escaped; - // real quotes (non-backslashed ones) - uint64_t _quote; - // string characters (includes start quote but not end quote) - uint64_t _in_string; -}; - -// Scans blocks for string characters, storing the state necessary to do so -class json_string_scanner { -public: - simdjson_inline json_string_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Intended to be defined by the implementation - simdjson_inline uint64_t find_escaped(uint64_t escape); - simdjson_inline uint64_t find_escaped_branchless(uint64_t escape); - - // Whether the last iteration was still inside a string (all 1's = true, all 0's = false). - uint64_t prev_in_string = 0ULL; - // Whether the first character of the next iteration is escaped. - uint64_t prev_escaped = 0ULL; -}; - -// -// Finds escaped characters (characters following \). -// -// Handles runs of backslashes like \\\" and \\\\" correctly (yielding 0101 and 01010, respectively). -// -// Does this by: -// - Shift the escape mask to get potentially escaped characters (characters after backslashes). -// - Mask escaped sequences that start on *even* bits with 1010101010 (odd bits are escaped, even bits are not) -// - Mask escaped sequences that start on *odd* bits with 0101010101 (even bits are escaped, odd bits are not) -// -// To distinguish between escaped sequences starting on even/odd bits, it finds the start of all -// escape sequences, filters out the ones that start on even bits, and adds that to the mask of -// escape sequences. This causes the addition to clear out the sequences starting on odd bits (since -// the start bit causes a carry), and leaves even-bit sequences alone. -// -// Example: -// -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// escape | xxx | xx xxx xxx xx xx | Removed overflow backslash; will | it into follows_escape -// odd_starts | x | x x x | escape & ~even_bits & ~follows_escape -// even_seq | c| cxxx c xx c | c = carry bit -- will be masked out later -// invert_mask | | cxxx c xx c| even_seq << 1 -// follows_escape | xx | x xx xxx xxx xx xx | Includes overflow bit -// escaped | x | x x x x x x x x | -// desired | x | x x x x x x x x | -// text | \\\ | \\\"\\\" \\\" \\"\\" | -// -simdjson_inline uint64_t json_string_scanner::find_escaped_branchless(uint64_t backslash) { - // If there was overflow, pretend the first character isn't a backslash - backslash &= ~prev_escaped; - uint64_t follows_escape = backslash << 1 | prev_escaped; - - // Get sequences starting on even bits by clearing out the odd series using + - const uint64_t even_bits = 0x5555555555555555ULL; - uint64_t odd_sequence_starts = backslash & ~even_bits & ~follows_escape; - uint64_t sequences_starting_on_even_bits; - prev_escaped = add_overflow(odd_sequence_starts, backslash, &sequences_starting_on_even_bits); - uint64_t invert_mask = sequences_starting_on_even_bits << 1; // The mask we want to return is the *escaped* bits, not escapes. - - // Mask every other backslashed character as an escaped character - // Flip the mask for sequences that start on even bits, to correct them - return (even_bits ^ invert_mask) & follows_escape; -} - -// -// Return a mask of all string characters plus end quotes. -// -// prev_escaped is overflow saying whether the next character is escaped. -// prev_in_string is overflow saying whether we're still in a string. -// -// Backslash sequences outside of quotes will be detected in stage 2. -// -simdjson_inline json_string_block json_string_scanner::next(const simd::simd8x64& in) { - const uint64_t backslash = in.eq('\\'); - const uint64_t escaped = find_escaped(backslash); - const uint64_t quote = in.eq('"') & ~escaped; - - // - // prefix_xor flips on bits inside the string (and flips off the end quote). - // - // Then we xor with prev_in_string: if we were in a string already, its effect is flipped - // (characters inside strings are outside, and characters outside strings are inside). - // - const uint64_t in_string = prefix_xor(quote) ^ prev_in_string; - - // - // Check if we're still in a string at the end of the box so the next block will know - // - // right shift of a signed value expected to be well-defined and standard - // compliant as of C++20, John Regher from Utah U. says this is fine code - // - prev_in_string = uint64_t(static_cast(in_string) >> 63); - - // Use ^ to turn the beginning quote off, and the end quote on. - - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_string_block( - backslash, - escaped, - quote, - in_string - ); -} - -simdjson_inline error_code json_string_scanner::finish() { - if (prev_in_string) { - return UNCLOSED_STRING; - } - return SUCCESS; -} - -} // namespace stage1 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/json_string_scanner.h */ -/* begin file src/generic/stage1/json_scanner.h */ -namespace simdjson { -namespace westmere { -namespace { -namespace stage1 { - -/** - * A block of scanned json, with information on operators and scalars. - * - * We seek to identify pseudo-structural characters. Anything that is inside - * a string must be omitted (hence & ~_string.string_tail()). - * Otherwise, pseudo-structural characters come in two forms. - * 1. We have the structural characters ([,],{,},:, comma). The - * term 'structural character' is from the JSON RFC. - * 2. We have the 'scalar pseudo-structural characters'. - * Scalars are quotes, and any character except structural characters and white space. - * - * To identify the scalar pseudo-structural characters, we must look at what comes - * before them: it must be a space, a quote or a structural characters. - * Starting with simdjson v0.3, we identify them by - * negation: we identify everything that is followed by a non-quote scalar, - * and we negate that. Whatever remains must be a 'scalar pseudo-structural character'. - */ -struct json_block { -public: - // We spell out the constructors in the hope of resolving inlining issues with Visual Studio 2017 - simdjson_inline json_block(json_string_block&& string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(std::move(string)), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - simdjson_inline json_block(json_string_block string, json_character_block characters, uint64_t follows_potential_nonquote_scalar) : - _string(string), _characters(characters), _follows_potential_nonquote_scalar(follows_potential_nonquote_scalar) {} - - /** - * The start of structurals. - * In simdjson prior to v0.3, these were called the pseudo-structural characters. - **/ - simdjson_inline uint64_t structural_start() const noexcept { return potential_structural_start() & ~_string.string_tail(); } - /** All JSON whitespace (i.e. not in a string) */ - simdjson_inline uint64_t whitespace() const noexcept { return non_quote_outside_string(_characters.whitespace()); } - - // Helpers - - /** Whether the given characters are inside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_inside_string(uint64_t mask) const noexcept { return _string.non_quote_inside_string(mask); } - /** Whether the given characters are outside a string (only works on non-quotes) */ - simdjson_inline uint64_t non_quote_outside_string(uint64_t mask) const noexcept { return _string.non_quote_outside_string(mask); } - - // string and escape characters - json_string_block _string; - // whitespace, structural characters ('operators'), scalars - json_character_block _characters; - // whether the previous character was a scalar - uint64_t _follows_potential_nonquote_scalar; -private: - // Potential structurals (i.e. disregarding strings) - - /** - * structural elements ([,],{,},:, comma) plus scalar starts like 123, true and "abc". - * They may reside inside a string. - **/ - simdjson_inline uint64_t potential_structural_start() const noexcept { return _characters.op() | potential_scalar_start(); } - /** - * The start of non-operator runs, like 123, true and "abc". - * It main reside inside a string. - **/ - simdjson_inline uint64_t potential_scalar_start() const noexcept { - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // Whenever it is preceded by something that is not a structural element ({,},[,],:, ") nor a white-space - // then we know that it is irrelevant structurally. - return _characters.scalar() & ~follows_potential_scalar(); - } - /** - * Whether the given character is immediately after a non-operator like 123, true. - * The characters following a quote are not included. - */ - simdjson_inline uint64_t follows_potential_scalar() const noexcept { - // _follows_potential_nonquote_scalar: is defined as marking any character that follows a character - // that is not a structural element ({,},[,],:, comma) nor a quote (") and that is not a - // white space. - // It is understood that within quoted region, anything at all could be marked (irrelevant). - return _follows_potential_nonquote_scalar; - } -}; - -/** - * Scans JSON for important bits: structural characters or 'operators', strings, and scalars. - * - * The scanner starts by calculating two distinct things: - * - string characters (taking \" into account) - * - structural characters or 'operators' ([]{},:, comma) - * and scalars (runs of non-operators like 123, true and "abc") - * - * To minimize data dependency (a key component of the scanner's speed), it finds these in parallel: - * in particular, the operator/scalar bit will find plenty of things that are actually part of - * strings. When we're done, json_block will fuse the two together by masking out tokens that are - * part of a string. - */ -class json_scanner { -public: - json_scanner() {} - simdjson_inline json_block next(const simd::simd8x64& in); - // Returns either UNCLOSED_STRING or SUCCESS - simdjson_inline error_code finish(); - -private: - // Whether the last character of the previous iteration is part of a scalar token - // (anything except whitespace or a structural character/'operator'). - uint64_t prev_scalar = 0ULL; - json_string_scanner string_scanner{}; -}; - - -// -// Check if the current character immediately follows a matching character. -// -// For example, this checks for quotes with backslashes in front of them: -// -// const uint64_t backslashed_quote = in.eq('"') & immediately_follows(in.eq('\'), prev_backslash); -// -simdjson_inline uint64_t follows(const uint64_t match, uint64_t &overflow) { - const uint64_t result = match << 1 | overflow; - overflow = match >> 63; - return result; -} - -simdjson_inline json_block json_scanner::next(const simd::simd8x64& in) { - json_string_block strings = string_scanner.next(in); - // identifies the white-space and the structural characters - json_character_block characters = json_character_block::classify(in); - // The term "scalar" refers to anything except structural characters and white space - // (so letters, numbers, quotes). - // We want follows_scalar to mark anything that follows a non-quote scalar (so letters and numbers). - // - // A terminal quote should either be followed by a structural character (comma, brace, bracket, colon) - // or nothing. However, we still want ' "a string"true ' to mark the 't' of 'true' as a potential - // pseudo-structural character just like we would if we had ' "a string" true '; otherwise we - // may need to add an extra check when parsing strings. - // - // Performance: there are many ways to skin this cat. - const uint64_t nonquote_scalar = characters.scalar() & ~strings.quote(); - uint64_t follows_nonquote_scalar = follows(nonquote_scalar, prev_scalar); - // We are returning a function-local object so either we get a move constructor - // or we get copy elision. - return json_block( - strings,// strings is a function-local object so either it moves or the copy is elided. - characters, - follows_nonquote_scalar - ); -} - -simdjson_inline error_code json_scanner::finish() { - return string_scanner.finish(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/json_scanner.h */ -/* begin file src/generic/stage1/json_minifier.h */ -// This file contains the common code every implementation uses in stage1 -// It is intended to be included multiple times and compiled multiple times -// We assume the file in which it is included already includes -// "simdjson/stage1.h" (this simplifies amalgation) - -namespace simdjson { -namespace westmere { -namespace { -namespace stage1 { - -class json_minifier { -public: - template - static error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept; - -private: - simdjson_inline json_minifier(uint8_t *_dst) - : dst{_dst} - {} - template - simdjson_inline void step(const uint8_t *block_buf, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block); - simdjson_inline error_code finish(uint8_t *dst_start, size_t &dst_len); - json_scanner scanner{}; - uint8_t *dst; -}; - -simdjson_inline void json_minifier::next(const simd::simd8x64& in, const json_block& block) { - uint64_t mask = block.whitespace(); - dst += in.compress(mask, dst); -} - -simdjson_inline error_code json_minifier::finish(uint8_t *dst_start, size_t &dst_len) { - error_code error = scanner.finish(); - if (error) { dst_len = 0; return error; } - dst_len = dst - dst_start; - return SUCCESS; -} - -template<> -simdjson_inline void json_minifier::step<128>(const uint8_t *block_buf, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - simd::simd8x64 in_2(block_buf+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1); - this->next(in_2, block_2); - reader.advance(); -} - -template<> -simdjson_inline void json_minifier::step<64>(const uint8_t *block_buf, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block_buf); - json_block block_1 = scanner.next(in_1); - this->next(block_buf, block_1); - reader.advance(); -} - -template -error_code json_minifier::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) noexcept { - buf_block_reader reader(buf, len); - json_minifier minifier(dst); - - // Index the first n-1 blocks - while (reader.has_full_block()) { - minifier.step(reader.full_block(), reader); - } - - // Index the last (remainder) block, padded with spaces - uint8_t block[STEP_SIZE]; - size_t remaining_bytes = reader.get_remainder(block); - if (remaining_bytes > 0) { - // We do not want to write directly to the output stream. Rather, we write - // to a local buffer (for safety). - uint8_t out_block[STEP_SIZE]; - uint8_t * const guarded_dst{minifier.dst}; - minifier.dst = out_block; - minifier.step(block, reader); - size_t to_write = minifier.dst - out_block; - // In some cases, we could be enticed to consider the padded spaces - // as part of the string. This is fine as long as we do not write more - // than we consumed. - if(to_write > remaining_bytes) { to_write = remaining_bytes; } - memcpy(guarded_dst, out_block, to_write); - minifier.dst = guarded_dst + to_write; - } - return minifier.finish(dst, dst_len); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/json_minifier.h */ -/* begin file src/generic/stage1/find_next_document_index.h */ -namespace simdjson { -namespace westmere { -namespace { - -/** - * This algorithm is used to quickly identify the last structural position that - * makes up a complete document. - * - * It does this by going backwards and finding the last *document boundary* (a - * place where one value follows another without a comma between them). If the - * last document (the characters after the boundary) has an equal number of - * start and end brackets, it is considered complete. - * - * Simply put, we iterate over the structural characters, starting from - * the end. We consider that we found the end of a JSON document when the - * first element of the pair is NOT one of these characters: '{' '[' ':' ',' - * and when the second element is NOT one of these characters: '}' ']' ':' ','. - * - * This simple comparison works most of the time, but it does not cover cases - * where the batch's structural indexes contain a perfect amount of documents. - * In such a case, we do not have access to the structural index which follows - * the last document, therefore, we do not have access to the second element in - * the pair, and that means we cannot identify the last document. To fix this - * issue, we keep a count of the open and closed curly/square braces we found - * while searching for the pair. When we find a pair AND the count of open and - * closed curly/square braces is the same, we know that we just passed a - * complete document, therefore the last json buffer location is the end of the - * batch. - */ -simdjson_inline uint32_t find_next_document_index(dom_parser_implementation &parser) { - // Variant: do not count separately, just figure out depth - if(parser.n_structural_indexes == 0) { return 0; } - auto arr_cnt = 0; - auto obj_cnt = 0; - for (auto i = parser.n_structural_indexes - 1; i > 0; i--) { - auto idxb = parser.structural_indexes[i]; - switch (parser.buf[idxb]) { - case ':': - case ',': - continue; - case '}': - obj_cnt--; - continue; - case ']': - arr_cnt--; - continue; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - auto idxa = parser.structural_indexes[i - 1]; - switch (parser.buf[idxa]) { - case '{': - case '[': - case ':': - case ',': - continue; - } - // Last document is complete, so the next document will appear after! - if (!arr_cnt && !obj_cnt) { - return parser.n_structural_indexes; - } - // Last document is incomplete; mark the document at i + 1 as the next one - return i; - } - // If we made it to the end, we want to finish counting to see if we have a full document. - switch (parser.buf[parser.structural_indexes[0]]) { - case '}': - obj_cnt--; - break; - case ']': - arr_cnt--; - break; - case '{': - obj_cnt++; - break; - case '[': - arr_cnt++; - break; - } - if (!arr_cnt && !obj_cnt) { - // We have a complete document. - return parser.n_structural_indexes; - } - return 0; -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/find_next_document_index.h */ - -namespace simdjson { -namespace westmere { -namespace { -namespace stage1 { - -class bit_indexer { -public: - uint32_t *tail; - - simdjson_inline bit_indexer(uint32_t *index_buf) : tail(index_buf) {} - - // flatten out values in 'bits' assuming that they are are to have values of idx - // plus their position in the bitvector, and store these indexes at - // base_ptr[base] incrementing base as we go - // will potentially store extra values beyond end of valid bits, so base_ptr - // needs to be large enough to handle this - // - // If the kernel sets SIMDJSON_CUSTOM_BIT_INDEXER, then it will provide its own - // version of the code. -#ifdef SIMDJSON_CUSTOM_BIT_INDEXER - simdjson_inline void write(uint32_t idx, uint64_t bits); -#else - simdjson_inline void write(uint32_t idx, uint64_t bits) { - // In some instances, the next branch is expensive because it is mispredicted. - // Unfortunately, in other cases, - // it helps tremendously. - if (bits == 0) - return; -#if defined(SIMDJSON_PREFER_REVERSE_BITS) - /** - * ARM lacks a fast trailing zero instruction, but it has a fast - * bit reversal instruction and a fast leading zero instruction. - * Thus it may be profitable to reverse the bits (once) and then - * to rely on a sequence of instructions that call the leading - * zero instruction. - * - * Performance notes: - * The chosen routine is not optimal in terms of data dependency - * since zero_leading_bit might require two instructions. However, - * it tends to minimize the total number of instructions which is - * beneficial. - */ - - uint64_t rev_bits = reverse_bits(bits); - int cnt = static_cast(count_ones(bits)); - int i = 0; - // Do the first 8 all together - for (; i<8; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - i = 8; - for (; i<16; i++) { - int lz = leading_zeroes(rev_bits); - this->tail[i] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - i = 16; - while (rev_bits != 0) { - int lz = leading_zeroes(rev_bits); - this->tail[i++] = static_cast(idx) + lz; - rev_bits = zero_leading_bit(rev_bits, lz); - } - } - } - this->tail += cnt; -#else // SIMDJSON_PREFER_REVERSE_BITS - /** - * Under recent x64 systems, we often have both a fast trailing zero - * instruction and a fast 'clear-lower-bit' instruction so the following - * algorithm can be competitive. - */ - - int cnt = static_cast(count_ones(bits)); - // Do the first 8 all together - for (int i=0; i<8; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Do the next 8 all together (we hope in most cases it won't happen at all - // and the branch is easily predicted). - if (simdjson_unlikely(cnt > 8)) { - for (int i=8; i<16; i++) { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - } - - // Most files don't have 16+ structurals per block, so we take several basically guaranteed - // branch mispredictions here. 16+ structurals per block means either punctuation ({} [] , :) - // or the start of a value ("abc" true 123) every four characters. - if (simdjson_unlikely(cnt > 16)) { - int i = 16; - do { - this->tail[i] = idx + trailing_zeroes(bits); - bits = clear_lowest_bit(bits); - i++; - } while (i < cnt); - } - } - - this->tail += cnt; -#endif - } -#endif // SIMDJSON_CUSTOM_BIT_INDEXER - -}; - -class json_structural_indexer { -public: - /** - * Find the important bits of JSON in a 128-byte chunk, and add them to structural_indexes. - * - * @param partial Setting the partial parameter to true allows the find_structural_bits to - * tolerate unclosed strings. The caller should still ensure that the input is valid UTF-8. If - * you are processing substrings, you may want to call on a function like trimmed_length_safe_utf8. - */ - template - static error_code index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept; - -private: - simdjson_inline json_structural_indexer(uint32_t *structural_indexes); - template - simdjson_inline void step(const uint8_t *block, buf_block_reader &reader) noexcept; - simdjson_inline void next(const simd::simd8x64& in, const json_block& block, size_t idx); - simdjson_inline error_code finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial); - - json_scanner scanner{}; - utf8_checker checker{}; - bit_indexer indexer; - uint64_t prev_structurals = 0; - uint64_t unescaped_chars_error = 0; -}; - -simdjson_inline json_structural_indexer::json_structural_indexer(uint32_t *structural_indexes) : indexer{structural_indexes} {} - -// Skip the last character if it is partial -simdjson_inline size_t trim_partial_utf8(const uint8_t *buf, size_t len) { - if (simdjson_unlikely(len < 3)) { - switch (len) { - case 2: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 2 bytes left - return len; - case 1: - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - return len; - case 0: - return len; - } - } - if (buf[len-1] >= 0xc0) { return len-1; } // 2-, 3- and 4-byte characters with only 1 byte left - if (buf[len-2] >= 0xe0) { return len-2; } // 3- and 4-byte characters with only 1 byte left - if (buf[len-3] >= 0xf0) { return len-3; } // 4-byte characters with only 3 bytes left - return len; -} - -// -// PERF NOTES: -// We pipe 2 inputs through these stages: -// 1. Load JSON into registers. This takes a long time and is highly parallelizable, so we load -// 2 inputs' worth at once so that by the time step 2 is looking for them input, it's available. -// 2. Scan the JSON for critical data: strings, scalars and operators. This is the critical path. -// The output of step 1 depends entirely on this information. These functions don't quite use -// up enough CPU: the second half of the functions is highly serial, only using 1 execution core -// at a time. The second input's scans has some dependency on the first ones finishing it, but -// they can make a lot of progress before they need that information. -// 3. Step 1 doesn't use enough capacity, so we run some extra stuff while we're waiting for that -// to finish: utf-8 checks and generating the output from the last iteration. -// -// The reason we run 2 inputs at a time, is steps 2 and 3 are *still* not enough to soak up all -// available capacity with just one input. Running 2 at a time seems to give the CPU a good enough -// workout. -// -template -error_code json_structural_indexer::index(const uint8_t *buf, size_t len, dom_parser_implementation &parser, stage1_mode partial) noexcept { - if (simdjson_unlikely(len > parser.capacity())) { return CAPACITY; } - // We guard the rest of the code so that we can assume that len > 0 throughout. - if (len == 0) { return EMPTY; } - if (is_streaming(partial)) { - len = trim_partial_utf8(buf, len); - // If you end up with an empty window after trimming - // the partial UTF-8 bytes, then chances are good that you - // have an UTF-8 formatting error. - if(len == 0) { return UTF8_ERROR; } - } - buf_block_reader reader(buf, len); - json_structural_indexer indexer(parser.structural_indexes.get()); - - // Read all but the last block - while (reader.has_full_block()) { - indexer.step(reader.full_block(), reader); - } - // Take care of the last block (will always be there unless file is empty which is - // not supposed to happen.) - uint8_t block[STEP_SIZE]; - if (simdjson_unlikely(reader.get_remainder(block) == 0)) { return UNEXPECTED_ERROR; } - indexer.step(block, reader); - return indexer.finish(parser, reader.block_index(), len, partial); -} - -template<> -simdjson_inline void json_structural_indexer::step<128>(const uint8_t *block, buf_block_reader<128> &reader) noexcept { - simd::simd8x64 in_1(block); - simd::simd8x64 in_2(block+64); - json_block block_1 = scanner.next(in_1); - json_block block_2 = scanner.next(in_2); - this->next(in_1, block_1, reader.block_index()); - this->next(in_2, block_2, reader.block_index()+64); - reader.advance(); -} - -template<> -simdjson_inline void json_structural_indexer::step<64>(const uint8_t *block, buf_block_reader<64> &reader) noexcept { - simd::simd8x64 in_1(block); - json_block block_1 = scanner.next(in_1); - this->next(in_1, block_1, reader.block_index()); - reader.advance(); -} - -simdjson_inline void json_structural_indexer::next(const simd::simd8x64& in, const json_block& block, size_t idx) { - uint64_t unescaped = in.lteq(0x1F); - checker.check_next_input(in); - indexer.write(uint32_t(idx-64), prev_structurals); // Output *last* iteration's structurals to the parser - prev_structurals = block.structural_start(); - unescaped_chars_error |= block.non_quote_inside_string(unescaped); -} - -simdjson_inline error_code json_structural_indexer::finish(dom_parser_implementation &parser, size_t idx, size_t len, stage1_mode partial) { - // Write out the final iteration's structurals - indexer.write(uint32_t(idx-64), prev_structurals); - error_code error = scanner.finish(); - // We deliberately break down the next expression so that it is - // human readable. - const bool should_we_exit = is_streaming(partial) ? - ((error != SUCCESS) && (error != UNCLOSED_STRING)) // when partial we tolerate UNCLOSED_STRING - : (error != SUCCESS); // if partial is false, we must have SUCCESS - const bool have_unclosed_string = (error == UNCLOSED_STRING); - if (simdjson_unlikely(should_we_exit)) { return error; } - - if (unescaped_chars_error) { - return UNESCAPED_CHARS; - } - parser.n_structural_indexes = uint32_t(indexer.tail - parser.structural_indexes.get()); - /*** - * The On Demand API requires special padding. - * - * This is related to https://github.com/simdjson/simdjson/issues/906 - * Basically, we want to make sure that if the parsing continues beyond the last (valid) - * structural character, it quickly stops. - * Only three structural characters can be repeated without triggering an error in JSON: [,] and }. - * We repeat the padding character (at 'len'). We don't know what it is, but if the parsing - * continues, then it must be [,] or }. - * Suppose it is ] or }. We backtrack to the first character, what could it be that would - * not trigger an error? It could be ] or } but no, because you can't start a document that way. - * It can't be a comma, a colon or any simple value. So the only way we could continue is - * if the repeated character is [. But if so, the document must start with [. But if the document - * starts with [, it should end with ]. If we enforce that rule, then we would get - * ][[ which is invalid. - * - * This is illustrated with the test array_iterate_unclosed_error() on the following input: - * R"({ "a": [,,)" - **/ - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); // used later in partial == stage1_mode::streaming_final - parser.structural_indexes[parser.n_structural_indexes + 1] = uint32_t(len); - parser.structural_indexes[parser.n_structural_indexes + 2] = 0; - parser.next_structural_index = 0; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - return EMPTY; - } - if (simdjson_unlikely(parser.structural_indexes[parser.n_structural_indexes - 1] > len)) { - return UNEXPECTED_ERROR; - } - if (partial == stage1_mode::streaming_partial) { - // If we have an unclosed string, then the last structural - // will be the quote and we want to make sure to omit it. - if(have_unclosed_string) { - parser.n_structural_indexes--; - // a valid JSON file cannot have zero structural indexes - we should have found something - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { return CAPACITY; } - } - // We truncate the input to the end of the last complete document (or zero). - auto new_structural_indexes = find_next_document_index(parser); - if (new_structural_indexes == 0 && parser.n_structural_indexes > 0) { - if(parser.structural_indexes[0] == 0) { - // If the buffer is partial and we started at index 0 but the document is - // incomplete, it's too big to parse. - return CAPACITY; - } else { - // It is possible that the document could be parsed, we just had a lot - // of white space. - parser.n_structural_indexes = 0; - return EMPTY; - } - } - - parser.n_structural_indexes = new_structural_indexes; - } else if (partial == stage1_mode::streaming_final) { - if(have_unclosed_string) { parser.n_structural_indexes--; } - // We truncate the input to the end of the last complete document (or zero). - // Because partial == stage1_mode::streaming_final, it means that we may - // silently ignore trailing garbage. Though it sounds bad, we do it - // deliberately because many people who have streams of JSON documents - // will truncate them for processing. E.g., imagine that you are uncompressing - // the data from a size file or receiving it in chunks from the network. You - // may not know where exactly the last document will be. Meanwhile the - // document_stream instances allow people to know the JSON documents they are - // parsing (see the iterator.source() method). - parser.n_structural_indexes = find_next_document_index(parser); - // We store the initial n_structural_indexes so that the client can see - // whether we used truncation. If initial_n_structural_indexes == parser.n_structural_indexes, - // then this will query parser.structural_indexes[parser.n_structural_indexes] which is len, - // otherwise, it will copy some prior index. - parser.structural_indexes[parser.n_structural_indexes + 1] = parser.structural_indexes[parser.n_structural_indexes]; - // This next line is critical, do not change it unless you understand what you are - // doing. - parser.structural_indexes[parser.n_structural_indexes] = uint32_t(len); - if (simdjson_unlikely(parser.n_structural_indexes == 0u)) { - // We tolerate an unclosed string at the very end of the stream. Indeed, users - // often load their data in bulk without being careful and they want us to ignore - // the trailing garbage. - return EMPTY; - } - } - checker.check_eof(); - return checker.errors(); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/json_structural_indexer.h */ -/* begin file src/generic/stage1/utf8_validator.h */ -namespace simdjson { -namespace westmere { -namespace { -namespace stage1 { - -/** - * Validates that the string is actual UTF-8. - */ -template -bool generic_validate_utf8(const uint8_t * input, size_t length) { - checker c{}; - buf_block_reader<64> reader(input, length); - while (reader.has_full_block()) { - simd::simd8x64 in(reader.full_block()); - c.check_next_input(in); - reader.advance(); - } - uint8_t block[64]{}; - reader.get_remainder(block); - simd::simd8x64 in(block); - c.check_next_input(in); - reader.advance(); - c.check_eof(); - return c.errors() == error_code::SUCCESS; -} - -bool generic_validate_utf8(const char * input, size_t length) { - return generic_validate_utf8(reinterpret_cast(input),length); -} - -} // namespace stage1 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage1/utf8_validator.h */ - -// -// Stage 2 -// -/* begin file src/generic/stage2/stringparsing.h */ -// This file contains the common code every implementation uses -// It is intended to be included multiple times and compiled multiple times - -namespace simdjson { -namespace westmere { -namespace { -/// @private -namespace stringparsing { - -// begin copypasta -// These chars yield themselves: " \ / -// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab -// u not handled in this table as it's complex -static const uint8_t escape_map[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4. - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5. - 0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6. - 0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7. - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -// handle a unicode codepoint -// write appropriate values into dest -// src will advance 6 bytes or 12 bytes -// dest will advance a variable amount (return via pointer) -// return true if the unicode codepoint was valid -// We work in little-endian then swap at write time -simdjson_warn_unused -simdjson_inline bool handle_unicode_codepoint(const uint8_t **src_ptr, - uint8_t **dst_ptr) { - // jsoncharutils::hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the - // conversion isn't valid; we defer the check for this to inside the - // multilingual plane check - uint32_t code_point = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - *src_ptr += 6; - - // If we found a high surrogate, we must - // check for low surrogate for characters - // outside the Basic - // Multilingual Plane. - if (code_point >= 0xd800 && code_point < 0xdc00) { - if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') { - return false; - } - uint32_t code_point_2 = jsoncharutils::hex_to_u32_nocheck(*src_ptr + 2); - - // if the first code point is invalid we will get here, as we will go past - // the check for being outside the Basic Multilingual plane. If we don't - // find a \u immediately afterwards we fail out anyhow, but if we do, - // this check catches both the case of the first code point being invalid - // or the second code point being invalid. - if ((code_point | code_point_2) >> 16) { - return false; - } - - code_point = - (((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000; - *src_ptr += 6; - } else if (code_point >= 0xdc00 && code_point <= 0xdfff) { - // If we encounter a low surrogate (not preceded by a high surrogate) - // then we have an error. - return false; - } - size_t offset = jsoncharutils::codepoint_to_utf8(code_point, *dst_ptr); - *dst_ptr += offset; - return offset > 0; -} - -/** - * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There - * must be an unescaped quote terminating the string. It returns the final output - * position as pointer. In case of error (e.g., the string has bad escaped codes), - * then null_nullptrptr is returned. It is assumed that the output buffer is large - * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + - * SIMDJSON_PADDING bytes. - */ -simdjson_warn_unused simdjson_inline uint8_t *parse_string(const uint8_t *src, uint8_t *dst) { - while (1) { - // Copy the next n bytes, and find the backslash and quote in them. - auto bs_quote = backslash_and_quote::copy_and_find(src, dst); - // If the next thing is the end quote, copy and return - if (bs_quote.has_quote_first()) { - // we encountered quotes first. Move dst to point to quotes and exit - return dst + bs_quote.quote_index(); - } - if (bs_quote.has_backslash()) { - /* find out where the backspace is */ - auto bs_dist = bs_quote.backslash_index(); - uint8_t escape_char = src[bs_dist + 1]; - /* we encountered backslash first. Handle backslash */ - if (escape_char == 'u') { - /* move src/dst up to the start; they will be further adjusted - within the unicode codepoint handling code. */ - src += bs_dist; - dst += bs_dist; - if (!handle_unicode_codepoint(&src, &dst)) { - return nullptr; - } - } else { - /* simple 1:1 conversion. Will eat bs_dist+2 characters in input and - * write bs_dist+1 characters to output - * note this may reach beyond the part of the buffer we've actually - * seen. I think this is ok */ - uint8_t escape_result = escape_map[escape_char]; - if (escape_result == 0u) { - return nullptr; /* bogus escape value is an error */ - } - dst[bs_dist] = escape_result; - src += bs_dist + 2; - dst += bs_dist + 1; - } - } else { - /* they are the same. Since they can't co-occur, it means we - * encountered neither. */ - src += backslash_and_quote::BYTES_PROCESSED; - dst += backslash_and_quote::BYTES_PROCESSED; - } - } - /* can't be reached */ - return nullptr; -} - -} // namespace stringparsing -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage2/stringparsing.h */ -/* begin file src/generic/stage2/tape_builder.h */ -/* begin file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/logger.h */ -// This is for an internal-only stage 2 specific logger. -// Set LOG_ENABLED = true to log what stage 2 is doing! -namespace simdjson { -namespace westmere { -namespace { -namespace logger { - - static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; - -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; -#else - static constexpr const bool LOG_ENABLED = false; -#endif - static constexpr const int LOG_EVENT_LEN = 20; - static constexpr const int LOG_BUFFER_LEN = 30; - static constexpr const int LOG_SMALL_BUFFER_LEN = 10; - static constexpr const int LOG_INDEX_LEN = 5; - - static int log_depth; // Not threadsafe. Log only. - - // Helper to turn unprintable or newline characters into spaces - static simdjson_inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } - } - - // Print the header and set up log_start - static simdjson_inline void log_start() { - if (LOG_ENABLED) { - log_depth = 0; - printf("\n"); - printf("| %-*s | %-*s | %-*s | %-*s | Detail |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", LOG_SMALL_BUFFER_LEN, "Next", 5, "Next#"); - printf("|%.*s|%.*s|%.*s|%.*s|--------|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, LOG_SMALL_BUFFER_LEN+2, DASHES, 5+2, DASHES); - } - } - - simdjson_unused static simdjson_inline void log_string(const char *message) { - if (LOG_ENABLED) { - printf("%s\n", message); - } - } - - // Logs a single line from the stage 2 DOM parser - template - static simdjson_inline void log_line(S &structurals, const char *title_prefix, const char *title, const char *detail) { - if (LOG_ENABLED) { - printf("| %*s%s%-*s ", log_depth*2, "", title_prefix, LOG_EVENT_LEN - log_depth*2 - int(strlen(title_prefix)), title); - auto current_index = structurals.at_beginning() ? nullptr : structurals.next_structural-1; - auto next_index = structurals.next_structural; - auto current = current_index ? &structurals.buf[*current_index] : reinterpret_cast(" "); - auto next = &structurals.buf[*next_index]; - { - // Print the next N characters in the buffer. - printf("| "); - // Otherwise, print the characters starting from the buffer position. - // Print spaces for unprintable or newline characters. - for (int i=0;i - simdjson_warn_unused simdjson_inline error_code walk_document(V &visitor) noexcept; - - /** - * Create an iterator capable of walking a JSON document. - * - * The document must have already passed through stage 1. - */ - simdjson_inline json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index); - - /** - * Look at the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *peek() const noexcept; - /** - * Advance to the next token. - * - * Tokens can be strings, numbers, booleans, null, or operators (`[{]},:`)). - * - * They may include invalid JSON as well (such as `1.2.3` or `ture`). - */ - simdjson_inline const uint8_t *advance() noexcept; - /** - * Get the remaining length of the document, from the start of the current token. - */ - simdjson_inline size_t remaining_len() const noexcept; - /** - * Check if we are at the end of the document. - * - * If this is true, there are no more tokens. - */ - simdjson_inline bool at_eof() const noexcept; - /** - * Check if we are at the beginning of the document. - */ - simdjson_inline bool at_beginning() const noexcept; - simdjson_inline uint8_t last_structural() const noexcept; - - /** - * Log that a value has been found. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_value(const char *type) const noexcept; - /** - * Log the start of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_start_value(const char *type) const noexcept; - /** - * Log the end of a multipart value. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_end_value(const char *type) const noexcept; - /** - * Log an error. - * - * Set LOG_ENABLED=true in logger.h to see logging. - */ - simdjson_inline void log_error(const char *error) const noexcept; - - template - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(V &visitor, const uint8_t *value) noexcept; - template - simdjson_warn_unused simdjson_inline error_code visit_primitive(V &visitor, const uint8_t *value) noexcept; -}; - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::walk_document(V &visitor) noexcept { - logger::log_start(); - - // - // Start the document - // - if (at_eof()) { return EMPTY; } - log_start_value("document"); - SIMDJSON_TRY( visitor.visit_document_start(*this) ); - - // - // Read first value - // - { - auto value = advance(); - - // Make sure the outer object or array is closed before continuing; otherwise, there are ways we - // could get into memory corruption. See https://github.com/simdjson/simdjson/issues/906 - if (!STREAMING) { - switch (*value) { - case '{': if (last_structural() != '}') { log_value("starting brace unmatched"); return TAPE_ERROR; }; break; - case '[': if (last_structural() != ']') { log_value("starting bracket unmatched"); return TAPE_ERROR; }; break; - } - } - - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_root_primitive(*this, value) ); break; - } - } - goto document_end; - -// -// Object parser states -// -object_begin: - log_start_value("object"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = false; - SIMDJSON_TRY( visitor.visit_object_start(*this) ); - - { - auto key = advance(); - if (*key != '"') { log_error("Object does not start with a key"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.increment_count(*this) ); - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - -object_field: - if (simdjson_unlikely( *advance() != ':' )) { log_error("Missing colon after key in object"); return TAPE_ERROR; } - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -object_continue: - switch (*advance()) { - case ',': - SIMDJSON_TRY( visitor.increment_count(*this) ); - { - auto key = advance(); - if (simdjson_unlikely( *key != '"' )) { log_error("Key string missing at beginning of field in object"); return TAPE_ERROR; } - SIMDJSON_TRY( visitor.visit_key(*this, key) ); - } - goto object_field; - case '}': log_end_value("object"); SIMDJSON_TRY( visitor.visit_object_end(*this) ); goto scope_end; - default: log_error("No comma between object fields"); return TAPE_ERROR; - } - -scope_end: - depth--; - if (depth == 0) { goto document_end; } - if (dom_parser.is_array[depth]) { goto array_continue; } - goto object_continue; - -// -// Array parser states -// -array_begin: - log_start_value("array"); - depth++; - if (depth >= dom_parser.max_depth()) { log_error("Exceeded max depth!"); return DEPTH_ERROR; } - dom_parser.is_array[depth] = true; - SIMDJSON_TRY( visitor.visit_array_start(*this) ); - SIMDJSON_TRY( visitor.increment_count(*this) ); - -array_value: - { - auto value = advance(); - switch (*value) { - case '{': if (*peek() == '}') { advance(); log_value("empty object"); SIMDJSON_TRY( visitor.visit_empty_object(*this) ); break; } goto object_begin; - case '[': if (*peek() == ']') { advance(); log_value("empty array"); SIMDJSON_TRY( visitor.visit_empty_array(*this) ); break; } goto array_begin; - default: SIMDJSON_TRY( visitor.visit_primitive(*this, value) ); break; - } - } - -array_continue: - switch (*advance()) { - case ',': SIMDJSON_TRY( visitor.increment_count(*this) ); goto array_value; - case ']': log_end_value("array"); SIMDJSON_TRY( visitor.visit_array_end(*this) ); goto scope_end; - default: log_error("Missing comma between array values"); return TAPE_ERROR; - } - -document_end: - log_end_value("document"); - SIMDJSON_TRY( visitor.visit_document_end(*this) ); - - dom_parser.next_structural_index = uint32_t(next_structural - &dom_parser.structural_indexes[0]); - - // If we didn't make it to the end, it's an error - if ( !STREAMING && dom_parser.next_structural_index != dom_parser.n_structural_indexes ) { - log_error("More than one JSON value at the root of the document, or extra characters at the end of the JSON!"); - return TAPE_ERROR; - } - - return SUCCESS; - -} // walk_document() - -simdjson_inline json_iterator::json_iterator(dom_parser_implementation &_dom_parser, size_t start_structural_index) - : buf{_dom_parser.buf}, - next_structural{&_dom_parser.structural_indexes[start_structural_index]}, - dom_parser{_dom_parser} { -} - -simdjson_inline const uint8_t *json_iterator::peek() const noexcept { - return &buf[*(next_structural)]; -} -simdjson_inline const uint8_t *json_iterator::advance() noexcept { - return &buf[*(next_structural++)]; -} -simdjson_inline size_t json_iterator::remaining_len() const noexcept { - return dom_parser.len - *(next_structural-1); -} - -simdjson_inline bool json_iterator::at_eof() const noexcept { - return next_structural == &dom_parser.structural_indexes[dom_parser.n_structural_indexes]; -} -simdjson_inline bool json_iterator::at_beginning() const noexcept { - return next_structural == dom_parser.structural_indexes.get(); -} -simdjson_inline uint8_t json_iterator::last_structural() const noexcept { - return buf[dom_parser.structural_indexes[dom_parser.n_structural_indexes - 1]]; -} - -simdjson_inline void json_iterator::log_value(const char *type) const noexcept { - logger::log_line(*this, "", type, ""); -} - -simdjson_inline void json_iterator::log_start_value(const char *type) const noexcept { - logger::log_line(*this, "+", type, ""); - if (logger::LOG_ENABLED) { logger::log_depth++; } -} - -simdjson_inline void json_iterator::log_end_value(const char *type) const noexcept { - if (logger::LOG_ENABLED) { logger::log_depth--; } - logger::log_line(*this, "-", type, ""); -} - -simdjson_inline void json_iterator::log_error(const char *error) const noexcept { - logger::log_line(*this, "", "ERROR", error); -} - -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_root_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_root_string(*this, value); - case 't': return visitor.visit_root_true_atom(*this, value); - case 'f': return visitor.visit_root_false_atom(*this, value); - case 'n': return visitor.visit_root_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_root_number(*this, value); - default: - log_error("Document starts with a non-value character"); - return TAPE_ERROR; - } -} -template -simdjson_warn_unused simdjson_inline error_code json_iterator::visit_primitive(V &visitor, const uint8_t *value) noexcept { - switch (*value) { - case '"': return visitor.visit_string(*this, value); - case 't': return visitor.visit_true_atom(*this, value); - case 'f': return visitor.visit_false_atom(*this, value); - case 'n': return visitor.visit_null_atom(*this, value); - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return visitor.visit_number(*this, value); - default: - log_error("Non-value found when value was expected!"); - return TAPE_ERROR; - } -} - -} // namespace stage2 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage2/json_iterator.h */ -/* begin file src/generic/stage2/tape_writer.h */ -namespace simdjson { -namespace westmere { -namespace { -namespace stage2 { - -struct tape_writer { - /** The next place to write to tape */ - uint64_t *next_tape_loc; - - /** Write a signed 64-bit value to tape. */ - simdjson_inline void append_s64(int64_t value) noexcept; - - /** Write an unsigned 64-bit value to tape. */ - simdjson_inline void append_u64(uint64_t value) noexcept; - - /** Write a double value to tape. */ - simdjson_inline void append_double(double value) noexcept; - - /** - * Append a tape entry (an 8-bit type,and 56 bits worth of value). - */ - simdjson_inline void append(uint64_t val, internal::tape_type t) noexcept; - - /** - * Skip the current tape entry without writing. - * - * Used to skip the start of the container, since we'll come back later to fill it in when the - * container ends. - */ - simdjson_inline void skip() noexcept; - - /** - * Skip the number of tape entries necessary to write a large u64 or i64. - */ - simdjson_inline void skip_large_integer() noexcept; - - /** - * Skip the number of tape entries necessary to write a double. - */ - simdjson_inline void skip_double() noexcept; - - /** - * Write a value to a known location on tape. - * - * Used to go back and write out the start of a container after the container ends. - */ - simdjson_inline static void write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept; - -private: - /** - * Append both the tape entry, and a supplementary value following it. Used for types that need - * all 64 bits, such as double and uint64_t. - */ - template - simdjson_inline void append2(uint64_t val, T val2, internal::tape_type t) noexcept; -}; // struct number_writer - -simdjson_inline void tape_writer::append_s64(int64_t value) noexcept { - append2(0, value, internal::tape_type::INT64); -} - -simdjson_inline void tape_writer::append_u64(uint64_t value) noexcept { - append(0, internal::tape_type::UINT64); - *next_tape_loc = value; - next_tape_loc++; -} - -/** Write a double value to tape. */ -simdjson_inline void tape_writer::append_double(double value) noexcept { - append2(0, value, internal::tape_type::DOUBLE); -} - -simdjson_inline void tape_writer::skip() noexcept { - next_tape_loc++; -} - -simdjson_inline void tape_writer::skip_large_integer() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::skip_double() noexcept { - next_tape_loc += 2; -} - -simdjson_inline void tape_writer::append(uint64_t val, internal::tape_type t) noexcept { - *next_tape_loc = val | ((uint64_t(char(t))) << 56); - next_tape_loc++; -} - -template -simdjson_inline void tape_writer::append2(uint64_t val, T val2, internal::tape_type t) noexcept { - append(val, t); - static_assert(sizeof(val2) == sizeof(*next_tape_loc), "Type is not 64 bits!"); - memcpy(next_tape_loc, &val2, sizeof(val2)); - next_tape_loc++; -} - -simdjson_inline void tape_writer::write(uint64_t &tape_loc, uint64_t val, internal::tape_type t) noexcept { - tape_loc = val | ((uint64_t(char(t))) << 56); -} - -} // namespace stage2 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage2/tape_writer.h */ - -namespace simdjson { -namespace westmere { -namespace { -namespace stage2 { - -struct tape_builder { - template - simdjson_warn_unused static simdjson_inline error_code parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept; - - /** Called when a non-empty document starts. */ - simdjson_warn_unused simdjson_inline error_code visit_document_start(json_iterator &iter) noexcept; - /** Called when a non-empty document ends without error. */ - simdjson_warn_unused simdjson_inline error_code visit_document_end(json_iterator &iter) noexcept; - - /** Called when a non-empty array starts. */ - simdjson_warn_unused simdjson_inline error_code visit_array_start(json_iterator &iter) noexcept; - /** Called when a non-empty array ends. */ - simdjson_warn_unused simdjson_inline error_code visit_array_end(json_iterator &iter) noexcept; - /** Called when an empty array is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_array(json_iterator &iter) noexcept; - - /** Called when a non-empty object starts. */ - simdjson_warn_unused simdjson_inline error_code visit_object_start(json_iterator &iter) noexcept; - /** - * Called when a key in a field is encountered. - * - * primitive, visit_object_start, visit_empty_object, visit_array_start, or visit_empty_array - * will be called after this with the field value. - */ - simdjson_warn_unused simdjson_inline error_code visit_key(json_iterator &iter, const uint8_t *key) noexcept; - /** Called when a non-empty object ends. */ - simdjson_warn_unused simdjson_inline error_code visit_object_end(json_iterator &iter) noexcept; - /** Called when an empty object is found. */ - simdjson_warn_unused simdjson_inline error_code visit_empty_object(json_iterator &iter) noexcept; - - /** - * Called when a string, number, boolean or null is found. - */ - simdjson_warn_unused simdjson_inline error_code visit_primitive(json_iterator &iter, const uint8_t *value) noexcept; - /** - * Called when a string, number, boolean or null is found at the top level of a document (i.e. - * when there is no array or object and the entire document is a single string, number, boolean or - * null. - * - * This is separate from primitive() because simdjson's normal primitive parsing routines assume - * there is at least one more token after the value, which is only true in an array or object. - */ - simdjson_warn_unused simdjson_inline error_code visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_string(json_iterator &iter, const uint8_t *value, bool key = false) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - simdjson_warn_unused simdjson_inline error_code visit_root_string(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_number(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept; - simdjson_warn_unused simdjson_inline error_code visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept; - - /** Called each time a new field or element in an array or object is found. */ - simdjson_warn_unused simdjson_inline error_code increment_count(json_iterator &iter) noexcept; - - /** Next location to write to tape */ - tape_writer tape; -private: - /** Next write location in the string buf for stage 2 parsing */ - uint8_t *current_string_buf_loc; - - simdjson_inline tape_builder(dom::document &doc) noexcept; - - simdjson_inline uint32_t next_tape_index(json_iterator &iter) const noexcept; - simdjson_inline void start_container(json_iterator &iter) noexcept; - simdjson_warn_unused simdjson_inline error_code end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_warn_unused simdjson_inline error_code empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept; - simdjson_inline uint8_t *on_start_string(json_iterator &iter) noexcept; - simdjson_inline void on_end_string(uint8_t *dst) noexcept; -}; // class tape_builder - -template -simdjson_warn_unused simdjson_inline error_code tape_builder::parse_document( - dom_parser_implementation &dom_parser, - dom::document &doc) noexcept { - dom_parser.doc = &doc; - json_iterator iter(dom_parser, STREAMING ? dom_parser.next_structural_index : 0); - tape_builder builder(doc); - return iter.walk_document(builder); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_root_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_primitive(json_iterator &iter, const uint8_t *value) noexcept { - return iter.visit_primitive(*this, value); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_object(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_empty_array(json_iterator &iter) noexcept { - return empty_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_start(json_iterator &iter) noexcept { - start_container(iter); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_object_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_OBJECT, internal::tape_type::END_OBJECT); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_array_end(json_iterator &iter) noexcept { - return end_container(iter, internal::tape_type::START_ARRAY, internal::tape_type::END_ARRAY); -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_document_end(json_iterator &iter) noexcept { - constexpr uint32_t start_tape_index = 0; - tape.append(start_tape_index, internal::tape_type::ROOT); - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter), internal::tape_type::ROOT); - return SUCCESS; -} -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_key(json_iterator &iter, const uint8_t *key) noexcept { - return visit_string(iter, key, true); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::increment_count(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].count++; // we have a key value pair in the object at parser.dom_parser.depth - 1 - return SUCCESS; -} - -simdjson_inline tape_builder::tape_builder(dom::document &doc) noexcept : tape{doc.tape.get()}, current_string_buf_loc{doc.string_buf.get()} {} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_string(json_iterator &iter, const uint8_t *value, bool key) noexcept { - iter.log_value(key ? "key" : "string"); - uint8_t *dst = on_start_string(iter); - dst = stringparsing::parse_string(value+1, dst); - if (dst == nullptr) { - iter.log_error("Invalid escape in string"); - return STRING_ERROR; - } - on_end_string(dst); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_string(json_iterator &iter, const uint8_t *value) noexcept { - return visit_string(iter, value); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_number(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("number"); - return numberparsing::parse_number(value, tape); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_number(json_iterator &iter, const uint8_t *value) noexcept { - // - // We need to make a copy to make sure that the string is space terminated. - // This is not about padding the input, which should already padded up - // to len + SIMDJSON_PADDING. However, we have no control at this stage - // on how the padding was done. What if the input string was padded with nulls? - // It is quite common for an input string to have an extra null character (C string). - // We do not want to allow 9\0 (where \0 is the null character) inside a JSON - // document, but the string "9\0" by itself is fine. So we make a copy and - // pad the input with spaces when we know that there is just one input element. - // This copy is relatively expensive, but it will almost never be called in - // practice unless you are in the strange scenario where you have many JSON - // documents made of single atoms. - // - std::unique_ptrcopy(new (std::nothrow) uint8_t[iter.remaining_len() + SIMDJSON_PADDING]); - if (copy.get() == nullptr) { return MEMALLOC; } - std::memcpy(copy.get(), value, iter.remaining_len()); - std::memset(copy.get() + iter.remaining_len(), ' ', SIMDJSON_PADDING); - error_code error = visit_number(iter, copy.get()); - return error; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value)) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_true_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("true"); - if (!atomparsing::is_valid_true_atom(value, iter.remaining_len())) { return T_ATOM_ERROR; } - tape.append(0, internal::tape_type::TRUE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value)) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_false_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("false"); - if (!atomparsing::is_valid_false_atom(value, iter.remaining_len())) { return F_ATOM_ERROR; } - tape.append(0, internal::tape_type::FALSE_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value)) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::visit_root_null_atom(json_iterator &iter, const uint8_t *value) noexcept { - iter.log_value("null"); - if (!atomparsing::is_valid_null_atom(value, iter.remaining_len())) { return N_ATOM_ERROR; } - tape.append(0, internal::tape_type::NULL_VALUE); - return SUCCESS; -} - -// private: - -simdjson_inline uint32_t tape_builder::next_tape_index(json_iterator &iter) const noexcept { - return uint32_t(tape.next_tape_loc - iter.dom_parser.doc->tape.get()); -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::empty_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - auto start_index = next_tape_index(iter); - tape.append(start_index+2, start); - tape.append(start_index, end); - return SUCCESS; -} - -simdjson_inline void tape_builder::start_container(json_iterator &iter) noexcept { - iter.dom_parser.open_containers[iter.depth].tape_index = next_tape_index(iter); - iter.dom_parser.open_containers[iter.depth].count = 0; - tape.skip(); // We don't actually *write* the start element until the end. -} - -simdjson_warn_unused simdjson_inline error_code tape_builder::end_container(json_iterator &iter, internal::tape_type start, internal::tape_type end) noexcept { - // Write the ending tape element, pointing at the start location - const uint32_t start_tape_index = iter.dom_parser.open_containers[iter.depth].tape_index; - tape.append(start_tape_index, end); - // Write the start tape element, pointing at the end location (and including count) - // count can overflow if it exceeds 24 bits... so we saturate - // the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff). - const uint32_t count = iter.dom_parser.open_containers[iter.depth].count; - const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count; - tape_writer::write(iter.dom_parser.doc->tape[start_tape_index], next_tape_index(iter) | (uint64_t(cntsat) << 32), start); - return SUCCESS; -} - -simdjson_inline uint8_t *tape_builder::on_start_string(json_iterator &iter) noexcept { - // we advance the point, accounting for the fact that we have a NULL termination - tape.append(current_string_buf_loc - iter.dom_parser.doc->string_buf.get(), internal::tape_type::STRING); - return current_string_buf_loc + sizeof(uint32_t); -} - -simdjson_inline void tape_builder::on_end_string(uint8_t *dst) noexcept { - uint32_t str_length = uint32_t(dst - (current_string_buf_loc + sizeof(uint32_t))); - // TODO check for overflow in case someone has a crazy string (>=4GB?) - // But only add the overflow check when the document itself exceeds 4GB - // Currently unneeded because we refuse to parse docs larger or equal to 4GB. - memcpy(current_string_buf_loc, &str_length, sizeof(uint32_t)); - // NULL termination is still handy if you expect all your strings to - // be NULL terminated? It comes at a small cost - *dst = 0; - current_string_buf_loc = dst + 1; -} - -} // namespace stage2 -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file src/generic/stage2/tape_builder.h */ - -// -// Implementation-specific overrides -// - -namespace simdjson { -namespace westmere { -namespace { -namespace stage1 { - -simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { - if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } - return find_escaped_branchless(backslash); -} - -} // namespace stage1 -} // unnamed namespace - -simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { - return westmere::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { - this->buf = _buf; - this->len = _len; - return westmere::stage1::json_structural_indexer::index<64>(_buf, _len, *this, streaming); -} - -simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { - return westmere::stage1::generic_validate_utf8(buf,len); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { - return stage2::tape_builder::parse_document(*this, _doc); -} - -simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst) const noexcept { - return westmere::stringparsing::parse_string(src, dst); -} - -simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { - auto error = stage1(_buf, _len, stage1_mode::regular); - if (error) { return error; } - return stage2(_doc); -} - -} // namespace westmere -} // namespace simdjson - -/* begin file include/simdjson/westmere/end.h */ -SIMDJSON_UNTARGET_WESTMERE -/* end file include/simdjson/westmere/end.h */ -/* end file src/westmere/dom_parser_implementation.cpp */ -#endif - -SIMDJSON_POP_DISABLE_WARNINGS -/* end file src/simdjson.cpp */ diff --git a/csrc/simdjson/simdjson.h b/csrc/simdjson/simdjson.h deleted file mode 100644 index dd045b6..0000000 --- a/csrc/simdjson/simdjson.h +++ /dev/null @@ -1,31436 +0,0 @@ -/* auto-generated on 2022-07-28 21:45:54 -0400. Do not edit! */ -/* begin file include/simdjson.h */ -#ifndef SIMDJSON_H -#define SIMDJSON_H - -/** - * @mainpage - * - * Check the [README.md](https://github.com/simdjson/simdjson/blob/master/README.md#simdjson--parsing-gigabytes-of-json-per-second). - * - * Sample code. See https://github.com/simdjson/simdjson/blob/master/doc/basics.md for more examples. - - #include "simdjson.h" - - int main(void) { - // load from `twitter.json` file: - simdjson::dom::parser parser; - simdjson::dom::element tweets = parser.load("twitter.json"); - std::cout << tweets["search_metadata"]["count"] << " results." << std::endl; - - // Parse and iterate through an array of objects - auto abstract_json = R"( [ - { "12345" : {"a":12.34, "b":56.78, "c": 9998877} }, - { "12545" : {"a":11.44, "b":12.78, "c": 11111111} } - ] )"_padded; - - for (simdjson::dom::object obj : parser.parse(abstract_json)) { - for(const auto key_value : obj) { - cout << "key: " << key_value.key << " : "; - simdjson::dom::object innerobj = key_value.value; - cout << "a: " << double(innerobj["a"]) << ", "; - cout << "b: " << double(innerobj["b"]) << ", "; - cout << "c: " << int64_t(innerobj["c"]) << endl; - } - } - } - */ - -/* begin file include/simdjson/simdjson_version.h */ -// /include/simdjson/simdjson_version.h automatically generated by release.py, -// do not change by hand -#ifndef SIMDJSON_SIMDJSON_VERSION_H -#define SIMDJSON_SIMDJSON_VERSION_H - -/** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION 2.2.2 - -namespace simdjson { -enum { - /** - * The major version (MAJOR.minor.revision) of simdjson being used. - */ - SIMDJSON_VERSION_MAJOR = 2, - /** - * The minor version (major.MINOR.revision) of simdjson being used. - */ - SIMDJSON_VERSION_MINOR = 2, - /** - * The revision (major.minor.REVISION) of simdjson being used. - */ - SIMDJSON_VERSION_REVISION = 2 -}; -} // namespace simdjson - -#endif // SIMDJSON_SIMDJSON_VERSION_H -/* end file include/simdjson/simdjson_version.h */ -/* begin file include/simdjson/dom.h */ -#ifndef SIMDJSON_DOM_H -#define SIMDJSON_DOM_H - -/* begin file include/simdjson/base.h */ -#ifndef SIMDJSON_BASE_H -#define SIMDJSON_BASE_H - -/* begin file include/simdjson/compiler_check.h */ -#ifndef SIMDJSON_COMPILER_CHECK_H -#define SIMDJSON_COMPILER_CHECK_H - -#ifndef __cplusplus -#error simdjson requires a C++ compiler -#endif - -#ifndef SIMDJSON_CPLUSPLUS -#if defined(_MSVC_LANG) && !defined(__clang__) -#define SIMDJSON_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) -#else -#define SIMDJSON_CPLUSPLUS __cplusplus -#endif -#endif - -// C++ 17 -#if !defined(SIMDJSON_CPLUSPLUS17) && (SIMDJSON_CPLUSPLUS >= 201703L) -#define SIMDJSON_CPLUSPLUS17 1 -#endif - -// C++ 14 -#if !defined(SIMDJSON_CPLUSPLUS14) && (SIMDJSON_CPLUSPLUS >= 201402L) -#define SIMDJSON_CPLUSPLUS14 1 -#endif - -// C++ 11 -#if !defined(SIMDJSON_CPLUSPLUS11) && (SIMDJSON_CPLUSPLUS >= 201103L) -#define SIMDJSON_CPLUSPLUS11 1 -#endif - -#ifndef SIMDJSON_CPLUSPLUS11 -#error simdjson requires a compiler compliant with the C++11 standard -#endif - -#endif // SIMDJSON_COMPILER_CHECK_H -/* end file include/simdjson/compiler_check.h */ -/* begin file include/simdjson/common_defs.h */ -#ifndef SIMDJSON_COMMON_DEFS_H -#define SIMDJSON_COMMON_DEFS_H - -#include -/* begin file include/simdjson/portability.h */ -#ifndef SIMDJSON_PORTABILITY_H -#define SIMDJSON_PORTABILITY_H - -#include -#include -#include -#include -#include -#ifndef _WIN32 -// strcasecmp, strncasecmp -#include -#endif - -#ifdef _MSC_VER -#define SIMDJSON_VISUAL_STUDIO 1 -/** - * We want to differentiate carefully between - * clang under visual studio and regular visual - * studio. - * - * Under clang for Windows, we enable: - * * target pragmas so that part and only part of the - * code gets compiled for advanced instructions. - * - */ -#ifdef __clang__ -// clang under visual studio -#define SIMDJSON_CLANG_VISUAL_STUDIO 1 -#else -// just regular visual studio (best guess) -#define SIMDJSON_REGULAR_VISUAL_STUDIO 1 -#endif // __clang__ -#endif // _MSC_VER - -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO -// https://en.wikipedia.org/wiki/C_alternative_tokens -// This header should have no effect, except maybe -// under Visual Studio. -#include -#endif - -#if defined(__x86_64__) || defined(_M_AMD64) -#define SIMDJSON_IS_X86_64 1 -#elif defined(__aarch64__) || defined(_M_ARM64) -#define SIMDJSON_IS_ARM64 1 -#elif defined(__PPC64__) || defined(_M_PPC64) -#define SIMDJSON_IS_PPC64 1 -#else -#define SIMDJSON_IS_32BITS 1 - -// We do not support 32-bit platforms, but it can be -// handy to identify them. -#if defined(_M_IX86) || defined(__i386__) -#define SIMDJSON_IS_X86_32BITS 1 -#elif defined(__arm__) || defined(_M_ARM) -#define SIMDJSON_IS_ARM_32BITS 1 -#elif defined(__PPC__) || defined(_M_PPC) -#define SIMDJSON_IS_PPC_32BITS 1 -#endif - -#endif // defined(__x86_64__) || defined(_M_AMD64) - -#ifdef SIMDJSON_IS_32BITS -#ifndef SIMDJSON_NO_PORTABILITY_WARNING -#pragma message("The simdjson library is designed \ -for 64-bit processors and it seems that you are not \ -compiling for a known 64-bit platform. All fast kernels \ -will be disabled and performance may be poor. Please \ -use a 64-bit target such as x64, 64-bit ARM or 64-bit PPC.") -#endif // SIMDJSON_NO_PORTABILITY_WARNING -#endif // SIMDJSON_IS_32BITS - -// this is almost standard? -#undef SIMDJSON_STRINGIFY_IMPLEMENTATION_ -#undef SIMDJSON_STRINGIFY -#define SIMDJSON_STRINGIFY_IMPLEMENTATION_(a) #a -#define SIMDJSON_STRINGIFY(a) SIMDJSON_STRINGIFY_IMPLEMENTATION_(a) - -// Our fast kernels require 64-bit systems. -// -// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions. -// Furthermore, the number of SIMD registers is reduced. -// -// On 32-bit ARM, we would have smaller registers. -// -// The simdjson users should still have the fallback kernel. It is -// slower, but it should run everywhere. - -// -// Enable valid runtime implementations, and select SIMDJSON_BUILTIN_IMPLEMENTATION -// - -// We are going to use runtime dispatch. -#ifdef SIMDJSON_IS_X86_64 -#ifdef __clang__ -// clang does not have GCC push pop -// warning: clang attribute push can't be used within a namespace in clang up -// til 8.0 so SIMDJSON_TARGET_REGION and SIMDJSON_UNTARGET_REGION must be *outside* of a -// namespace. -#define SIMDJSON_TARGET_REGION(T) \ - _Pragma(SIMDJSON_STRINGIFY( \ - clang attribute push(__attribute__((target(T))), apply_to = function))) -#define SIMDJSON_UNTARGET_REGION _Pragma("clang attribute pop") -#elif defined(__GNUC__) -// GCC is easier -#define SIMDJSON_TARGET_REGION(T) \ - _Pragma("GCC push_options") _Pragma(SIMDJSON_STRINGIFY(GCC target(T))) -#define SIMDJSON_UNTARGET_REGION _Pragma("GCC pop_options") -#endif // clang then gcc - -#endif // x86 - -// Default target region macros don't do anything. -#ifndef SIMDJSON_TARGET_REGION -#define SIMDJSON_TARGET_REGION(T) -#define SIMDJSON_UNTARGET_REGION -#endif - -// Is threading enabled? -#if defined(_REENTRANT) || defined(_MT) -#ifndef SIMDJSON_THREADS_ENABLED -#define SIMDJSON_THREADS_ENABLED -#endif -#endif - -// workaround for large stack sizes under -O0. -// https://github.com/simdjson/simdjson/issues/691 -#ifdef __APPLE__ -#ifndef __OPTIMIZE__ -// Apple systems have small stack sizes in secondary threads. -// Lack of compiler optimization may generate high stack usage. -// Users may want to disable threads for safety, but only when -// in debug mode which we detect by the fact that the __OPTIMIZE__ -// macro is not defined. -#undef SIMDJSON_THREADS_ENABLED -#endif -#endif - - -#if defined(__clang__) -#define SIMDJSON_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined"))) -#elif defined(__GNUC__) -#define SIMDJSON_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined)) -#else -#define SIMDJSON_NO_SANITIZE_UNDEFINED -#endif - -#ifdef SIMDJSON_VISUAL_STUDIO -// This is one case where we do not distinguish between -// regular visual studio and clang under visual studio. -// clang under Windows has _stricmp (like visual studio) but not strcasecmp (as clang normally has) -#define simdjson_strcasecmp _stricmp -#define simdjson_strncasecmp _strnicmp -#else -// The strcasecmp, strncasecmp, and strcasestr functions do not work with multibyte strings (e.g. UTF-8). -// So they are only useful for ASCII in our context. -// https://www.gnu.org/software/libunistring/manual/libunistring.html#char-_002a-strings -#define simdjson_strcasecmp strcasecmp -#define simdjson_strncasecmp strncasecmp -#endif - -#ifdef NDEBUG - -#ifdef SIMDJSON_VISUAL_STUDIO -#define SIMDJSON_UNREACHABLE() __assume(0) -#define SIMDJSON_ASSUME(COND) __assume(COND) -#else -#define SIMDJSON_UNREACHABLE() __builtin_unreachable(); -#define SIMDJSON_ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0) -#endif - -#else // NDEBUG - -#define SIMDJSON_UNREACHABLE() assert(0); -#define SIMDJSON_ASSUME(COND) assert(COND) - -#endif - -#endif // SIMDJSON_PORTABILITY_H -/* end file include/simdjson/portability.h */ - -namespace simdjson { - -namespace internal { -/** - * @private - * Our own implementation of the C++17 to_chars function. - * Defined in src/to_chars - */ -char *to_chars(char *first, const char *last, double value); -/** - * @private - * A number parsing routine. - * Defined in src/from_chars - */ -double from_chars(const char *first) noexcept; -double from_chars(const char *first, const char* end) noexcept; - -} - -#ifndef SIMDJSON_EXCEPTIONS -#if __cpp_exceptions -#define SIMDJSON_EXCEPTIONS 1 -#else -#define SIMDJSON_EXCEPTIONS 0 -#endif -#endif - -/** The maximum document size supported by simdjson. */ -constexpr size_t SIMDJSON_MAXSIZE_BYTES = 0xFFFFFFFF; - -/** - * The amount of padding needed in a buffer to parse JSON. - * - * The input buf should be readable up to buf + SIMDJSON_PADDING - * this is a stopgap; there should be a better description of the - * main loop and its behavior that abstracts over this - * See https://github.com/simdjson/simdjson/issues/174 - */ -constexpr size_t SIMDJSON_PADDING = 64; - -/** - * By default, simdjson supports this many nested objects and arrays. - * - * This is the default for parser::max_depth(). - */ -constexpr size_t DEFAULT_MAX_DEPTH = 1024; - -} // namespace simdjson - -#if defined(__GNUC__) - // Marks a block with a name so that MCA analysis can see it. - #define SIMDJSON_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name); - #define SIMDJSON_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name); - #define SIMDJSON_DEBUG_BLOCK(name, block) BEGIN_DEBUG_BLOCK(name); block; END_DEBUG_BLOCK(name); -#else - #define SIMDJSON_BEGIN_DEBUG_BLOCK(name) - #define SIMDJSON_END_DEBUG_BLOCK(name) - #define SIMDJSON_DEBUG_BLOCK(name, block) -#endif - -// Align to N-byte boundary -#define SIMDJSON_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1)) -#define SIMDJSON_ROUNDDOWN_N(a, n) ((a) & ~((n)-1)) - -#define SIMDJSON_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0) - -#if defined(SIMDJSON_REGULAR_VISUAL_STUDIO) - - #define simdjson_really_inline __forceinline - #define simdjson_never_inline __declspec(noinline) - - #define simdjson_unused - #define simdjson_warn_unused - - #ifndef simdjson_likely - #define simdjson_likely(x) x - #endif - #ifndef simdjson_unlikely - #define simdjson_unlikely(x) x - #endif - - #define SIMDJSON_PUSH_DISABLE_WARNINGS __pragma(warning( push )) - #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS __pragma(warning( push, 0 )) - #define SIMDJSON_DISABLE_VS_WARNING(WARNING_NUMBER) __pragma(warning( disable : WARNING_NUMBER )) - // Get rid of Intellisense-only warnings (Code Analysis) - // Though __has_include is C++17, it is supported in Visual Studio 2017 or better (_MSC_VER>=1910). - #ifdef __has_include - #if __has_include() - #include - #define SIMDJSON_DISABLE_UNDESIRED_WARNINGS SIMDJSON_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS) - #endif - #endif - - #ifndef SIMDJSON_DISABLE_UNDESIRED_WARNINGS - #define SIMDJSON_DISABLE_UNDESIRED_WARNINGS - #endif - - #define SIMDJSON_DISABLE_DEPRECATED_WARNING SIMDJSON_DISABLE_VS_WARNING(4996) - #define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING - #define SIMDJSON_POP_DISABLE_WARNINGS __pragma(warning( pop )) - -#else // SIMDJSON_REGULAR_VISUAL_STUDIO - - #define simdjson_really_inline inline __attribute__((always_inline)) - #define simdjson_never_inline inline __attribute__((noinline)) - - #define simdjson_unused __attribute__((unused)) - #define simdjson_warn_unused __attribute__((warn_unused_result)) - - #ifndef simdjson_likely - #define simdjson_likely(x) __builtin_expect(!!(x), 1) - #endif - #ifndef simdjson_unlikely - #define simdjson_unlikely(x) __builtin_expect(!!(x), 0) - #endif - - #define SIMDJSON_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push") - // gcc doesn't seem to disable all warnings with all and extra, add warnings here as necessary - // We do it separately for clang since it has different warnings. - #ifdef __clang__ - // clang is missing -Wmaybe-uninitialized. - #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \ - SIMDJSON_DISABLE_GCC_WARNING(-Weffc++) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wall) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wconversion) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wextra) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wattributes) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wreturn-type) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) - #else // __clang__ - #define SIMDJSON_PUSH_DISABLE_ALL_WARNINGS SIMDJSON_PUSH_DISABLE_WARNINGS \ - SIMDJSON_DISABLE_GCC_WARNING(-Weffc++) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wall) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wconversion) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wextra) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wattributes) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wreturn-type) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wshadow) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wunused-parameter) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wunused-variable) \ - SIMDJSON_DISABLE_GCC_WARNING(-Wmaybe-uninitialized) - #endif // __clang__ - - #define SIMDJSON_PRAGMA(P) _Pragma(#P) - #define SIMDJSON_DISABLE_GCC_WARNING(WARNING) SIMDJSON_PRAGMA(GCC diagnostic ignored #WARNING) - #if defined(SIMDJSON_CLANG_VISUAL_STUDIO) - #define SIMDJSON_DISABLE_UNDESIRED_WARNINGS SIMDJSON_DISABLE_GCC_WARNING(-Wmicrosoft-include) - #else - #define SIMDJSON_DISABLE_UNDESIRED_WARNINGS - #endif - #define SIMDJSON_DISABLE_DEPRECATED_WARNING SIMDJSON_DISABLE_GCC_WARNING(-Wdeprecated-declarations) - #define SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING SIMDJSON_DISABLE_GCC_WARNING(-Wstrict-overflow) - #define SIMDJSON_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop") - - - -#endif // MSC_VER - -#if defined(simdjson_inline) - // Prefer the user's definition of simdjson_inline; don't define it ourselves. -#elif defined(__GNUC__) && !defined(__OPTIMIZE__) - // If optimizations are disabled, forcing inlining can lead to significant - // code bloat and high compile times. Don't use simdjson_really_inline for - // unoptimized builds. - #define simdjson_inline inline -#else - // Force inlining for most simdjson functions. - #define simdjson_inline simdjson_really_inline -#endif - -#if defined(SIMDJSON_VISUAL_STUDIO) - /** - * Windows users need to do some extra work when building - * or using a dynamic library (DLL). When building, we need - * to set SIMDJSON_DLLIMPORTEXPORT to __declspec(dllexport). - * When *using* the DLL, the user needs to set - * SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport). - * - * Static libraries not need require such work. - * - * It does not matter here whether you are using - * the regular visual studio or clang under visual - * studio, you still need to handle these issues. - * - * Non-Windows systems do not have this complexity. - */ - #if SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY - // We set SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY when we build a DLL under Windows. - // It should never happen that both SIMDJSON_BUILDING_WINDOWS_DYNAMIC_LIBRARY and - // SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY are set. - #define SIMDJSON_DLLIMPORTEXPORT __declspec(dllexport) - #elif SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY - // Windows user who call a dynamic library should set SIMDJSON_USING_WINDOWS_DYNAMIC_LIBRARY to 1. - #define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) - #else - // We assume by default static linkage - #define SIMDJSON_DLLIMPORTEXPORT - #endif - -/** - * Workaround for the vcpkg package manager. Only vcpkg should - * ever touch the next line. The SIMDJSON_USING_LIBRARY macro is otherwise unused. - */ -#if SIMDJSON_USING_LIBRARY -#define SIMDJSON_DLLIMPORTEXPORT __declspec(dllimport) -#endif -/** - * End of workaround for the vcpkg package manager. - */ -#else - #define SIMDJSON_DLLIMPORTEXPORT -#endif - -// C++17 requires string_view. -#if SIMDJSON_CPLUSPLUS17 -#define SIMDJSON_HAS_STRING_VIEW -#include // by the standard, this has to be safe. -#endif - -// This macro (__cpp_lib_string_view) has to be defined -// for C++17 and better, but if it is otherwise defined, -// we are going to assume that string_view is available -// even if we do not have C++17 support. -#ifdef __cpp_lib_string_view -#define SIMDJSON_HAS_STRING_VIEW -#endif - -// Some systems have string_view even if we do not have C++17 support, -// and even if __cpp_lib_string_view is undefined, it is the case -// with Apple clang version 11. -// We must handle it. *This is important.* -#ifndef SIMDJSON_HAS_STRING_VIEW -#if defined __has_include -// do not combine the next #if with the previous one (unsafe) -#if __has_include () -// now it is safe to trigger the include -#include // though the file is there, it does not follow that we got the implementation -#if defined(_LIBCPP_STRING_VIEW) -// Ah! So we under libc++ which under its Library Fundamentals Technical Specification, which preceded C++17, -// included string_view. -// This means that we have string_view *even though* we may not have C++17. -#define SIMDJSON_HAS_STRING_VIEW -#endif // _LIBCPP_STRING_VIEW -#endif // __has_include () -#endif // defined __has_include -#endif // def SIMDJSON_HAS_STRING_VIEW -// end of complicated but important routine to try to detect string_view. - -// -// Backfill std::string_view using nonstd::string_view on systems where -// we expect that string_view is missing. Important: if we get this wrong, -// we will end up with two string_view definitions and potential trouble. -// That is why we work so hard above to avoid it. -// -#ifndef SIMDJSON_HAS_STRING_VIEW -SIMDJSON_PUSH_DISABLE_ALL_WARNINGS -/* begin file include/simdjson/nonstd/string_view.hpp */ -// Copyright 2017-2020 by Martin Moene -// -// string-view lite, a C++17-like string_view for C++98 and later. -// For more information see https://github.com/martinmoene/string-view-lite -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#pragma once - -#ifndef NONSTD_SV_LITE_H_INCLUDED -#define NONSTD_SV_LITE_H_INCLUDED - -#define string_view_lite_MAJOR 1 -#define string_view_lite_MINOR 6 -#define string_view_lite_PATCH 0 - -#define string_view_lite_VERSION nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH) - -#define nssv_STRINGIFY( x ) nssv_STRINGIFY_( x ) -#define nssv_STRINGIFY_( x ) #x - -// string-view lite configuration: - -#define nssv_STRING_VIEW_DEFAULT 0 -#define nssv_STRING_VIEW_NONSTD 1 -#define nssv_STRING_VIEW_STD 2 - -// tweak header support: - -#ifdef __has_include -# if __has_include() -# include -# endif -#define nssv_HAVE_TWEAK_HEADER 1 -#else -#define nssv_HAVE_TWEAK_HEADER 0 -//# pragma message("string_view.hpp: Note: Tweak header not supported.") -#endif - -// string_view selection and configuration: - -#if !defined( nssv_CONFIG_SELECT_STRING_VIEW ) -# define nssv_CONFIG_SELECT_STRING_VIEW ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD ) -#endif - -#ifndef nssv_CONFIG_STD_SV_OPERATOR -# define nssv_CONFIG_STD_SV_OPERATOR 0 -#endif - -#ifndef nssv_CONFIG_USR_SV_OPERATOR -# define nssv_CONFIG_USR_SV_OPERATOR 1 -#endif - -#ifdef nssv_CONFIG_CONVERSION_STD_STRING -# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS nssv_CONFIG_CONVERSION_STD_STRING -# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS nssv_CONFIG_CONVERSION_STD_STRING -#endif - -#ifndef nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS -# define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS 1 -#endif - -#ifndef nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS -# define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS 1 -#endif - -#ifndef nssv_CONFIG_NO_STREAM_INSERTION -# define nssv_CONFIG_NO_STREAM_INSERTION 0 -#endif - -// Control presence of exception handling (try and auto discover): - -#ifndef nssv_CONFIG_NO_EXCEPTIONS -# if _MSC_VER -# include // for _HAS_EXCEPTIONS -# endif -# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) -# define nssv_CONFIG_NO_EXCEPTIONS 0 -# else -# define nssv_CONFIG_NO_EXCEPTIONS 1 -# endif -#endif - -// C++ language version detection (C++20 is speculative): -// Note: VC14.0/1900 (VS2015) lacks too much from C++14. - -#ifndef nssv_CPLUSPLUS -# if defined(_MSVC_LANG ) && !defined(__clang__) -# define nssv_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) -# else -# define nssv_CPLUSPLUS __cplusplus -# endif -#endif - -#define nssv_CPP98_OR_GREATER ( nssv_CPLUSPLUS >= 199711L ) -#define nssv_CPP11_OR_GREATER ( nssv_CPLUSPLUS >= 201103L ) -#define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L ) -#define nssv_CPP14_OR_GREATER ( nssv_CPLUSPLUS >= 201402L ) -#define nssv_CPP17_OR_GREATER ( nssv_CPLUSPLUS >= 201703L ) -#define nssv_CPP20_OR_GREATER ( nssv_CPLUSPLUS >= 202000L ) - -// use C++17 std::string_view if available and requested: - -#if nssv_CPP17_OR_GREATER && defined(__has_include ) -# if __has_include( ) -# define nssv_HAVE_STD_STRING_VIEW 1 -# else -# define nssv_HAVE_STD_STRING_VIEW 0 -# endif -#else -# define nssv_HAVE_STD_STRING_VIEW 0 -#endif - -#define nssv_USES_STD_STRING_VIEW ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) ) - -#define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW ) -#define nssv_HAVE_ENDS_WITH nssv_HAVE_STARTS_WITH - -// -// Use C++17 std::string_view: -// - -#if nssv_USES_STD_STRING_VIEW - -#include - -// Extensions for std::string: - -#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - -namespace nonstd { - -template< class CharT, class Traits, class Allocator = std::allocator > -std::basic_string -to_string( std::basic_string_view v, Allocator const & a = Allocator() ) -{ - return std::basic_string( v.begin(), v.end(), a ); -} - -template< class CharT, class Traits, class Allocator > -std::basic_string_view -to_string_view( std::basic_string const & s ) -{ - return std::basic_string_view( s.data(), s.size() ); -} - -// Literal operators sv and _sv: - -#if nssv_CONFIG_STD_SV_OPERATOR - -using namespace std::literals::string_view_literals; - -#endif - -#if nssv_CONFIG_USR_SV_OPERATOR - -inline namespace literals { -inline namespace string_view_literals { - - -constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept // (1) -{ - return std::string_view{ str, len }; -} - -constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept // (2) -{ - return std::u16string_view{ str, len }; -} - -constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept // (3) -{ - return std::u32string_view{ str, len }; -} - -constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept // (4) -{ - return std::wstring_view{ str, len }; -} - -}} // namespace literals::string_view_literals - -#endif // nssv_CONFIG_USR_SV_OPERATOR - -} // namespace nonstd - -#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - -namespace nonstd { - -using std::string_view; -using std::wstring_view; -using std::u16string_view; -using std::u32string_view; -using std::basic_string_view; - -// literal "sv" and "_sv", see above - -using std::operator==; -using std::operator!=; -using std::operator<; -using std::operator<=; -using std::operator>; -using std::operator>=; - -using std::operator<<; - -} // namespace nonstd - -#else // nssv_HAVE_STD_STRING_VIEW - -// -// Before C++17: use string_view lite: -// - -// Compiler versions: -// -// MSVC++ 6.0 _MSC_VER == 1200 nssv_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) -// MSVC++ 7.0 _MSC_VER == 1300 nssv_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) -// MSVC++ 7.1 _MSC_VER == 1310 nssv_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) -// MSVC++ 8.0 _MSC_VER == 1400 nssv_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) -// MSVC++ 9.0 _MSC_VER == 1500 nssv_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) -// MSVC++ 10.0 _MSC_VER == 1600 nssv_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) -// MSVC++ 11.0 _MSC_VER == 1700 nssv_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) -// MSVC++ 12.0 _MSC_VER == 1800 nssv_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) -// MSVC++ 14.0 _MSC_VER == 1900 nssv_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) -// MSVC++ 14.1 _MSC_VER >= 1910 nssv_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) -// MSVC++ 14.2 _MSC_VER >= 1920 nssv_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) - -#if defined(_MSC_VER ) && !defined(__clang__) -# define nssv_COMPILER_MSVC_VER (_MSC_VER ) -# define nssv_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) -#else -# define nssv_COMPILER_MSVC_VER 0 -# define nssv_COMPILER_MSVC_VERSION 0 -#endif - -#define nssv_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) - -#if defined( __apple_build_version__ ) -# define nssv_COMPILER_APPLECLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) -# define nssv_COMPILER_CLANG_VERSION 0 -#elif defined( __clang__ ) -# define nssv_COMPILER_APPLECLANG_VERSION 0 -# define nssv_COMPILER_CLANG_VERSION nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) -#else -# define nssv_COMPILER_APPLECLANG_VERSION 0 -# define nssv_COMPILER_CLANG_VERSION 0 -#endif - -#if defined(__GNUC__) && !defined(__clang__) -# define nssv_COMPILER_GNUC_VERSION nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#else -# define nssv_COMPILER_GNUC_VERSION 0 -#endif - -// half-open range [lo..hi): -#define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) - -// Presence of language and library features: - -#ifdef _HAS_CPP0X -# define nssv_HAS_CPP0X _HAS_CPP0X -#else -# define nssv_HAS_CPP0X 0 -#endif - -// Unless defined otherwise below, consider VC14 as C++11 for variant-lite: - -#if nssv_COMPILER_MSVC_VER >= 1900 -# undef nssv_CPP11_OR_GREATER -# define nssv_CPP11_OR_GREATER 1 -#endif - -#define nssv_CPP11_90 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500) -#define nssv_CPP11_100 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600) -#define nssv_CPP11_110 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700) -#define nssv_CPP11_120 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800) -#define nssv_CPP11_140 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900) -#define nssv_CPP11_141 (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910) - -#define nssv_CPP14_000 (nssv_CPP14_OR_GREATER) -#define nssv_CPP17_000 (nssv_CPP17_OR_GREATER) - -// Presence of C++11 language features: - -#define nssv_HAVE_CONSTEXPR_11 nssv_CPP11_140 -#define nssv_HAVE_EXPLICIT_CONVERSION nssv_CPP11_140 -#define nssv_HAVE_INLINE_NAMESPACE nssv_CPP11_140 -#define nssv_HAVE_NOEXCEPT nssv_CPP11_140 -#define nssv_HAVE_NULLPTR nssv_CPP11_100 -#define nssv_HAVE_REF_QUALIFIER nssv_CPP11_140 -#define nssv_HAVE_UNICODE_LITERALS nssv_CPP11_140 -#define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140 -#define nssv_HAVE_WCHAR16_T nssv_CPP11_100 -#define nssv_HAVE_WCHAR32_T nssv_CPP11_100 - -#if ! ( ( nssv_CPP11_OR_GREATER && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) ) -# define nssv_HAVE_STD_DEFINED_LITERALS nssv_CPP11_140 -#else -# define nssv_HAVE_STD_DEFINED_LITERALS 0 -#endif - -// Presence of C++14 language features: - -#define nssv_HAVE_CONSTEXPR_14 nssv_CPP14_000 - -// Presence of C++17 language features: - -#define nssv_HAVE_NODISCARD nssv_CPP17_000 - -// Presence of C++ library features: - -#define nssv_HAVE_STD_HASH nssv_CPP11_120 - -// Presence of compiler intrinsics: - -// Providing char-type specializations for compare() and length() that -// use compiler intrinsics can improve compile- and run-time performance. -// -// The challenge is in using the right combinations of builtin availability -// and its constexpr-ness. -// -// | compiler | __builtin_memcmp (constexpr) | memcmp (constexpr) | -// |----------|------------------------------|---------------------| -// | clang | 4.0 (>= 4.0 ) | any (? ) | -// | clang-a | 9.0 (>= 9.0 ) | any (? ) | -// | gcc | any (constexpr) | any (? ) | -// | msvc | >= 14.2 C++17 (>= 14.2 ) | any (? ) | - -#define nssv_HAVE_BUILTIN_VER ( (nssv_CPP17_000 && nssv_COMPILER_MSVC_VERSION >= 142) || nssv_COMPILER_GNUC_VERSION > 0 || nssv_COMPILER_CLANG_VERSION >= 400 || nssv_COMPILER_APPLECLANG_VERSION >= 900 ) -#define nssv_HAVE_BUILTIN_CE ( nssv_HAVE_BUILTIN_VER ) - -#define nssv_HAVE_BUILTIN_MEMCMP ( (nssv_HAVE_CONSTEXPR_14 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_14 ) -#define nssv_HAVE_BUILTIN_STRLEN ( (nssv_HAVE_CONSTEXPR_11 && nssv_HAVE_BUILTIN_CE) || !nssv_HAVE_CONSTEXPR_11 ) - -#ifdef __has_builtin -# define nssv_HAVE_BUILTIN( x ) __has_builtin( x ) -#else -# define nssv_HAVE_BUILTIN( x ) 0 -#endif - -#if nssv_HAVE_BUILTIN(__builtin_memcmp) || nssv_HAVE_BUILTIN_VER -# define nssv_BUILTIN_MEMCMP __builtin_memcmp -#else -# define nssv_BUILTIN_MEMCMP memcmp -#endif - -#if nssv_HAVE_BUILTIN(__builtin_strlen) || nssv_HAVE_BUILTIN_VER -# define nssv_BUILTIN_STRLEN __builtin_strlen -#else -# define nssv_BUILTIN_STRLEN strlen -#endif - -// C++ feature usage: - -#if nssv_HAVE_CONSTEXPR_11 -# define nssv_constexpr constexpr -#else -# define nssv_constexpr /*constexpr*/ -#endif - -#if nssv_HAVE_CONSTEXPR_14 -# define nssv_constexpr14 constexpr -#else -# define nssv_constexpr14 /*constexpr*/ -#endif - -#if nssv_HAVE_EXPLICIT_CONVERSION -# define nssv_explicit explicit -#else -# define nssv_explicit /*explicit*/ -#endif - -#if nssv_HAVE_INLINE_NAMESPACE -# define nssv_inline_ns inline -#else -# define nssv_inline_ns /*inline*/ -#endif - -#if nssv_HAVE_NOEXCEPT -# define nssv_noexcept noexcept -#else -# define nssv_noexcept /*noexcept*/ -#endif - -//#if nssv_HAVE_REF_QUALIFIER -//# define nssv_ref_qual & -//# define nssv_refref_qual && -//#else -//# define nssv_ref_qual /*&*/ -//# define nssv_refref_qual /*&&*/ -//#endif - -#if nssv_HAVE_NULLPTR -# define nssv_nullptr nullptr -#else -# define nssv_nullptr NULL -#endif - -#if nssv_HAVE_NODISCARD -# define nssv_nodiscard [[nodiscard]] -#else -# define nssv_nodiscard /*[[nodiscard]]*/ -#endif - -// Additional includes: - -#include -#include -#include -#include -#include // std::char_traits<> - -#if ! nssv_CONFIG_NO_STREAM_INSERTION -# include -#endif - -#if ! nssv_CONFIG_NO_EXCEPTIONS -# include -#endif - -#if nssv_CPP11_OR_GREATER -# include -#endif - -// Clang, GNUC, MSVC warning suppression macros: - -#if defined(__clang__) -# pragma clang diagnostic ignored "-Wreserved-user-defined-literal" -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wuser-defined-literals" -#elif defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wliteral-suffix" -#endif // __clang__ - -#if nssv_COMPILER_MSVC_VERSION >= 140 -# define nssv_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] -# define nssv_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) -# define nssv_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) -#else -# define nssv_SUPPRESS_MSGSL_WARNING(expr) -# define nssv_SUPPRESS_MSVC_WARNING(code, descr) -# define nssv_DISABLE_MSVC_WARNINGS(codes) -#endif - -#if defined(__clang__) -# define nssv_RESTORE_WARNINGS() _Pragma("clang diagnostic pop") -#elif defined(__GNUC__) -# define nssv_RESTORE_WARNINGS() _Pragma("GCC diagnostic pop") -#elif nssv_COMPILER_MSVC_VERSION >= 140 -# define nssv_RESTORE_WARNINGS() __pragma(warning(pop )) -#else -# define nssv_RESTORE_WARNINGS() -#endif - -// Suppress the following MSVC (GSL) warnings: -// - C4455, non-gsl : 'operator ""sv': literal suffix identifiers that do not -// start with an underscore are reserved -// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; -// use brace initialization, gsl::narrow_cast or gsl::narow -// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead - -nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 ) -//nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" ) -//nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix ) - -namespace nonstd { namespace sv_lite { - -namespace detail { - -// support constexpr comparison in C++14; -// for C++17 and later, use provided traits: - -template< typename CharT > -inline nssv_constexpr14 int compare( CharT const * s1, CharT const * s2, std::size_t count ) -{ - while ( count-- != 0 ) - { - if ( *s1 < *s2 ) return -1; - if ( *s1 > *s2 ) return +1; - ++s1; ++s2; - } - return 0; -} - -#if nssv_HAVE_BUILTIN_MEMCMP - -// specialization of compare() for char, see also generic compare() above: - -inline nssv_constexpr14 int compare( char const * s1, char const * s2, std::size_t count ) -{ - return nssv_BUILTIN_MEMCMP( s1, s2, count ); -} - -#endif - -#if nssv_HAVE_BUILTIN_STRLEN - -// specialization of length() for char, see also generic length() further below: - -inline nssv_constexpr std::size_t length( char const * s ) -{ - return nssv_BUILTIN_STRLEN( s ); -} - -#endif - -#if defined(__OPTIMIZE__) - -// gcc, clang provide __OPTIMIZE__ -// Expect tail call optimization to make length() non-recursive: - -template< typename CharT > -inline nssv_constexpr std::size_t length( CharT * s, std::size_t result = 0 ) -{ - return *s == '\0' ? result : length( s + 1, result + 1 ); -} - -#else // OPTIMIZE - -// non-recursive: - -template< typename CharT > -inline nssv_constexpr14 std::size_t length( CharT * s ) -{ - std::size_t result = 0; - while ( *s++ != '\0' ) - { - ++result; - } - return result; -} - -#endif // OPTIMIZE - -} // namespace detail - -template -< - class CharT, - class Traits = std::char_traits -> -class basic_string_view; - -// -// basic_string_view: -// - -template -< - class CharT, - class Traits /* = std::char_traits */ -> -class basic_string_view -{ -public: - // Member types: - - typedef Traits traits_type; - typedef CharT value_type; - - typedef CharT * pointer; - typedef CharT const * const_pointer; - typedef CharT & reference; - typedef CharT const & const_reference; - - typedef const_pointer iterator; - typedef const_pointer const_iterator; - typedef std::reverse_iterator< const_iterator > reverse_iterator; - typedef std::reverse_iterator< const_iterator > const_reverse_iterator; - - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - - // 24.4.2.1 Construction and assignment: - - nssv_constexpr basic_string_view() nssv_noexcept - : data_( nssv_nullptr ) - , size_( 0 ) - {} - -#if nssv_CPP11_OR_GREATER - nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default; -#else - nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept - : data_( other.data_) - , size_( other.size_) - {} -#endif - - nssv_constexpr basic_string_view( CharT const * s, size_type count ) nssv_noexcept // non-standard noexcept - : data_( s ) - , size_( count ) - {} - - nssv_constexpr basic_string_view( CharT const * s) nssv_noexcept // non-standard noexcept - : data_( s ) -#if nssv_CPP17_OR_GREATER - , size_( Traits::length(s) ) -#elif nssv_CPP11_OR_GREATER - , size_( detail::length(s) ) -#else - , size_( Traits::length(s) ) -#endif - {} - - // Assignment: - -#if nssv_CPP11_OR_GREATER - nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default; -#else - nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept - { - data_ = other.data_; - size_ = other.size_; - return *this; - } -#endif - - // 24.4.2.2 Iterator support: - - nssv_constexpr const_iterator begin() const nssv_noexcept { return data_; } - nssv_constexpr const_iterator end() const nssv_noexcept { return data_ + size_; } - - nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); } - nssv_constexpr const_iterator cend() const nssv_noexcept { return end(); } - - nssv_constexpr const_reverse_iterator rbegin() const nssv_noexcept { return const_reverse_iterator( end() ); } - nssv_constexpr const_reverse_iterator rend() const nssv_noexcept { return const_reverse_iterator( begin() ); } - - nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); } - nssv_constexpr const_reverse_iterator crend() const nssv_noexcept { return rend(); } - - // 24.4.2.3 Capacity: - - nssv_constexpr size_type size() const nssv_noexcept { return size_; } - nssv_constexpr size_type length() const nssv_noexcept { return size_; } - nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); } - - // since C++20 - nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept - { - return 0 == size_; - } - - // 24.4.2.4 Element access: - - nssv_constexpr const_reference operator[]( size_type pos ) const - { - return data_at( pos ); - } - - nssv_constexpr14 const_reference at( size_type pos ) const - { -#if nssv_CONFIG_NO_EXCEPTIONS - assert( pos < size() ); -#else - if ( pos >= size() ) - { - throw std::out_of_range("nonstd::string_view::at()"); - } -#endif - return data_at( pos ); - } - - nssv_constexpr const_reference front() const { return data_at( 0 ); } - nssv_constexpr const_reference back() const { return data_at( size() - 1 ); } - - nssv_constexpr const_pointer data() const nssv_noexcept { return data_; } - - // 24.4.2.5 Modifiers: - - nssv_constexpr14 void remove_prefix( size_type n ) - { - assert( n <= size() ); - data_ += n; - size_ -= n; - } - - nssv_constexpr14 void remove_suffix( size_type n ) - { - assert( n <= size() ); - size_ -= n; - } - - nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept - { - const basic_string_view tmp(other); - other = *this; - *this = tmp; - } - - // 24.4.2.6 String operations: - - size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const - { -#if nssv_CONFIG_NO_EXCEPTIONS - assert( pos <= size() ); -#else - if ( pos > size() ) - { - throw std::out_of_range("nonstd::string_view::copy()"); - } -#endif - const size_type rlen = (std::min)( n, size() - pos ); - - (void) Traits::copy( dest, data() + pos, rlen ); - - return rlen; - } - - nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const - { -#if nssv_CONFIG_NO_EXCEPTIONS - assert( pos <= size() ); -#else - if ( pos > size() ) - { - throw std::out_of_range("nonstd::string_view::substr()"); - } -#endif - return basic_string_view( data() + pos, (std::min)( n, size() - pos ) ); - } - - // compare(), 6x: - - nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1) - { -#if nssv_CPP17_OR_GREATER - if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) -#else - if ( const int result = detail::compare( data(), other.data(), (std::min)( size(), other.size() ) ) ) -#endif - { - return result; - } - - return size() == other.size() ? 0 : size() < other.size() ? -1 : 1; - } - - nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2) - { - return substr( pos1, n1 ).compare( other ); - } - - nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3) - { - return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) ); - } - - nssv_constexpr int compare( CharT const * s ) const // (4) - { - return compare( basic_string_view( s ) ); - } - - nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5) - { - return substr( pos1, n1 ).compare( basic_string_view( s ) ); - } - - nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6) - { - return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) ); - } - - // 24.4.2.7 Searching: - - // starts_with(), 3x, since C++20: - - nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept // (1) - { - return size() >= v.size() && compare( 0, v.size(), v ) == 0; - } - - nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept // (2) - { - return starts_with( basic_string_view( &c, 1 ) ); - } - - nssv_constexpr bool starts_with( CharT const * s ) const // (3) - { - return starts_with( basic_string_view( s ) ); - } - - // ends_with(), 3x, since C++20: - - nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept // (1) - { - return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0; - } - - nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept // (2) - { - return ends_with( basic_string_view( &c, 1 ) ); - } - - nssv_constexpr bool ends_with( CharT const * s ) const // (3) - { - return ends_with( basic_string_view( s ) ); - } - - // find(), 4x: - - nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) - { - return assert( v.size() == 0 || v.data() != nssv_nullptr ) - , pos >= size() - ? npos - : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); - } - - nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) - { - return find( basic_string_view( &c, 1 ), pos ); - } - - nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const // (3) - { - return find( basic_string_view( s, n ), pos ); - } - - nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const // (4) - { - return find( basic_string_view( s ), pos ); - } - - // rfind(), 4x: - - nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) - { - if ( size() < v.size() ) - { - return npos; - } - - if ( v.empty() ) - { - return (std::min)( size(), pos ); - } - - const_iterator last = cbegin() + (std::min)( size() - v.size(), pos ) + v.size(); - const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq ); - - return result != last ? size_type( result - cbegin() ) : npos; - } - - nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept // (2) - { - return rfind( basic_string_view( &c, 1 ), pos ); - } - - nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const // (3) - { - return rfind( basic_string_view( s, n ), pos ); - } - - nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const // (4) - { - return rfind( basic_string_view( s ), pos ); - } - - // find_first_of(), 4x: - - nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) - { - return pos >= size() - ? npos - : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) ); - } - - nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) - { - return find_first_of( basic_string_view( &c, 1 ), pos ); - } - - nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const // (3) - { - return find_first_of( basic_string_view( s, n ), pos ); - } - - nssv_constexpr size_type find_first_of( CharT const * s, size_type pos = 0 ) const // (4) - { - return find_first_of( basic_string_view( s ), pos ); - } - - // find_last_of(), 4x: - - nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) - { - return empty() - ? npos - : pos >= size() - ? find_last_of( v, size() - 1 ) - : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) ); - } - - nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) - { - return find_last_of( basic_string_view( &c, 1 ), pos ); - } - - nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const // (3) - { - return find_last_of( basic_string_view( s, count ), pos ); - } - - nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const // (4) - { - return find_last_of( basic_string_view( s ), pos ); - } - - // find_first_not_of(), 4x: - - nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept // (1) - { - return pos >= size() - ? npos - : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) ); - } - - nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept // (2) - { - return find_first_not_of( basic_string_view( &c, 1 ), pos ); - } - - nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const // (3) - { - return find_first_not_of( basic_string_view( s, count ), pos ); - } - - nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const // (4) - { - return find_first_not_of( basic_string_view( s ), pos ); - } - - // find_last_not_of(), 4x: - - nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept // (1) - { - return empty() - ? npos - : pos >= size() - ? find_last_not_of( v, size() - 1 ) - : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) ); - } - - nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept // (2) - { - return find_last_not_of( basic_string_view( &c, 1 ), pos ); - } - - nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const // (3) - { - return find_last_not_of( basic_string_view( s, count ), pos ); - } - - nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const // (4) - { - return find_last_not_of( basic_string_view( s ), pos ); - } - - // Constants: - -#if nssv_CPP17_OR_GREATER - static nssv_constexpr size_type npos = size_type(-1); -#elif nssv_CPP11_OR_GREATER - enum : size_type { npos = size_type(-1) }; -#else - enum { npos = size_type(-1) }; -#endif - -private: - struct not_in_view - { - const basic_string_view v; - - nssv_constexpr explicit not_in_view( basic_string_view v_ ) : v( v_ ) {} - - nssv_constexpr bool operator()( CharT c ) const - { - return npos == v.find_first_of( c ); - } - }; - - nssv_constexpr size_type to_pos( const_iterator it ) const - { - return it == cend() ? npos : size_type( it - cbegin() ); - } - - nssv_constexpr size_type to_pos( const_reverse_iterator it ) const - { - return it == crend() ? npos : size_type( crend() - it - 1 ); - } - - nssv_constexpr const_reference data_at( size_type pos ) const - { -#if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 ) - return data_[pos]; -#else - return assert( pos < size() ), data_[pos]; -#endif - } - -private: - const_pointer data_; - size_type size_; - -public: -#if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS - - template< class Allocator > - basic_string_view( std::basic_string const & s ) nssv_noexcept - : data_( s.data() ) - , size_( s.size() ) - {} - -#if nssv_HAVE_EXPLICIT_CONVERSION - - template< class Allocator > - explicit operator std::basic_string() const - { - return to_string( Allocator() ); - } - -#endif // nssv_HAVE_EXPLICIT_CONVERSION - -#if nssv_CPP11_OR_GREATER - - template< class Allocator = std::allocator > - std::basic_string - to_string( Allocator const & a = Allocator() ) const - { - return std::basic_string( begin(), end(), a ); - } - -#else - - std::basic_string - to_string() const - { - return std::basic_string( begin(), end() ); - } - - template< class Allocator > - std::basic_string - to_string( Allocator const & a ) const - { - return std::basic_string( begin(), end(), a ); - } - -#endif // nssv_CPP11_OR_GREATER - -#endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS -}; - -// -// Non-member functions: -// - -// 24.4.3 Non-member comparison functions: -// lexicographically compare two string views (function template): - -template< class CharT, class Traits > -nssv_constexpr bool operator== ( - basic_string_view lhs, - basic_string_view rhs ) nssv_noexcept -{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - -template< class CharT, class Traits > -nssv_constexpr bool operator!= ( - basic_string_view lhs, - basic_string_view rhs ) nssv_noexcept -{ return !( lhs == rhs ); } - -template< class CharT, class Traits > -nssv_constexpr bool operator< ( - basic_string_view lhs, - basic_string_view rhs ) nssv_noexcept -{ return lhs.compare( rhs ) < 0; } - -template< class CharT, class Traits > -nssv_constexpr bool operator<= ( - basic_string_view lhs, - basic_string_view rhs ) nssv_noexcept -{ return lhs.compare( rhs ) <= 0; } - -template< class CharT, class Traits > -nssv_constexpr bool operator> ( - basic_string_view lhs, - basic_string_view rhs ) nssv_noexcept -{ return lhs.compare( rhs ) > 0; } - -template< class CharT, class Traits > -nssv_constexpr bool operator>= ( - basic_string_view lhs, - basic_string_view rhs ) nssv_noexcept -{ return lhs.compare( rhs ) >= 0; } - -// Let S be basic_string_view, and sv be an instance of S. -// Implementations shall provide sufficient additional overloads marked -// constexpr and noexcept so that an object t with an implicit conversion -// to S can be compared according to Table 67. - -#if ! nssv_CPP11_OR_GREATER || nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 ) - -// accommodate for older compilers: - -// == - -template< class CharT, class Traits> -nssv_constexpr bool operator==( - basic_string_view lhs, - CharT const * rhs ) nssv_noexcept -{ return lhs.size() == detail::length( rhs ) && lhs.compare( rhs ) == 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator==( - CharT const * lhs, - basic_string_view rhs ) nssv_noexcept -{ return detail::length( lhs ) == rhs.size() && rhs.compare( lhs ) == 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator==( - basic_string_view lhs, - std::basic_string rhs ) nssv_noexcept -{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator==( - std::basic_string rhs, - basic_string_view lhs ) nssv_noexcept -{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - -// != - -template< class CharT, class Traits> -nssv_constexpr bool operator!=( - basic_string_view lhs, - CharT const * rhs ) nssv_noexcept -{ return !( lhs == rhs ); } - -template< class CharT, class Traits> -nssv_constexpr bool operator!=( - CharT const * lhs, - basic_string_view rhs ) nssv_noexcept -{ return !( lhs == rhs ); } - -template< class CharT, class Traits> -nssv_constexpr bool operator!=( - basic_string_view lhs, - std::basic_string rhs ) nssv_noexcept -{ return !( lhs == rhs ); } - -template< class CharT, class Traits> -nssv_constexpr bool operator!=( - std::basic_string rhs, - basic_string_view lhs ) nssv_noexcept -{ return !( lhs == rhs ); } - -// < - -template< class CharT, class Traits> -nssv_constexpr bool operator<( - basic_string_view lhs, - CharT const * rhs ) nssv_noexcept -{ return lhs.compare( rhs ) < 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator<( - CharT const * lhs, - basic_string_view rhs ) nssv_noexcept -{ return rhs.compare( lhs ) > 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator<( - basic_string_view lhs, - std::basic_string rhs ) nssv_noexcept -{ return lhs.compare( rhs ) < 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator<( - std::basic_string rhs, - basic_string_view lhs ) nssv_noexcept -{ return rhs.compare( lhs ) > 0; } - -// <= - -template< class CharT, class Traits> -nssv_constexpr bool operator<=( - basic_string_view lhs, - CharT const * rhs ) nssv_noexcept -{ return lhs.compare( rhs ) <= 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator<=( - CharT const * lhs, - basic_string_view rhs ) nssv_noexcept -{ return rhs.compare( lhs ) >= 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator<=( - basic_string_view lhs, - std::basic_string rhs ) nssv_noexcept -{ return lhs.compare( rhs ) <= 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator<=( - std::basic_string rhs, - basic_string_view lhs ) nssv_noexcept -{ return rhs.compare( lhs ) >= 0; } - -// > - -template< class CharT, class Traits> -nssv_constexpr bool operator>( - basic_string_view lhs, - CharT const * rhs ) nssv_noexcept -{ return lhs.compare( rhs ) > 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator>( - CharT const * lhs, - basic_string_view rhs ) nssv_noexcept -{ return rhs.compare( lhs ) < 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator>( - basic_string_view lhs, - std::basic_string rhs ) nssv_noexcept -{ return lhs.compare( rhs ) > 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator>( - std::basic_string rhs, - basic_string_view lhs ) nssv_noexcept -{ return rhs.compare( lhs ) < 0; } - -// >= - -template< class CharT, class Traits> -nssv_constexpr bool operator>=( - basic_string_view lhs, - CharT const * rhs ) nssv_noexcept -{ return lhs.compare( rhs ) >= 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator>=( - CharT const * lhs, - basic_string_view rhs ) nssv_noexcept -{ return rhs.compare( lhs ) <= 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator>=( - basic_string_view lhs, - std::basic_string rhs ) nssv_noexcept -{ return lhs.compare( rhs ) >= 0; } - -template< class CharT, class Traits> -nssv_constexpr bool operator>=( - std::basic_string rhs, - basic_string_view lhs ) nssv_noexcept -{ return rhs.compare( lhs ) <= 0; } - -#else // newer compilers: - -#define nssv_BASIC_STRING_VIEW_I(T,U) typename std::decay< basic_string_view >::type - -#if defined(_MSC_VER) // issue 40 -# define nssv_MSVC_ORDER(x) , int=x -#else -# define nssv_MSVC_ORDER(x) /*, int=x*/ -#endif - -// == - -template< class CharT, class Traits nssv_MSVC_ORDER(1) > -nssv_constexpr bool operator==( - basic_string_view lhs, - nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept -{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - -template< class CharT, class Traits nssv_MSVC_ORDER(2) > -nssv_constexpr bool operator==( - nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs, - basic_string_view rhs ) nssv_noexcept -{ return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; } - -// != - -template< class CharT, class Traits nssv_MSVC_ORDER(1) > -nssv_constexpr bool operator!= ( - basic_string_view < CharT, Traits > lhs, - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept -{ return !( lhs == rhs ); } - -template< class CharT, class Traits nssv_MSVC_ORDER(2) > -nssv_constexpr bool operator!= ( - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, - basic_string_view < CharT, Traits > rhs ) nssv_noexcept -{ return !( lhs == rhs ); } - -// < - -template< class CharT, class Traits nssv_MSVC_ORDER(1) > -nssv_constexpr bool operator< ( - basic_string_view < CharT, Traits > lhs, - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept -{ return lhs.compare( rhs ) < 0; } - -template< class CharT, class Traits nssv_MSVC_ORDER(2) > -nssv_constexpr bool operator< ( - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, - basic_string_view < CharT, Traits > rhs ) nssv_noexcept -{ return lhs.compare( rhs ) < 0; } - -// <= - -template< class CharT, class Traits nssv_MSVC_ORDER(1) > -nssv_constexpr bool operator<= ( - basic_string_view < CharT, Traits > lhs, - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept -{ return lhs.compare( rhs ) <= 0; } - -template< class CharT, class Traits nssv_MSVC_ORDER(2) > -nssv_constexpr bool operator<= ( - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, - basic_string_view < CharT, Traits > rhs ) nssv_noexcept -{ return lhs.compare( rhs ) <= 0; } - -// > - -template< class CharT, class Traits nssv_MSVC_ORDER(1) > -nssv_constexpr bool operator> ( - basic_string_view < CharT, Traits > lhs, - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept -{ return lhs.compare( rhs ) > 0; } - -template< class CharT, class Traits nssv_MSVC_ORDER(2) > -nssv_constexpr bool operator> ( - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, - basic_string_view < CharT, Traits > rhs ) nssv_noexcept -{ return lhs.compare( rhs ) > 0; } - -// >= - -template< class CharT, class Traits nssv_MSVC_ORDER(1) > -nssv_constexpr bool operator>= ( - basic_string_view < CharT, Traits > lhs, - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept -{ return lhs.compare( rhs ) >= 0; } - -template< class CharT, class Traits nssv_MSVC_ORDER(2) > -nssv_constexpr bool operator>= ( - nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs, - basic_string_view < CharT, Traits > rhs ) nssv_noexcept -{ return lhs.compare( rhs ) >= 0; } - -#undef nssv_MSVC_ORDER -#undef nssv_BASIC_STRING_VIEW_I - -#endif // compiler-dependent approach to comparisons - -// 24.4.4 Inserters and extractors: - -#if ! nssv_CONFIG_NO_STREAM_INSERTION - -namespace detail { - -template< class Stream > -void write_padding( Stream & os, std::streamsize n ) -{ - for ( std::streamsize i = 0; i < n; ++i ) - os.rdbuf()->sputc( os.fill() ); -} - -template< class Stream, class View > -Stream & write_to_stream( Stream & os, View const & sv ) -{ - typename Stream::sentry sentry( os ); - - if ( !os ) - return os; - - const std::streamsize length = static_cast( sv.length() ); - - // Whether, and how, to pad: - const bool pad = ( length < os.width() ); - const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; - - if ( left_pad ) - write_padding( os, os.width() - length ); - - // Write span characters: - os.rdbuf()->sputn( sv.begin(), length ); - - if ( pad && !left_pad ) - write_padding( os, os.width() - length ); - - // Reset output stream width: - os.width( 0 ); - - return os; -} - -} // namespace detail - -template< class CharT, class Traits > -std::basic_ostream & -operator<<( - std::basic_ostream& os, - basic_string_view sv ) -{ - return detail::write_to_stream( os, sv ); -} - -#endif // nssv_CONFIG_NO_STREAM_INSERTION - -// Several typedefs for common character types are provided: - -typedef basic_string_view string_view; -typedef basic_string_view wstring_view; -#if nssv_HAVE_WCHAR16_T -typedef basic_string_view u16string_view; -typedef basic_string_view u32string_view; -#endif - -}} // namespace nonstd::sv_lite - -// -// 24.4.6 Suffix for basic_string_view literals: -// - -#if nssv_HAVE_USER_DEFINED_LITERALS - -namespace nonstd { -nssv_inline_ns namespace literals { -nssv_inline_ns namespace string_view_literals { - -#if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS - -nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept // (1) -{ - return nonstd::sv_lite::string_view{ str, len }; -} - -nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept // (2) -{ - return nonstd::sv_lite::u16string_view{ str, len }; -} - -nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept // (3) -{ - return nonstd::sv_lite::u32string_view{ str, len }; -} - -nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) -{ - return nonstd::sv_lite::wstring_view{ str, len }; -} - -#endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS - -#if nssv_CONFIG_USR_SV_OPERATOR - -nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept // (1) -{ - return nonstd::sv_lite::string_view{ str, len }; -} - -nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept // (2) -{ - return nonstd::sv_lite::u16string_view{ str, len }; -} - -nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept // (3) -{ - return nonstd::sv_lite::u32string_view{ str, len }; -} - -nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept // (4) -{ - return nonstd::sv_lite::wstring_view{ str, len }; -} - -#endif // nssv_CONFIG_USR_SV_OPERATOR - -}}} // namespace nonstd::literals::string_view_literals - -#endif - -// -// Extensions for std::string: -// - -#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - -namespace nonstd { -namespace sv_lite { - -// Exclude MSVC 14 (19.00): it yields ambiguous to_string(): - -#if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140 - -template< class CharT, class Traits, class Allocator = std::allocator > -std::basic_string -to_string( basic_string_view v, Allocator const & a = Allocator() ) -{ - return std::basic_string( v.begin(), v.end(), a ); -} - -#else - -template< class CharT, class Traits > -std::basic_string -to_string( basic_string_view v ) -{ - return std::basic_string( v.begin(), v.end() ); -} - -template< class CharT, class Traits, class Allocator > -std::basic_string -to_string( basic_string_view v, Allocator const & a ) -{ - return std::basic_string( v.begin(), v.end(), a ); -} - -#endif // nssv_CPP11_OR_GREATER - -template< class CharT, class Traits, class Allocator > -basic_string_view -to_string_view( std::basic_string const & s ) -{ - return basic_string_view( s.data(), s.size() ); -} - -}} // namespace nonstd::sv_lite - -#endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS - -// -// make types and algorithms available in namespace nonstd: -// - -namespace nonstd { - -using sv_lite::basic_string_view; -using sv_lite::string_view; -using sv_lite::wstring_view; - -#if nssv_HAVE_WCHAR16_T -using sv_lite::u16string_view; -#endif -#if nssv_HAVE_WCHAR32_T -using sv_lite::u32string_view; -#endif - -// literal "sv" - -using sv_lite::operator==; -using sv_lite::operator!=; -using sv_lite::operator<; -using sv_lite::operator<=; -using sv_lite::operator>; -using sv_lite::operator>=; - -#if ! nssv_CONFIG_NO_STREAM_INSERTION -using sv_lite::operator<<; -#endif - -#if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS -using sv_lite::to_string; -using sv_lite::to_string_view; -#endif - -} // namespace nonstd - -// 24.4.5 Hash support (C++11): - -// Note: The hash value of a string view object is equal to the hash value of -// the corresponding string object. - -#if nssv_HAVE_STD_HASH - -#include - -namespace std { - -template<> -struct hash< nonstd::string_view > -{ -public: - std::size_t operator()( nonstd::string_view v ) const nssv_noexcept - { - return std::hash()( std::string( v.data(), v.size() ) ); - } -}; - -template<> -struct hash< nonstd::wstring_view > -{ -public: - std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept - { - return std::hash()( std::wstring( v.data(), v.size() ) ); - } -}; - -template<> -struct hash< nonstd::u16string_view > -{ -public: - std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept - { - return std::hash()( std::u16string( v.data(), v.size() ) ); - } -}; - -template<> -struct hash< nonstd::u32string_view > -{ -public: - std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept - { - return std::hash()( std::u32string( v.data(), v.size() ) ); - } -}; - -} // namespace std - -#endif // nssv_HAVE_STD_HASH - -nssv_RESTORE_WARNINGS() - -#endif // nssv_HAVE_STD_STRING_VIEW -#endif // NONSTD_SV_LITE_H_INCLUDED -/* end file include/simdjson/nonstd/string_view.hpp */ -SIMDJSON_POP_DISABLE_WARNINGS - -namespace std { - using string_view = nonstd::string_view; -} -#endif // SIMDJSON_HAS_STRING_VIEW -#undef SIMDJSON_HAS_STRING_VIEW // We are not going to need this macro anymore. - -/// If EXPR is an error, returns it. -#define SIMDJSON_TRY(EXPR) { auto _err = (EXPR); if (_err) { return _err; } } - -// Unless the programmer has already set SIMDJSON_DEVELOPMENT_CHECKS, -// we want to set it under debug builds. We detect a debug build -// under Visual Studio when the _DEBUG macro is set. Under the other -// compilers, we use the fact that they define __OPTIMIZE__ whenever -// they allow optimizations. -// It is possible that this could miss some cases where SIMDJSON_DEVELOPMENT_CHECKS -// is helpful, but the programmer can set the macro SIMDJSON_DEVELOPMENT_CHECKS. -// It could also wrongly set SIMDJSON_DEVELOPMENT_CHECKS (e.g., if the programmer -// sets _DEBUG in a release build under Visual Studio, or if some compiler fails to -// set the __OPTIMIZE__ macro). -#ifndef SIMDJSON_DEVELOPMENT_CHECKS -#ifdef _MSC_VER -// Visual Studio seems to set _DEBUG for debug builds. -#ifdef _DEBUG -#define SIMDJSON_DEVELOPMENT_CHECKS 1 -#endif // _DEBUG -#else // _MSC_VER -// All other compilers appear to set __OPTIMIZE__ to a positive integer -// when the compiler is optimizing. -#ifndef __OPTIMIZE__ -#define SIMDJSON_DEVELOPMENT_CHECKS 1 -#endif // __OPTIMIZE__ -#endif // _MSC_VER -#endif // SIMDJSON_DEVELOPMENT_CHECKS - -// The SIMDJSON_CHECK_EOF macro is a feature flag for the "don't require padding" -// feature. - -#if SIMDJSON_CPLUSPLUS17 -// if we have C++, then fallthrough is a default attribute -# define simdjson_fallthrough [[fallthrough]] -// check if we have __attribute__ support -#elif defined(__has_attribute) -// check if we have the __fallthrough__ attribute -#if __has_attribute(__fallthrough__) -// we are good to go: -# define simdjson_fallthrough __attribute__((__fallthrough__)) -#endif // __has_attribute(__fallthrough__) -#endif // SIMDJSON_CPLUSPLUS17 -// on some systems, we simply do not have support for fallthrough, so use a default: -#ifndef simdjson_fallthrough -# define simdjson_fallthrough do {} while (0) /* fallthrough */ -#endif // simdjson_fallthrough - -#endif // SIMDJSON_COMMON_DEFS_H -/* end file include/simdjson/common_defs.h */ - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_UNDESIRED_WARNINGS - -// Public API -/* begin file include/simdjson/error.h */ -#ifndef SIMDJSON_ERROR_H -#define SIMDJSON_ERROR_H - -#include - -namespace simdjson { - -/** - * All possible errors returned by simdjson. These error codes are subject to change - * and not all simdjson kernel returns the same error code given the same input: it is not - * well defined which error a given input should produce. - * - * Only SUCCESS evaluates to false as a Boolean. All other error codes will evaluate - * to true as a Boolean. - */ -enum error_code { - SUCCESS = 0, ///< No error - CAPACITY, ///< This parser can't support a document that big - MEMALLOC, ///< Error allocating memory, most likely out of memory - TAPE_ERROR, ///< Something went wrong while writing to the tape (stage 2), this is a generic error - DEPTH_ERROR, ///< Your document exceeds the user-specified depth limitation - STRING_ERROR, ///< Problem while parsing a string - T_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 't' - F_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 'f' - N_ATOM_ERROR, ///< Problem while parsing an atom starting with the letter 'n' - NUMBER_ERROR, ///< Problem while parsing a number - UTF8_ERROR, ///< the input is not valid UTF-8 - UNINITIALIZED, ///< unknown error, or uninitialized document - EMPTY, ///< no structural element found - UNESCAPED_CHARS, ///< found unescaped characters in a string. - UNCLOSED_STRING, ///< missing quote at the end - UNSUPPORTED_ARCHITECTURE, ///< unsupported architecture - INCORRECT_TYPE, ///< JSON element has a different type than user expected - NUMBER_OUT_OF_RANGE, ///< JSON number does not fit in 64 bits - INDEX_OUT_OF_BOUNDS, ///< JSON array index too large - NO_SUCH_FIELD, ///< JSON field not found in object - IO_ERROR, ///< Error reading a file - INVALID_JSON_POINTER, ///< Invalid JSON pointer reference - INVALID_URI_FRAGMENT, ///< Invalid URI fragment - UNEXPECTED_ERROR, ///< indicative of a bug in simdjson - PARSER_IN_USE, ///< parser is already in use. - OUT_OF_ORDER_ITERATION, ///< tried to iterate an array or object out of order - INSUFFICIENT_PADDING, ///< The JSON doesn't have enough padding for simdjson to safely parse it. - INCOMPLETE_ARRAY_OR_OBJECT, ///< The document ends early. - SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value. - OUT_OF_BOUNDS, ///< Attempted to access location outside of document. - NUM_ERROR_CODES -}; - -/** - * Get the error message for the given error code. - * - * dom::parser parser; - * dom::element doc; - * auto error = parser.parse("foo",3).get(doc); - * if (error) { printf("Error: %s\n", error_message(error)); } - * - * @return The error message. - */ -inline const char *error_message(error_code error) noexcept; - -/** - * Write the error message to the output stream - */ -inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept; - -/** - * Exception thrown when an exception-supporting simdjson method is called - */ -struct simdjson_error : public std::exception { - /** - * Create an exception from a simdjson error code. - * @param error The error code - */ - simdjson_error(error_code error) noexcept : _error{error} { } - /** The error message */ - const char *what() const noexcept { return error_message(error()); } - /** The error code */ - error_code error() const noexcept { return _error; } -private: - /** The error code that was used */ - error_code _error; -}; - -namespace internal { - -/** - * The result of a simdjson operation that could fail. - * - * Gives the option of reading error codes, or throwing an exception by casting to the desired result. - * - * This is a base class for implementations that want to add functions to the result type for - * chaining. - * - * Override like: - * - * struct simdjson_result : public internal::simdjson_result_base { - * simdjson_result() noexcept : internal::simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ -template -struct simdjson_result_base : protected std::pair { - - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline simdjson_result_base() noexcept; - - /** - * Create a new error result. - */ - simdjson_inline simdjson_result_base(error_code error) noexcept; - - /** - * Create a new successful result. - */ - simdjson_inline simdjson_result_base(T &&value) noexcept; - - /** - * Create a new result with both things (use if you don't want to branch when creating the result). - */ - simdjson_inline simdjson_result_base(T &&value, error_code error) noexcept; - - /** - * Move the value and the error to the provided variables. - * - * @param value The variable to assign the value to. May not be set if there is an error. - * @param error The variable to assign the error to. Set to SUCCESS if there is no error. - */ - simdjson_inline void tie(T &value, error_code &error) && noexcept; - - /** - * Move the value to the provided variable. - * - * @param value The variable to assign the value to. May not be set if there is an error. - */ - simdjson_inline error_code get(T &value) && noexcept; - - /** - * The error. - */ - simdjson_inline error_code error() const noexcept; - -#if SIMDJSON_EXCEPTIONS - - /** - * Get the result value. - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T& value() & noexcept(false); - - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && noexcept(false); - - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& take_value() && noexcept(false); - - /** - * Cast to the value (will throw on error). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline operator T&&() && noexcept(false); -#endif // SIMDJSON_EXCEPTIONS - - /** - * Get the result value. This function is safe if and only - * the error() method returns a value that evaluates to false. - */ - simdjson_inline const T& value_unsafe() const& noexcept; - - /** - * Take the result value (move it). This function is safe if and only - * the error() method returns a value that evaluates to false. - */ - simdjson_inline T&& value_unsafe() && noexcept; - -}; // struct simdjson_result_base - -} // namespace internal - -/** - * The result of a simdjson operation that could fail. - * - * Gives the option of reading error codes, or throwing an exception by casting to the desired result. - */ -template -struct simdjson_result : public internal::simdjson_result_base { - /** - * @private Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline simdjson_result() noexcept; - /** - * @private Create a new error result. - */ - simdjson_inline simdjson_result(T &&value) noexcept; - /** - * @private Create a new successful result. - */ - simdjson_inline simdjson_result(error_code error_code) noexcept; - /** - * @private Create a new result with both things (use if you don't want to branch when creating the result). - */ - simdjson_inline simdjson_result(T &&value, error_code error) noexcept; - - /** - * Move the value and the error to the provided variables. - * - * @param value The variable to assign the value to. May not be set if there is an error. - * @param error The variable to assign the error to. Set to SUCCESS if there is no error. - */ - simdjson_inline void tie(T &value, error_code &error) && noexcept; - - /** - * Move the value to the provided variable. - * - * @param value The variable to assign the value to. May not be set if there is an error. - */ - simdjson_warn_unused simdjson_inline error_code get(T &value) && noexcept; - - /** - * The error. - */ - simdjson_inline error_code error() const noexcept; - -#if SIMDJSON_EXCEPTIONS - - /** - * Get the result value. - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T& value() & noexcept(false); - - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && noexcept(false); - - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& take_value() && noexcept(false); - - /** - * Cast to the value (will throw on error). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline operator T&&() && noexcept(false); -#endif // SIMDJSON_EXCEPTIONS - - /** - * Get the result value. This function is safe if and only - * the error() method returns a value that evaluates to false. - */ - simdjson_inline const T& value_unsafe() const& noexcept; - - /** - * Take the result value (move it). This function is safe if and only - * the error() method returns a value that evaluates to false. - */ - simdjson_inline T&& value_unsafe() && noexcept; - -}; // struct simdjson_result - -#if SIMDJSON_EXCEPTIONS - -template -inline std::ostream& operator<<(std::ostream& out, simdjson_result value) { return out << value.value(); } -#endif // SIMDJSON_EXCEPTIONS - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -/** - * @deprecated This is an alias and will be removed, use error_code instead - */ -using ErrorValues [[deprecated("This is an alias and will be removed, use error_code instead")]] = error_code; - -/** - * @deprecated Error codes should be stored and returned as `error_code`, use `error_message()` instead. - */ -[[deprecated("Error codes should be stored and returned as `error_code`, use `error_message()` instead.")]] -inline const std::string error_message(int error) noexcept; -#endif // SIMDJSON_DISABLE_DEPRECATED_API -} // namespace simdjson - -#endif // SIMDJSON_ERROR_H -/* end file include/simdjson/error.h */ -/* begin file include/simdjson/minify.h */ -#ifndef SIMDJSON_MINIFY_H -#define SIMDJSON_MINIFY_H - -/* begin file include/simdjson/padded_string.h */ -#ifndef SIMDJSON_PADDED_STRING_H -#define SIMDJSON_PADDED_STRING_H - -#include -#include -#include -#include - -namespace simdjson { - -class padded_string_view; - -/** - * String with extra allocation for ease of use with parser::parse() - * - * This is a move-only class, it cannot be copied. - */ -struct padded_string final { - - /** - * Create a new, empty padded string. - */ - explicit inline padded_string() noexcept; - /** - * Create a new padded string buffer. - * - * @param length the size of the string. - */ - explicit inline padded_string(size_t length) noexcept; - /** - * Create a new padded string by copying the given input. - * - * @param data the buffer to copy - * @param length the number of bytes to copy - */ - explicit inline padded_string(const char *data, size_t length) noexcept; - /** - * Create a new padded string by copying the given input. - * - * @param str_ the string to copy - */ - inline padded_string(const std::string & str_ ) noexcept; - /** - * Create a new padded string by copying the given input. - * - * @param sv_ the string to copy - */ - inline padded_string(std::string_view sv_) noexcept; - /** - * Move one padded string into another. - * - * The original padded string will be reduced to zero capacity. - * - * @param o the string to move. - */ - inline padded_string(padded_string &&o) noexcept; - /** - * Move one padded string into another. - * - * The original padded string will be reduced to zero capacity. - * - * @param o the string to move. - */ - inline padded_string &operator=(padded_string &&o) noexcept; - inline void swap(padded_string &o) noexcept; - ~padded_string() noexcept; - - /** - * The length of the string. - * - * Does not include padding. - */ - size_t size() const noexcept; - - /** - * The length of the string. - * - * Does not include padding. - */ - size_t length() const noexcept; - - /** - * The string data. - **/ - const char *data() const noexcept; - const uint8_t *u8data() const noexcept { return static_cast(static_cast(data_ptr));} - - /** - * The string data. - **/ - char *data() noexcept; - - /** - * Create a std::string_view with the same content. - */ - operator std::string_view() const; - - /** - * Create a padded_string_view with the same content. - */ - operator padded_string_view() const noexcept; - - /** - * Load this padded string from a file. - * - * @return IO_ERROR on error. Be mindful that on some 32-bit systems, - * the file size might be limited to 2 GB. - * - * @param path the path to the file. - **/ - inline static simdjson_result load(std::string_view path) noexcept; - -private: - padded_string &operator=(const padded_string &o) = delete; - padded_string(const padded_string &o) = delete; - - size_t viable_size{0}; - char *data_ptr{nullptr}; - -}; // padded_string - -/** - * Send padded_string instance to an output stream. - * - * @param out The output stream. - * @param s The padded_string instance. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. - */ -inline std::ostream& operator<<(std::ostream& out, const padded_string& s) { return out << s.data(); } - -#if SIMDJSON_EXCEPTIONS -/** - * Send padded_string instance to an output stream. - * - * @param out The output stream. - * @param s The padded_string instance. - * @throw simdjson_error if the result being printed has an error. If there is an error with the - * underlying output stream, that error will be propagated (simdjson_error will not be - * thrown). - */ -inline std::ostream& operator<<(std::ostream& out, simdjson_result &s) noexcept(false) { return out << s.value(); } -#endif - -} // namespace simdjson - -// This is deliberately outside of simdjson so that people get it without having to use the namespace -inline simdjson::padded_string operator "" _padded(const char *str, size_t len) { - return simdjson::padded_string(str, len); -} - -namespace simdjson { -namespace internal { - -// The allocate_padded_buffer function is a low-level function to allocate memory -// with padding so we can read past the "length" bytes safely. It is used by -// the padded_string class automatically. It returns nullptr in case -// of error: the caller should check for a null pointer. -// The length parameter is the maximum size in bytes of the string. -// The caller is responsible to free the memory (e.g., delete[] (...)). -inline char *allocate_padded_buffer(size_t length) noexcept; - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_PADDED_STRING_H -/* end file include/simdjson/padded_string.h */ -#include -#include -#include - -namespace simdjson { - - - -/** - * - * Minify the input string assuming that it represents a JSON string, does not parse or validate. - * This function is much faster than parsing a JSON string and then writing a minified version of it. - * However, it does not validate the input. It will merely return an error in simple cases (e.g., if - * there is a string that was never terminated). - * - * - * @param buf the json document to minify. - * @param len the length of the json document. - * @param dst the buffer to write the minified document to. *MUST* be allocated up to len bytes. - * @param dst_len the number of bytes written. Output only. - * @return the error code, or SUCCESS if there was no error. - */ -simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, size_t &dst_len) noexcept; - -} // namespace simdjson - -#endif // SIMDJSON_MINIFY_H -/* end file include/simdjson/minify.h */ -/* begin file include/simdjson/padded_string_view.h */ -#ifndef SIMDJSON_PADDED_STRING_VIEW_H -#define SIMDJSON_PADDED_STRING_VIEW_H - - -#include -#include -#include -#include - -namespace simdjson { - -/** - * User-provided string that promises it has extra padded bytes at the end for use with parser::parse(). - */ -class padded_string_view : public std::string_view { -private: - size_t _capacity; - -public: - /** Create an empty padded_string_view. */ - inline padded_string_view() noexcept = default; - - /** - * Promise the given buffer has at least SIMDJSON_PADDING extra bytes allocated to it. - * - * @param s The string. - * @param len The length of the string (not including padding). - * @param capacity The allocated length of the string, including padding. - */ - explicit inline padded_string_view(const char* s, size_t len, size_t capacity) noexcept; - /** overload explicit inline padded_string_view(const char* s, size_t len) noexcept */ - explicit inline padded_string_view(const uint8_t* s, size_t len, size_t capacity) noexcept; - - /** - * Promise the given string has at least SIMDJSON_PADDING extra bytes allocated to it. - * - * The capacity of the string will be used to determine its padding. - * - * @param s The string. - */ - explicit inline padded_string_view(const std::string &s) noexcept; - - /** - * Promise the given string_view has at least SIMDJSON_PADDING extra bytes allocated to it. - * - * @param s The string. - * @param capacity The allocated length of the string, including padding. - */ - explicit inline padded_string_view(std::string_view s, size_t capacity) noexcept; - - /** The number of allocated bytes. */ - inline size_t capacity() const noexcept; - - /** The amount of padding on the string (capacity() - length()) */ - inline size_t padding() const noexcept; - -}; // padded_string_view - -#if SIMDJSON_EXCEPTIONS -/** - * Send padded_string instance to an output stream. - * - * @param out The output stream. - * @param s The padded_string_view. - * @throw simdjson_error if the result being printed has an error. If there is an error with the - * underlying output stream, that error will be propagated (simdjson_error will not be - * thrown). - */ -inline std::ostream& operator<<(std::ostream& out, simdjson_result &s) noexcept(false) { return out << s.value(); } -#endif - -} // namespace simdjson - -#endif // SIMDJSON_PADDED_STRING_VIEW_H -/* end file include/simdjson/padded_string_view.h */ -/* begin file include/simdjson/implementation.h */ -#ifndef SIMDJSON_IMPLEMENTATION_H -#define SIMDJSON_IMPLEMENTATION_H - -/* begin file include/simdjson/internal/dom_parser_implementation.h */ -#ifndef SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H -#define SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H - -#include - -namespace simdjson { - -namespace dom { -class document; -} // namespace dom - -/** -* This enum is used with the dom_parser_implementation::stage1 function. -* 1) The regular mode expects a fully formed JSON document. -* 2) The streaming_partial mode expects a possibly truncated -* input within a stream on JSON documents. -* 3) The stream_final mode allows us to truncate final -* unterminated strings. It is useful in conjunction with streaming_partial. -*/ -enum class stage1_mode { regular, streaming_partial, streaming_final}; - -/** - * Returns true if mode == streaming_partial or mode == streaming_final - */ -inline bool is_streaming(stage1_mode mode) { - // performance note: it is probably faster to check that mode is different - // from regular than checking that it is either streaming_partial or streaming_final. - return (mode != stage1_mode::regular); - // return (mode == stage1_mode::streaming_partial || mode == stage1_mode::streaming_final); -} - - -namespace internal { - - -/** - * An implementation of simdjson's DOM parser for a particular CPU architecture. - * - * This class is expected to be accessed only by pointer, and never move in memory (though the - * pointer can move). - */ -class dom_parser_implementation { -public: - - /** - * @private For internal implementation use - * - * Run a full JSON parse on a single document (stage1 + stage2). - * - * Guaranteed only to be called when capacity > document length. - * - * Overridden by each implementation. - * - * @param buf The json document to parse. *MUST* be allocated up to len + SIMDJSON_PADDING bytes. - * @param len The length of the json document. - * @return The error code, or SUCCESS if there was no error. - */ - simdjson_warn_unused virtual error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept = 0; - - /** - * @private For internal implementation use - * - * Stage 1 of the document parser. - * - * Guaranteed only to be called when capacity > document length. - * - * Overridden by each implementation. - * - * @param buf The json document to parse. - * @param len The length of the json document. - * @param streaming Whether this is being called by parser::parse_many. - * @return The error code, or SUCCESS if there was no error. - */ - simdjson_warn_unused virtual error_code stage1(const uint8_t *buf, size_t len, stage1_mode streaming) noexcept = 0; - - /** - * @private For internal implementation use - * - * Stage 2 of the document parser. - * - * Called after stage1(). - * - * Overridden by each implementation. - * - * @param doc The document to output to. - * @return The error code, or SUCCESS if there was no error. - */ - simdjson_warn_unused virtual error_code stage2(dom::document &doc) noexcept = 0; - - /** - * @private For internal implementation use - * - * Stage 2 of the document parser for parser::parse_many. - * - * Guaranteed only to be called after stage1(). - * Overridden by each implementation. - * - * @param doc The document to output to. - * @return The error code, SUCCESS if there was no error, or EMPTY if all documents have been parsed. - */ - simdjson_warn_unused virtual error_code stage2_next(dom::document &doc) noexcept = 0; - - /** - * Unescape a valid UTF-8 string from src to dst, stopping at a final unescaped quote. There - * must be an unescaped quote terminating the string. It returns the final output - * position as pointer. In case of error (e.g., the string has bad escaped codes), - * then null_nullptrptr is returned. It is assumed that the output buffer is large - * enough. E.g., if src points at 'joe"', then dst needs to have four free bytes + - * SIMDJSON_PADDING bytes. - * - * Overridden by each implementation. - * - * @param str pointer to the beginning of a valid UTF-8 JSON string, must end with an unescaped quote. - * @param dst pointer to a destination buffer, it must point a region in memory of sufficient size. - * @return end of the of the written region (exclusive) or nullptr in case of error. - */ - simdjson_warn_unused virtual uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept = 0; - - /** - * Change the capacity of this parser. - * - * The capacity can never exceed SIMDJSON_MAXSIZE_BYTES (e.g., 4 GB) - * and an CAPACITY error is returned if it is attempted. - * - * Generally used for reallocation. - * - * @param capacity The new capacity. - * @param max_depth The new max_depth. - * @return The error code, or SUCCESS if there was no error. - */ - virtual error_code set_capacity(size_t capacity) noexcept = 0; - - /** - * Change the max depth of this parser. - * - * Generally used for reallocation. - * - * @param capacity The new capacity. - * @param max_depth The new max_depth. - * @return The error code, or SUCCESS if there was no error. - */ - virtual error_code set_max_depth(size_t max_depth) noexcept = 0; - - /** - * Deallocate this parser. - */ - virtual ~dom_parser_implementation() = default; - - /** Number of structural indices passed from stage 1 to stage 2 */ - uint32_t n_structural_indexes{0}; - /** Structural indices passed from stage 1 to stage 2 */ - std::unique_ptr structural_indexes{}; - /** Next structural index to parse */ - uint32_t next_structural_index{0}; - - /** - * The largest document this parser can support without reallocating. - * - * @return Current capacity, in bytes. - */ - simdjson_inline size_t capacity() const noexcept; - - /** - * The maximum level of nested object and arrays supported by this parser. - * - * @return Maximum depth, in bytes. - */ - simdjson_inline size_t max_depth() const noexcept; - - /** - * Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length - * and `max_depth` depth. - * - * @param capacity The new capacity. - * @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH. - * @return The error, if there is one. - */ - simdjson_warn_unused inline error_code allocate(size_t capacity, size_t max_depth) noexcept; - - -protected: - /** - * The maximum document length this parser supports. - * - * Buffers are large enough to handle any document up to this length. - */ - size_t _capacity{0}; - - /** - * The maximum depth (number of nested objects and arrays) supported by this parser. - * - * Defaults to DEFAULT_MAX_DEPTH. - */ - size_t _max_depth{0}; - - // Declaring these so that subclasses can use them to implement their constructors. - simdjson_inline dom_parser_implementation() noexcept; - simdjson_inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; - simdjson_inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; - - simdjson_inline dom_parser_implementation(const dom_parser_implementation &) noexcept = delete; - simdjson_inline dom_parser_implementation &operator=(const dom_parser_implementation &other) noexcept = delete; -}; // class dom_parser_implementation - -simdjson_inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -simdjson_inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -simdjson_inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -simdjson_inline size_t dom_parser_implementation::capacity() const noexcept { - return _capacity; -} - -simdjson_inline size_t dom_parser_implementation::max_depth() const noexcept { - return _max_depth; -} - -simdjson_warn_unused -inline error_code dom_parser_implementation::allocate(size_t capacity, size_t max_depth) noexcept { - if (this->max_depth() != max_depth) { - error_code err = set_max_depth(max_depth); - if (err) { return err; } - } - if (_capacity != capacity) { - error_code err = set_capacity(capacity); - if (err) { return err; } - } - return SUCCESS; -} - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_DOM_PARSER_IMPLEMENTATION_H -/* end file include/simdjson/internal/dom_parser_implementation.h */ -/* begin file include/simdjson/internal/isadetection.h */ -/* From -https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h -Highly modified. - -Copyright (c) 2016- Facebook, Inc (Adam Paszke) -Copyright (c) 2014- Facebook, Inc (Soumith Chintala) -Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) -Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) -Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) -Copyright (c) 2011-2013 NYU (Clement Farabet) -Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, -Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute -(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, -Samy Bengio, Johnny Mariethoz) - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories -America and IDIAP Research Institute nor the names of its contributors may be - used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef SIMDJSON_INTERNAL_ISADETECTION_H -#define SIMDJSON_INTERNAL_ISADETECTION_H - -#include -#include -#if defined(_MSC_VER) -#include -#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) -#include -#endif - -namespace simdjson { -namespace internal { - - -enum instruction_set { - DEFAULT = 0x0, - NEON = 0x1, - AVX2 = 0x4, - SSE42 = 0x8, - PCLMULQDQ = 0x10, - BMI1 = 0x20, - BMI2 = 0x40, - ALTIVEC = 0x80, - AVX512F = 0x100, - AVX512DQ = 0x200, - AVX512IFMA = 0x400, - AVX512PF = 0x800, - AVX512ER = 0x1000, - AVX512CD = 0x2000, - AVX512BW = 0x4000, - AVX512VL = 0x8000, - AVX512VBMI2 = 0x10000 -}; - -#if defined(__PPC64__) - -static inline uint32_t detect_supported_architectures() { - return instruction_set::ALTIVEC; -} - -#elif defined(__arm__) || defined(__aarch64__) // incl. armel, armhf, arm64 - -#if defined(__ARM_NEON) - -static inline uint32_t detect_supported_architectures() { - return instruction_set::NEON; -} - -#else // ARM without NEON - -static inline uint32_t detect_supported_architectures() { - return instruction_set::DEFAULT; -} - -#endif - -#elif defined(__x86_64__) || defined(_M_AMD64) // x64 - - -namespace { -// Can be found on Intel ISA Reference for CPUID -constexpr uint32_t cpuid_avx2_bit = 1 << 5; ///< @private Bit 5 of EBX for EAX=0x7 -constexpr uint32_t cpuid_bmi1_bit = 1 << 3; ///< @private bit 3 of EBX for EAX=0x7 -constexpr uint32_t cpuid_bmi2_bit = 1 << 8; ///< @private bit 8 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512f_bit = 1 << 16; ///< @private bit 16 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512dq_bit = 1 << 17; ///< @private bit 17 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512ifma_bit = 1 << 21; ///< @private bit 21 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512pf_bit = 1 << 26; ///< @private bit 26 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512er_bit = 1 << 27; ///< @private bit 27 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512cd_bit = 1 << 28; ///< @private bit 28 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512bw_bit = 1 << 30; ///< @private bit 30 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512vl_bit = 1 << 31; ///< @private bit 31 of EBX for EAX=0x7 -constexpr uint32_t cpuid_avx512vbmi2_bit = 1 << 6; ///< @private bit 6 of ECX for EAX=0x7 -constexpr uint32_t cpuid_sse42_bit = 1 << 20; ///< @private bit 20 of ECX for EAX=0x1 -constexpr uint32_t cpuid_pclmulqdq_bit = 1 << 1; ///< @private bit 1 of ECX for EAX=0x1 -} - - - -static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, - uint32_t *edx) { -#if defined(_MSC_VER) - int cpu_info[4]; - __cpuid(cpu_info, *eax); - *eax = cpu_info[0]; - *ebx = cpu_info[1]; - *ecx = cpu_info[2]; - *edx = cpu_info[3]; -#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID) - uint32_t level = *eax; - __get_cpuid(level, eax, ebx, ecx, edx); -#else - uint32_t a = *eax, b, c = *ecx, d; - asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d)); - *eax = a; - *ebx = b; - *ecx = c; - *edx = d; -#endif -} - -static inline uint32_t detect_supported_architectures() { - uint32_t eax, ebx, ecx, edx; - uint32_t host_isa = 0x0; - - // ECX for EAX=0x7 - eax = 0x7; - ecx = 0x0; - cpuid(&eax, &ebx, &ecx, &edx); - if (ebx & cpuid_avx2_bit) { - host_isa |= instruction_set::AVX2; - } - if (ebx & cpuid_bmi1_bit) { - host_isa |= instruction_set::BMI1; - } - - if (ebx & cpuid_bmi2_bit) { - host_isa |= instruction_set::BMI2; - } - - if (ebx & cpuid_avx512f_bit) { - host_isa |= instruction_set::AVX512F; - } - - if (ebx & cpuid_avx512dq_bit) { - host_isa |= instruction_set::AVX512DQ; - } - - if (ebx & cpuid_avx512ifma_bit) { - host_isa |= instruction_set::AVX512IFMA; - } - - if (ebx & cpuid_avx512pf_bit) { - host_isa |= instruction_set::AVX512PF; - } - - if (ebx & cpuid_avx512er_bit) { - host_isa |= instruction_set::AVX512ER; - } - - if (ebx & cpuid_avx512cd_bit) { - host_isa |= instruction_set::AVX512CD; - } - - if (ebx & cpuid_avx512bw_bit) { - host_isa |= instruction_set::AVX512BW; - } - - if (ebx & cpuid_avx512vl_bit) { - host_isa |= instruction_set::AVX512VL; - } - - if (ecx & cpuid_avx512vbmi2_bit) { - host_isa |= instruction_set::AVX512VBMI2; - } - - // EBX for EAX=0x1 - eax = 0x1; - cpuid(&eax, &ebx, &ecx, &edx); - - if (ecx & cpuid_sse42_bit) { - host_isa |= instruction_set::SSE42; - } - - if (ecx & cpuid_pclmulqdq_bit) { - host_isa |= instruction_set::PCLMULQDQ; - } - - return host_isa; -} -#else // fallback - - -static inline uint32_t detect_supported_architectures() { - return instruction_set::DEFAULT; -} - - -#endif // end SIMD extension detection code - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_ISADETECTION_H -/* end file include/simdjson/internal/isadetection.h */ -#include -#include -#include - -namespace simdjson { - -/** - * Validate the UTF-8 string. - * - * @param buf the string to validate. - * @param len the length of the string in bytes. - * @return true if the string is valid UTF-8. - */ -simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) noexcept; -/** - * Validate the UTF-8 string. - * - * @param sv the string_view to validate. - * @return true if the string is valid UTF-8. - */ -simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view sv) noexcept { - return validate_utf8(sv.data(), sv.size()); -} - -/** - * Validate the UTF-8 string. - * - * @param p the string to validate. - * @return true if the string is valid UTF-8. - */ -simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string& s) noexcept { - return validate_utf8(s.data(), s.size()); -} - -namespace dom { - class document; -} // namespace dom - -/** - * An implementation of simdjson for a particular CPU architecture. - * - * Also used to maintain the currently active implementation. The active implementation is - * automatically initialized on first use to the most advanced implementation supported by the host. - */ -class implementation { -public: - - /** - * The name of this implementation. - * - * const implementation *impl = simdjson::get_active_implementation(); - * cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl; - * - * @return the name of the implementation, e.g. "haswell", "westmere", "arm64" - */ - virtual const std::string &name() const { return _name; } - - /** - * The description of this implementation. - * - * const implementation *impl = simdjson::get_active_implementation(); - * cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl; - * - * @return the name of the implementation, e.g. "haswell", "westmere", "arm64" - */ - virtual const std::string &description() const { return _description; } - - /** - * The instruction sets this implementation is compiled against - * and the current CPU match. This function may poll the current CPU/system - * and should therefore not be called too often if performance is a concern. - * - * - * @return true if the implementation can be safely used on the current system (determined at runtime) - */ - bool supported_by_runtime_system() const; - - /** - * @private For internal implementation use - * - * The instruction sets this implementation is compiled against. - * - * @return a mask of all required `internal::instruction_set::` values - */ - virtual uint32_t required_instruction_sets() const { return _required_instruction_sets; }; - - /** - * @private For internal implementation use - * - * const implementation *impl = simdjson::get_active_implementation(); - * cout << "simdjson is optimized for " << impl->name() << "(" << impl->description() << ")" << endl; - * - * @param capacity The largest document that will be passed to the parser. - * @param max_depth The maximum JSON object/array nesting this parser is expected to handle. - * @param dst The place to put the resulting parser implementation. - * @return the name of the implementation, e.g. "haswell", "westmere", "arm64" - */ - virtual error_code create_dom_parser_implementation( - size_t capacity, - size_t max_depth, - std::unique_ptr &dst - ) const noexcept = 0; - - /** - * @private For internal implementation use - * - * Minify the input string assuming that it represents a JSON string, does not parse or validate. - * - * Overridden by each implementation. - * - * @param buf the json document to minify. - * @param len the length of the json document. - * @param dst the buffer to write the minified document to. *MUST* be allocated up to len + SIMDJSON_PADDING bytes. - * @param dst_len the number of bytes written. Output only. - * @return the error code, or SUCCESS if there was no error. - */ - simdjson_warn_unused virtual error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept = 0; - - - /** - * Validate the UTF-8 string. - * - * Overridden by each implementation. - * - * @param buf the string to validate. - * @param len the length of the string in bytes. - * @return true if and only if the string is valid UTF-8. - */ - simdjson_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0; - -protected: - /** @private Construct an implementation with the given name and description. For subclasses. */ - simdjson_inline implementation( - std::string_view name, - std::string_view description, - uint32_t required_instruction_sets - ) : - _name(name), - _description(description), - _required_instruction_sets(required_instruction_sets) - { - } - virtual ~implementation()=default; - -private: - /** - * The name of this implementation. - */ - const std::string _name; - - /** - * The description of this implementation. - */ - const std::string _description; - - /** - * Instruction sets required for this implementation. - */ - const uint32_t _required_instruction_sets; -}; - -/** @private */ -namespace internal { - -/** - * The list of available implementations compiled into simdjson. - */ -class available_implementation_list { -public: - /** Get the list of available implementations compiled into simdjson */ - simdjson_inline available_implementation_list() {} - /** Number of implementations */ - size_t size() const noexcept; - /** STL const begin() iterator */ - const implementation * const *begin() const noexcept; - /** STL const end() iterator */ - const implementation * const *end() const noexcept; - - /** - * Get the implementation with the given name. - * - * Case sensitive. - * - * const implementation *impl = simdjson::get_available_implementations()["westmere"]; - * if (!impl) { exit(1); } - * if (!imp->supported_by_runtime_system()) { exit(1); } - * simdjson::get_active_implementation() = impl; - * - * @param name the implementation to find, e.g. "westmere", "haswell", "arm64" - * @return the implementation, or nullptr if the parse failed. - */ - const implementation * operator[](const std::string_view &name) const noexcept { - for (const implementation * impl : *this) { - if (impl->name() == name) { return impl; } - } - return nullptr; - } - - /** - * Detect the most advanced implementation supported by the current host. - * - * This is used to initialize the implementation on startup. - * - * const implementation *impl = simdjson::available_implementation::detect_best_supported(); - * simdjson::get_active_implementation() = impl; - * - * @return the most advanced supported implementation for the current host, or an - * implementation that returns UNSUPPORTED_ARCHITECTURE if there is no supported - * implementation. Will never return nullptr. - */ - const implementation *detect_best_supported() const noexcept; -}; - -template -class atomic_ptr { -public: - atomic_ptr(T *_ptr) : ptr{_ptr} {} - - operator const T*() const { return ptr.load(); } - const T& operator*() const { return *ptr; } - const T* operator->() const { return ptr.load(); } - - operator T*() { return ptr.load(); } - T& operator*() { return *ptr; } - T* operator->() { return ptr.load(); } - atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; } - -private: - std::atomic ptr; -}; - -} // namespace internal - -/** - * The list of available implementations compiled into simdjson. - */ -extern SIMDJSON_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations(); - -/** - * The active implementation. - * - * Automatically initialized on first use to the most advanced implementation supported by this hardware. - */ -extern SIMDJSON_DLLIMPORTEXPORT internal::atomic_ptr& get_active_implementation(); - -} // namespace simdjson - -#endif // SIMDJSON_IMPLEMENTATION_H -/* end file include/simdjson/implementation.h */ - -// Inline functions -/* begin file include/simdjson/error-inl.h */ -#ifndef SIMDJSON_INLINE_ERROR_H -#define SIMDJSON_INLINE_ERROR_H - -#include -#include -#include - -namespace simdjson { -namespace internal { - // We store the error code so we can validate the error message is associated with the right code - struct error_code_info { - error_code code; - const char* message; // do not use a fancy std::string where a simple C string will do (no alloc, no destructor) - }; - // These MUST match the codes in error_code. We check this constraint in basictests. - extern SIMDJSON_DLLIMPORTEXPORT const error_code_info error_codes[]; -} // namespace internal - - -inline const char *error_message(error_code error) noexcept { - // If you're using error_code, we're trusting you got it from the enum. - return internal::error_codes[int(error)].message; -} - -// deprecated function -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -inline const std::string error_message(int error) noexcept { - if (error < 0 || error >= error_code::NUM_ERROR_CODES) { - return internal::error_codes[UNEXPECTED_ERROR].message; - } - return internal::error_codes[error].message; -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API - -inline std::ostream& operator<<(std::ostream& out, error_code error) noexcept { - return out << error_message(error); -} - -namespace internal { - -// -// internal::simdjson_result_base inline implementation -// - -template -simdjson_inline void simdjson_result_base::tie(T &value, error_code &error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } -} - -template -simdjson_warn_unused simdjson_inline error_code simdjson_result_base::get(T &value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; -} - -template -simdjson_inline error_code simdjson_result_base::error() const noexcept { - return this->second; -} - -#if SIMDJSON_EXCEPTIONS - -template -simdjson_inline T& simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; -} - -template -simdjson_inline T&& simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); -} - -template -simdjson_inline T&& simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); -} - -template -simdjson_inline simdjson_result_base::operator T&&() && noexcept(false) { - return std::forward>(*this).take_value(); -} - -#endif // SIMDJSON_EXCEPTIONS - -template -simdjson_inline const T& simdjson_result_base::value_unsafe() const& noexcept { - return this->first; -} - -template -simdjson_inline T&& simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); -} - -template -simdjson_inline simdjson_result_base::simdjson_result_base(T &&value, error_code error) noexcept - : std::pair(std::forward(value), error) {} -template -simdjson_inline simdjson_result_base::simdjson_result_base(error_code error) noexcept - : simdjson_result_base(T{}, error) {} -template -simdjson_inline simdjson_result_base::simdjson_result_base(T &&value) noexcept - : simdjson_result_base(std::forward(value), SUCCESS) {} -template -simdjson_inline simdjson_result_base::simdjson_result_base() noexcept - : simdjson_result_base(T{}, UNINITIALIZED) {} - -} // namespace internal - -/// -/// simdjson_result inline implementation -/// - -template -simdjson_inline void simdjson_result::tie(T &value, error_code &error) && noexcept { - std::forward>(*this).tie(value, error); -} - -template -simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &value) && noexcept { - return std::forward>(*this).get(value); -} - -template -simdjson_inline error_code simdjson_result::error() const noexcept { - return internal::simdjson_result_base::error(); -} - -#if SIMDJSON_EXCEPTIONS - -template -simdjson_inline T& simdjson_result::value() & noexcept(false) { - return internal::simdjson_result_base::value(); -} - -template -simdjson_inline T&& simdjson_result::value() && noexcept(false) { - return std::forward>(*this).value(); -} - -template -simdjson_inline T&& simdjson_result::take_value() && noexcept(false) { - return std::forward>(*this).take_value(); -} - -template -simdjson_inline simdjson_result::operator T&&() && noexcept(false) { - return std::forward>(*this).take_value(); -} - -#endif // SIMDJSON_EXCEPTIONS - -template -simdjson_inline const T& simdjson_result::value_unsafe() const& noexcept { - return internal::simdjson_result_base::value_unsafe(); -} - -template -simdjson_inline T&& simdjson_result::value_unsafe() && noexcept { - return std::forward>(*this).value_unsafe(); -} - -template -simdjson_inline simdjson_result::simdjson_result(T &&value, error_code error) noexcept - : internal::simdjson_result_base(std::forward(value), error) {} -template -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} -template -simdjson_inline simdjson_result::simdjson_result(T &&value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} -template -simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} - -} // namespace simdjson - -#endif // SIMDJSON_INLINE_ERROR_H -/* end file include/simdjson/error-inl.h */ -/* begin file include/simdjson/padded_string-inl.h */ -#ifndef SIMDJSON_INLINE_PADDED_STRING_H -#define SIMDJSON_INLINE_PADDED_STRING_H - - -#include -#include -#include -#include - -namespace simdjson { -namespace internal { - -// The allocate_padded_buffer function is a low-level function to allocate memory -// with padding so we can read past the "length" bytes safely. It is used by -// the padded_string class automatically. It returns nullptr in case -// of error: the caller should check for a null pointer. -// The length parameter is the maximum size in bytes of the string. -// The caller is responsible to free the memory (e.g., delete[] (...)). -inline char *allocate_padded_buffer(size_t length) noexcept { - const size_t totalpaddedlength = length + SIMDJSON_PADDING; - if(totalpaddedlength(1UL<<20)) { - return nullptr; - } -#endif - - char *padded_buffer = new (std::nothrow) char[totalpaddedlength]; - if (padded_buffer == nullptr) { - return nullptr; - } - // We write zeroes in the padded region to avoid having uninitized - // garbage. If nothing else, garbage getting read might trigger a - // warning in a memory checking. - std::memset(padded_buffer + length, 0, totalpaddedlength - length); - return padded_buffer; -} // allocate_padded_buffer() - -} // namespace internal - - -inline padded_string::padded_string() noexcept {} -inline padded_string::padded_string(size_t length) noexcept - : viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) { -} -inline padded_string::padded_string(const char *data, size_t length) noexcept - : viable_size(length), data_ptr(internal::allocate_padded_buffer(length)) { - if ((data != nullptr) && (data_ptr != nullptr)) { - std::memcpy(data_ptr, data, length); - } -} -// note: do not pass std::string arguments by value -inline padded_string::padded_string(const std::string & str_ ) noexcept - : viable_size(str_.size()), data_ptr(internal::allocate_padded_buffer(str_.size())) { - if (data_ptr != nullptr) { - std::memcpy(data_ptr, str_.data(), str_.size()); - } -} -// note: do pass std::string_view arguments by value -inline padded_string::padded_string(std::string_view sv_) noexcept - : viable_size(sv_.size()), data_ptr(internal::allocate_padded_buffer(sv_.size())) { - if(simdjson_unlikely(!data_ptr)) { - //allocation failed or zero size - viable_size=0; - return; - } - if (sv_.size()) { - std::memcpy(data_ptr, sv_.data(), sv_.size()); - } -} -inline padded_string::padded_string(padded_string &&o) noexcept - : viable_size(o.viable_size), data_ptr(o.data_ptr) { - o.data_ptr = nullptr; // we take ownership -} - -inline padded_string &padded_string::operator=(padded_string &&o) noexcept { - delete[] data_ptr; - data_ptr = o.data_ptr; - viable_size = o.viable_size; - o.data_ptr = nullptr; // we take ownership - o.viable_size = 0; - return *this; -} - -inline void padded_string::swap(padded_string &o) noexcept { - size_t tmp_viable_size = viable_size; - char *tmp_data_ptr = data_ptr; - viable_size = o.viable_size; - data_ptr = o.data_ptr; - o.data_ptr = tmp_data_ptr; - o.viable_size = tmp_viable_size; -} - -inline padded_string::~padded_string() noexcept { - delete[] data_ptr; -} - -inline size_t padded_string::size() const noexcept { return viable_size; } - -inline size_t padded_string::length() const noexcept { return viable_size; } - -inline const char *padded_string::data() const noexcept { return data_ptr; } - -inline char *padded_string::data() noexcept { return data_ptr; } - -inline padded_string::operator std::string_view() const { return std::string_view(data(), length()); } - -inline padded_string::operator padded_string_view() const noexcept { - return padded_string_view(data(), length(), length() + SIMDJSON_PADDING); -} - -inline simdjson_result padded_string::load(std::string_view filename) noexcept { - // Open the file - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe - std::FILE *fp = std::fopen(filename.data(), "rb"); - SIMDJSON_POP_DISABLE_WARNINGS - - if (fp == nullptr) { - return IO_ERROR; - } - - // Get the file size - if(std::fseek(fp, 0, SEEK_END) < 0) { - std::fclose(fp); - return IO_ERROR; - } -#if defined(SIMDJSON_VISUAL_STUDIO) && !SIMDJSON_IS_32BITS - __int64 llen = _ftelli64(fp); - if(llen == -1L) { - std::fclose(fp); - return IO_ERROR; - } -#else - long llen = std::ftell(fp); - if((llen < 0) || (llen == LONG_MAX)) { - std::fclose(fp); - return IO_ERROR; - } -#endif - - // Allocate the padded_string - size_t len = static_cast(llen); - padded_string s(len); - if (s.data() == nullptr) { - std::fclose(fp); - return MEMALLOC; - } - - // Read the padded_string - std::rewind(fp); - size_t bytes_read = std::fread(s.data(), 1, len, fp); - if (std::fclose(fp) != 0 || bytes_read != len) { - return IO_ERROR; - } - - return s; -} - -} // namespace simdjson - -#endif // SIMDJSON_INLINE_PADDED_STRING_H -/* end file include/simdjson/padded_string-inl.h */ -/* begin file include/simdjson/padded_string_view-inl.h */ -#ifndef SIMDJSON_PADDED_STRING_VIEW_INL_H -#define SIMDJSON_PADDED_STRING_VIEW_INL_H - - -#include -#include -#include -#include - -namespace simdjson { - -inline padded_string_view::padded_string_view(const char* s, size_t len, size_t capacity) noexcept - : std::string_view(s, len), _capacity(capacity) -{ -} - -inline padded_string_view::padded_string_view(const uint8_t* s, size_t len, size_t capacity) noexcept - : padded_string_view(reinterpret_cast(s), len, capacity) -{ -} - -inline padded_string_view::padded_string_view(const std::string &s) noexcept - : std::string_view(s), _capacity(s.capacity()) -{ -} - -inline padded_string_view::padded_string_view(std::string_view s, size_t capacity) noexcept - : std::string_view(s), _capacity(capacity) -{ -} - -inline size_t padded_string_view::capacity() const noexcept { return _capacity; } - -inline size_t padded_string_view::padding() const noexcept { return capacity() - length(); } - -} // namespace simdjson - -#endif // SIMDJSON_PADDED_STRING_VIEW_INL_H -/* end file include/simdjson/padded_string_view-inl.h */ - -SIMDJSON_POP_DISABLE_WARNINGS - -#endif // SIMDJSON_BASE_H -/* end file include/simdjson/base.h */ - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_UNDESIRED_WARNINGS - -/* begin file include/simdjson/dom/array.h */ -#ifndef SIMDJSON_DOM_ARRAY_H -#define SIMDJSON_DOM_ARRAY_H - -/* begin file include/simdjson/internal/tape_ref.h */ -#ifndef SIMDJSON_INTERNAL_TAPE_REF_H -#define SIMDJSON_INTERNAL_TAPE_REF_H - -/* begin file include/simdjson/internal/tape_type.h */ -#ifndef SIMDJSON_INTERNAL_TAPE_TYPE_H -#define SIMDJSON_INTERNAL_TAPE_TYPE_H - -namespace simdjson { -namespace internal { - -/** - * The possible types in the tape. - */ -enum class tape_type { - ROOT = 'r', - START_ARRAY = '[', - START_OBJECT = '{', - END_ARRAY = ']', - END_OBJECT = '}', - STRING = '"', - INT64 = 'l', - UINT64 = 'u', - DOUBLE = 'd', - TRUE_VALUE = 't', - FALSE_VALUE = 'f', - NULL_VALUE = 'n' -}; // enum class tape_type - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_TAPE_TYPE_H -/* end file include/simdjson/internal/tape_type.h */ - -namespace simdjson { - -namespace dom { - class document; -} - -namespace internal { - -constexpr const uint64_t JSON_VALUE_MASK = 0x00FFFFFFFFFFFFFF; -constexpr const uint32_t JSON_COUNT_MASK = 0xFFFFFF; - -/** - * A reference to an element on the tape. Internal only. - */ -class tape_ref { -public: - simdjson_inline tape_ref() noexcept; - simdjson_inline tape_ref(const dom::document *doc, size_t json_index) noexcept; - inline size_t after_element() const noexcept; - simdjson_inline tape_type tape_ref_type() const noexcept; - simdjson_inline uint64_t tape_value() const noexcept; - simdjson_inline bool is_double() const noexcept; - simdjson_inline bool is_int64() const noexcept; - simdjson_inline bool is_uint64() const noexcept; - simdjson_inline bool is_false() const noexcept; - simdjson_inline bool is_true() const noexcept; - simdjson_inline bool is_null_on_tape() const noexcept;// different name to avoid clash with is_null. - simdjson_inline uint32_t matching_brace_index() const noexcept; - simdjson_inline uint32_t scope_count() const noexcept; - template - simdjson_inline T next_tape_value() const noexcept; - simdjson_inline uint32_t get_string_length() const noexcept; - simdjson_inline const char * get_c_str() const noexcept; - inline std::string_view get_string_view() const noexcept; - simdjson_inline bool is_document_root() const noexcept; - - /** The document this element references. */ - const dom::document *doc; - - /** The index of this element on `doc.tape[]` */ - size_t json_index; -}; - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_TAPE_REF_H -/* end file include/simdjson/internal/tape_ref.h */ - -namespace simdjson { - -namespace internal { -template -class string_builder; -} -namespace dom { - -class document; -class element; - -/** - * JSON array. - */ -class array { -public: - /** Create a new, invalid array */ - simdjson_inline array() noexcept; - - class iterator { - public: - using value_type = element; - using difference_type = std::ptrdiff_t; - - /** - * Get the actual value - */ - inline value_type operator*() const noexcept; - /** - * Get the next value. - * - * Part of the std::iterator interface. - */ - inline iterator& operator++() noexcept; - /** - * Get the next value. - * - * Part of the std::iterator interface. - */ - inline iterator operator++(int) noexcept; - /** - * Check if these values come from the same place in the JSON. - * - * Part of the std::iterator interface. - */ - inline bool operator!=(const iterator& other) const noexcept; - inline bool operator==(const iterator& other) const noexcept; - - inline bool operator<(const iterator& other) const noexcept; - inline bool operator<=(const iterator& other) const noexcept; - inline bool operator>=(const iterator& other) const noexcept; - inline bool operator>(const iterator& other) const noexcept; - - iterator() noexcept = default; - iterator(const iterator&) noexcept = default; - iterator& operator=(const iterator&) noexcept = default; - private: - simdjson_inline iterator(const internal::tape_ref &tape) noexcept; - internal::tape_ref tape; - friend class array; - }; - - /** - * Return the first array element. - * - * Part of the std::iterable interface. - */ - inline iterator begin() const noexcept; - /** - * One past the last array element. - * - * Part of the std::iterable interface. - */ - inline iterator end() const noexcept; - /** - * Get the size of the array (number of immediate children). - * It is a saturated value with a maximum of 0xFFFFFF: if the value - * is 0xFFFFFF then the size is 0xFFFFFF or greater. - */ - inline size_t size() const noexcept; - /** - * Get the total number of slots used by this array on the tape. - * - * Note that this is not the same thing as `size()`, which reports the - * number of actual elements within an array (not counting its children). - * - * Since an element can use 1 or 2 slots on the tape, you can only use this - * to figure out the total size of an array (including its children, - * recursively) if you know its structure ahead of time. - **/ - inline size_t number_of_slots() const noexcept; - /** - * Get the value associated with the given JSON pointer. We use the RFC 6901 - * https://tools.ietf.org/html/rfc6901 standard, interpreting the current node - * as the root of its own JSON document. - * - * dom::parser parser; - * array a = parser.parse(R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded); - * a.at_pointer("/0/foo/a/1") == 20 - * a.at_pointer("0")["foo"]["a"].at(1) == 20 - * - * @return The value associated with the given JSON pointer, or: - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed - */ - inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; - - /** - * Get the value at the given index. This function has linear-time complexity and - * is equivalent to the following: - * - * size_t i=0; - * for (auto element : *this) { - * if (i == index) { return element; } - * i++; - * } - * return INDEX_OUT_OF_BOUNDS; - * - * Avoid calling the at() function repeatedly. - * - * @return The value at the given index, or: - * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length - */ - inline simdjson_result at(size_t index) const noexcept; - -private: - simdjson_inline array(const internal::tape_ref &tape) noexcept; - internal::tape_ref tape; - friend class element; - friend struct simdjson_result; - template - friend class simdjson::internal::string_builder; -}; - - -} // namespace dom - -/** The result of a JSON conversion that may fail. */ -template<> -struct simdjson_result : public internal::simdjson_result_base { -public: - simdjson_inline simdjson_result() noexcept; ///< @private - simdjson_inline simdjson_result(dom::array value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - - inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; - inline simdjson_result at(size_t index) const noexcept; - -#if SIMDJSON_EXCEPTIONS - inline dom::array::iterator begin() const noexcept(false); - inline dom::array::iterator end() const noexcept(false); - inline size_t size() const noexcept(false); -#endif // SIMDJSON_EXCEPTIONS -}; - - - -} // namespace simdjson - -#if defined(__cpp_lib_ranges) -#include - -namespace std { -namespace ranges { -template<> -inline constexpr bool enable_view = true; -#if SIMDJSON_EXCEPTIONS -template<> -inline constexpr bool enable_view> = true; -#endif // SIMDJSON_EXCEPTIONS -} // namespace ranges -} // namespace std -#endif // defined(__cpp_lib_ranges) - -#endif // SIMDJSON_DOM_ARRAY_H -/* end file include/simdjson/dom/array.h */ -/* begin file include/simdjson/dom/document_stream.h */ -#ifndef SIMDJSON_DOCUMENT_STREAM_H -#define SIMDJSON_DOCUMENT_STREAM_H - -/* begin file include/simdjson/dom/parser.h */ -#ifndef SIMDJSON_DOM_PARSER_H -#define SIMDJSON_DOM_PARSER_H - -/* begin file include/simdjson/dom/document.h */ -#ifndef SIMDJSON_DOM_DOCUMENT_H -#define SIMDJSON_DOM_DOCUMENT_H - -#include -#include - -namespace simdjson { -namespace dom { - -class element; - -/** - * A parsed JSON document. - * - * This class cannot be copied, only moved, to avoid unintended allocations. - */ -class document { -public: - /** - * Create a document container with zero capacity. - * - * The parser will allocate capacity as needed. - */ - document() noexcept = default; - ~document() noexcept = default; - - /** - * Take another document's buffers. - * - * @param other The document to take. Its capacity is zeroed and it is invalidated. - */ - document(document &&other) noexcept = default; - /** @private */ - document(const document &) = delete; // Disallow copying - /** - * Take another document's buffers. - * - * @param other The document to take. Its capacity is zeroed. - */ - document &operator=(document &&other) noexcept = default; - /** @private */ - document &operator=(const document &) = delete; // Disallow copying - - /** - * Get the root element of this document as a JSON array. - */ - element root() const noexcept; - - /** - * @private Dump the raw tape for debugging. - * - * @param os the stream to output to. - * @return false if the tape is likely wrong (e.g., you did not parse a valid JSON). - */ - bool dump_raw_tape(std::ostream &os) const noexcept; - - /** @private Structural values. */ - std::unique_ptr tape{}; - - /** @private String values. - * - * Should be at least byte_capacity. - */ - std::unique_ptr string_buf{}; - /** @private Allocate memory to support - * input JSON documents of up to len bytes. - * - * When calling this function, you lose - * all the data. - * - * The memory allocation is strict: you - * can you use this function to increase - * or lower the amount of allocated memory. - * Passsing zero clears the memory. - */ - error_code allocate(size_t len) noexcept; - /** @private Capacity in bytes, in terms - * of how many bytes of input JSON we can - * support. - */ - size_t capacity() const noexcept; - - -private: - size_t allocated_capacity{0}; - friend class parser; -}; // class document - -} // namespace dom -} // namespace simdjson - -#endif // SIMDJSON_DOM_DOCUMENT_H -/* end file include/simdjson/dom/document.h */ -#include -#include -#include - -namespace simdjson { - -namespace dom { - -class document_stream; -class element; - -/** The default batch size for parser.parse_many() and parser.load_many() */ -static constexpr size_t DEFAULT_BATCH_SIZE = 1000000; -/** - * Some adversary might try to set the batch size to 0 or 1, which might cause problems. - * We set a minimum of 32B since anything else is highly likely to be an error. In practice, - * most users will want a much larger batch size. - * - * All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON - * document can ever span 0 or 1 byte and that very large values would create memory allocation issues. - */ -static constexpr size_t MINIMAL_BATCH_SIZE = 32; - -/** - * It is wasteful to allocate memory for tiny documents (e.g., 4 bytes). - */ -static constexpr size_t MINIMAL_DOCUMENT_CAPACITY = 32; - -/** - * A persistent document parser. - * - * The parser is designed to be reused, holding the internal buffers necessary to do parsing, - * as well as memory for a single document. The parsed document is overwritten on each parse. - * - * This class cannot be copied, only moved, to avoid unintended allocations. - * - * @note Moving a parser instance may invalidate "dom::element" instances. If you need to - * preserve both the "dom::element" instances and the parser, consider wrapping the parser - * instance in a std::unique_ptr instance: - * - * std::unique_ptr parser(new dom::parser{}); - * auto error = parser->load(f).get(root); - * - * You can then move std::unique_ptr safely. - * - * @note This is not thread safe: one parser cannot produce two documents at the same time! - */ -class parser { -public: - /** - * Create a JSON parser. - * - * The new parser will have zero capacity. - * - * @param max_capacity The maximum document length the parser can automatically handle. The parser - * will allocate more capacity on an as needed basis (when it sees documents too big to handle) - * up to this amount. The parser still starts with zero capacity no matter what this number is: - * to allocate an initial capacity, call allocate() after constructing the parser. - * Defaults to SIMDJSON_MAXSIZE_BYTES (the largest single document simdjson can process). - */ - simdjson_inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept; - /** - * Take another parser's buffers and state. - * - * @param other The parser to take. Its capacity is zeroed. - */ - simdjson_inline parser(parser &&other) noexcept; - parser(const parser &) = delete; ///< @private Disallow copying - /** - * Take another parser's buffers and state. - * - * @param other The parser to take. Its capacity is zeroed. - */ - simdjson_inline parser &operator=(parser &&other) noexcept; - parser &operator=(const parser &) = delete; ///< @private Disallow copying - - /** Deallocate the JSON parser. */ - ~parser()=default; - - /** - * Load a JSON document from a file and return a reference to it. - * - * dom::parser parser; - * const element doc = parser.load("jsonexamples/twitter.json"); - * - * The function is eager: the file's content is loaded in memory inside the parser instance - * and immediately parsed. The file can be deleted after the `parser.load` call. - * - * ### IMPORTANT: Document Lifetime - * - * The JSON document still lives in the parser: this is the most efficient way to parse JSON - * documents because it reuses the same buffers, but you *must* use the document before you - * destroy the parser or call parse() again. - * - * Moving the parser instance is safe, but it invalidates the element instances. You may store - * the parser instance without moving it by wrapping it inside an `unique_ptr` instance like - * so: `std::unique_ptr parser(new dom::parser{});`. - * - * ### Parser Capacity - * - * If the parser's current capacity is less than the file length, it will allocate enough capacity - * to handle it (up to max_capacity). - * - * @param path The path to load. - * @return The document, or an error: - * - IO_ERROR if there was an error opening or reading the file. - * Be mindful that on some 32-bit systems, - * the file size might be limited to 2 GB. - * - MEMALLOC if the parser does not have enough capacity and memory allocation fails. - * - CAPACITY if the parser does not have enough capacity and len > max_capacity. - * - other json errors if parsing fails. You should not rely on these errors to always the same for the - * same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware). - */ - inline simdjson_result load(const std::string &path) & noexcept; - inline simdjson_result load(const std::string &path) && = delete ; - /** - * Parse a JSON document and return a temporary reference to it. - * - * dom::parser parser; - * element doc_root = parser.parse(buf, len); - * - * The function eagerly parses the input: the input can be modified and discarded after - * the `parser.parse(buf, len)` call has completed. - * - * ### IMPORTANT: Document Lifetime - * - * The JSON document still lives in the parser: this is the most efficient way to parse JSON - * documents because it reuses the same buffers, but you *must* use the document before you - * destroy the parser or call parse() again. - * - * Moving the parser instance is safe, but it invalidates the element instances. You may store - * the parser instance without moving it by wrapping it inside an `unique_ptr` instance like - * so: `std::unique_ptr parser(new dom::parser{});`. - * - * ### REQUIRED: Buffer Padding - * - * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what - * those bytes are initialized to, as long as they are allocated. - * - * If realloc_if_needed is true (the default), it is assumed that the buffer does *not* have enough padding, - * and it is copied into an enlarged temporary buffer before parsing. Thus the following is safe: - * - * const char *json = R"({"key":"value"})"; - * const size_t json_len = std::strlen(json); - * simdjson::dom::parser parser; - * simdjson::dom::element element = parser.parse(json, json_len); - * - * If you set realloc_if_needed to false (e.g., parser.parse(json, json_len, false)), - * you must provide a buffer with at least SIMDJSON_PADDING extra bytes at the end. - * The benefit of setting realloc_if_needed to false is that you avoid a temporary - * memory allocation and a copy. - * - * The padded bytes may be read. It is not important how you initialize - * these bytes though we recommend a sensible default like null character values or spaces. - * For example, the following low-level code is safe: - * - * const char *json = R"({"key":"value"})"; - * const size_t json_len = std::strlen(json); - * std::unique_ptr padded_json_copy{new char[json_len + SIMDJSON_PADDING]}; - * std::memcpy(padded_json_copy.get(), json, json_len); - * std::memset(padded_json_copy.get() + json_len, '\0', SIMDJSON_PADDING); - * simdjson::dom::parser parser; - * simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len, false); - * - * ### Parser Capacity - * - * If the parser's current capacity is less than len, it will allocate enough capacity - * to handle it (up to max_capacity). - * - * @param buf The JSON to parse. Must have at least len + SIMDJSON_PADDING allocated bytes, unless - * realloc_if_needed is true. - * @param len The length of the JSON. - * @param realloc_if_needed Whether to reallocate and enlarge the JSON buffer to add padding. - * @return An element pointing at the root of the document, or an error: - * - MEMALLOC if realloc_if_needed is true or the parser does not have enough capacity, - * and memory allocation fails. - * - CAPACITY if the parser does not have enough capacity and len > max_capacity. - * - other json errors if parsing fails. You should not rely on these errors to always the same for the - * same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware). - */ - inline simdjson_result parse(const uint8_t *buf, size_t len, bool realloc_if_needed = true) & noexcept; - inline simdjson_result parse(const uint8_t *buf, size_t len, bool realloc_if_needed = true) && =delete; - /** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */ - simdjson_inline simdjson_result parse(const char *buf, size_t len, bool realloc_if_needed = true) & noexcept; - simdjson_inline simdjson_result parse(const char *buf, size_t len, bool realloc_if_needed = true) && =delete; - /** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */ - simdjson_inline simdjson_result parse(const std::string &s) & noexcept; - simdjson_inline simdjson_result parse(const std::string &s) && =delete; - /** @overload parse(const uint8_t *buf, size_t len, bool realloc_if_needed) */ - simdjson_inline simdjson_result parse(const padded_string &s) & noexcept; - simdjson_inline simdjson_result parse(const padded_string &s) && =delete; - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_inline simdjson_result parse(const char *buf) noexcept = delete; - - /** - * Parse a JSON document into a provide document instance and return a temporary reference to it. - * It is similar to the function `parse` except that instead of parsing into the internal - * `document` instance associated with the parser, it allows the user to provide a document - * instance. - * - * dom::parser parser; - * dom::document doc; - * element doc_root = parser.parse_into_document(doc, buf, len); - * - * The function eagerly parses the input: the input can be modified and discarded after - * the `parser.parse(buf, len)` call has completed. - * - * ### IMPORTANT: Document Lifetime - * - * After the call to parse_into_document, the parser is no longer needed. - * - * The JSON document lives in the document instance: you must keep the document - * instance alive while you navigate through it (i.e., used the returned value from - * parse_into_document). You are encourage to reuse the document instance - * many times with new data to avoid reallocations: - * - * dom::document doc; - * element doc_root1 = parser.parse_into_document(doc, buf1, len); - * //... doc_root1 is a pointer inside doc - * element doc_root2 = parser.parse_into_document(doc, buf1, len); - * //... doc_root2 is a pointer inside doc - * // at this point doc_root1 is no longer safe - * - * Moving the document instance is safe, but it invalidates the element instances. After - * moving a document, you can recover safe access to the document root with its `root()` method. - * - * @param doc The document instance where the parsed data will be stored (on success). - * @param buf The JSON to parse. Must have at least len + SIMDJSON_PADDING allocated bytes, unless - * realloc_if_needed is true. - * @param len The length of the JSON. - * @param realloc_if_needed Whether to reallocate and enlarge the JSON buffer to add padding. - * @return An element pointing at the root of document, or an error: - * - MEMALLOC if realloc_if_needed is true or the parser does not have enough capacity, - * and memory allocation fails. - * - CAPACITY if the parser does not have enough capacity and len > max_capacity. - * - other json errors if parsing fails. You should not rely on these errors to always the same for the - * same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware). - */ - inline simdjson_result parse_into_document(document& doc, const uint8_t *buf, size_t len, bool realloc_if_needed = true) & noexcept; - inline simdjson_result parse_into_document(document& doc, const uint8_t *buf, size_t len, bool realloc_if_needed = true) && =delete; - /** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */ - simdjson_inline simdjson_result parse_into_document(document& doc, const char *buf, size_t len, bool realloc_if_needed = true) & noexcept; - simdjson_inline simdjson_result parse_into_document(document& doc, const char *buf, size_t len, bool realloc_if_needed = true) && =delete; - /** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */ - simdjson_inline simdjson_result parse_into_document(document& doc, const std::string &s) & noexcept; - simdjson_inline simdjson_result parse_into_document(document& doc, const std::string &s) && =delete; - /** @overload parse_into_document(const uint8_t *buf, size_t len, bool realloc_if_needed) */ - simdjson_inline simdjson_result parse_into_document(document& doc, const padded_string &s) & noexcept; - simdjson_inline simdjson_result parse_into_document(document& doc, const padded_string &s) && =delete; - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_inline simdjson_result parse_into_document(document& doc, const char *buf) noexcept = delete; - - /** - * Load a file containing many JSON documents. - * - * dom::parser parser; - * for (const element doc : parser.load_many(path)) { - * cout << std::string(doc["title"]) << endl; - * } - * - * The file is loaded in memory and can be safely deleted after the `parser.load_many(path)` - * function has returned. The memory is held by the `parser` instance. - * - * The function is lazy: it may be that no more than one JSON document at a time is parsed. - * And, possibly, no document many have been parsed when the `parser.load_many(path)` function - * returned. - * - * ### Format - * - * The file must contain a series of one or more JSON documents, concatenated into a single - * buffer, separated by whitespace. It effectively parses until it has a fully valid document, - * then starts parsing the next document at that point. (It does this with more parallelism and - * lookahead than you might think, though.) - * - * Documents that consist of an object or array may omit the whitespace between them, concatenating - * with no separator. documents that consist of a single primitive (i.e. documents that are not - * arrays or objects) MUST be separated with whitespace. - * - * The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse. - * Setting batch_size to excessively large or excesively small values may impact negatively the - * performance. - * - * ### Error Handling - * - * All errors are returned during iteration: if there is a global error such as memory allocation, - * it will be yielded as the first result. Iteration always stops after the first error. - * - * As with all other simdjson methods, non-exception error handling is readily available through - * the same interface, requiring you to check the error before using the document: - * - * dom::parser parser; - * dom::document_stream docs; - * auto error = parser.load_many(path).get(docs); - * if (error) { cerr << error << endl; exit(1); } - * for (auto doc : docs) { - * std::string_view title; - * if ((error = doc["title"].get(title)) { cerr << error << endl; exit(1); } - * cout << title << endl; - * } - * - * ### Threads - * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. - * - * ### Parser Capacity - * - * If the parser's current capacity is less than batch_size, it will allocate enough capacity - * to handle it (up to max_capacity). - * - * @param path File name pointing at the concatenated JSON to parse. - * @param batch_size The batch size to use. MUST be larger than the largest document. The sweet - * spot is cache-related: small enough to fit in cache, yet big enough to - * parse as many documents as possible in one tight loop. - * Defaults to 1MB (as simdjson::dom::DEFAULT_BATCH_SIZE), which has been a reasonable sweet - * spot in our tests. - * If you set the batch_size to a value smaller than simdjson::dom::MINIMAL_BATCH_SIZE - * (currently 32B), it will be replaced by simdjson::dom::MINIMAL_BATCH_SIZE. - * @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors: - * - IO_ERROR if there was an error opening or reading the file. - * - MEMALLOC if the parser does not have enough capacity and memory allocation fails. - * - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity. - * - other json errors if parsing fails. You should not rely on these errors to always the same for the - * same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware). - */ - inline simdjson_result load_many(const std::string &path, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; - - /** - * Parse a buffer containing many JSON documents. - * - * dom::parser parser; - * for (element doc : parser.parse_many(buf, len)) { - * cout << std::string(doc["title"]) << endl; - * } - * - * No copy of the input buffer is made. - * - * The function is lazy: it may be that no more than one JSON document at a time is parsed. - * And, possibly, no document many have been parsed when the `parser.load_many(path)` function - * returned. - * - * The caller is responsabile to ensure that the input string data remains unchanged and is - * not deleted during the loop. In particular, the following is unsafe and will not compile: - * - * auto docs = parser.parse_many("[\"temporary data\"]"_padded); - * // here the string "[\"temporary data\"]" may no longer exist in memory - * // the parser instance may not have even accessed the input yet - * for (element doc : docs) { - * cout << std::string(doc["title"]) << endl; - * } - * - * The following is safe: - * - * auto json = "[\"temporary data\"]"_padded; - * auto docs = parser.parse_many(json); - * for (element doc : docs) { - * cout << std::string(doc["title"]) << endl; - * } - * - * ### Format - * - * The buffer must contain a series of one or more JSON documents, concatenated into a single - * buffer, separated by whitespace. It effectively parses until it has a fully valid document, - * then starts parsing the next document at that point. (It does this with more parallelism and - * lookahead than you might think, though.) - * - * documents that consist of an object or array may omit the whitespace between them, concatenating - * with no separator. documents that consist of a single primitive (i.e. documents that are not - * arrays or objects) MUST be separated with whitespace. - * - * The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse. - * Setting batch_size to excessively large or excesively small values may impact negatively the - * performance. - * - * ### Error Handling - * - * All errors are returned during iteration: if there is a global error such as memory allocation, - * it will be yielded as the first result. Iteration always stops after the first error. - * - * As with all other simdjson methods, non-exception error handling is readily available through - * the same interface, requiring you to check the error before using the document: - * - * dom::parser parser; - * dom::document_stream docs; - * auto error = parser.load_many(path).get(docs); - * if (error) { cerr << error << endl; exit(1); } - * for (auto doc : docs) { - * std::string_view title; - * if ((error = doc["title"].get(title)) { cerr << error << endl; exit(1); } - * cout << title << endl; - * } - * - * ### REQUIRED: Buffer Padding - * - * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what - * those bytes are initialized to, as long as they are allocated. - * - * ### Threads - * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. - * - * ### Parser Capacity - * - * If the parser's current capacity is less than batch_size, it will allocate enough capacity - * to handle it (up to max_capacity). - * - * @param buf The concatenated JSON to parse. Must have at least len + SIMDJSON_PADDING allocated bytes. - * @param len The length of the concatenated JSON. - * @param batch_size The batch size to use. MUST be larger than the largest document. The sweet - * spot is cache-related: small enough to fit in cache, yet big enough to - * parse as many documents as possible in one tight loop. - * Defaults to 10MB, which has been a reasonable sweet spot in our tests. - * @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors: - * - MEMALLOC if the parser does not have enough capacity and memory allocation fails - * - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity. - * - other json errors if parsing fails. You should not rely on these errors to always the same for the - * same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware). - */ - inline simdjson_result parse_many(const uint8_t *buf, size_t len, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result parse_many(const char *buf, size_t len, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result parse_many(const std::string &s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; - inline simdjson_result parse_many(const std::string &&s, size_t batch_size) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result parse_many(const padded_string &s, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept; - inline simdjson_result parse_many(const padded_string &&s, size_t batch_size) = delete;// unsafe - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result parse_many(const char *buf, size_t batch_size = dom::DEFAULT_BATCH_SIZE) noexcept = delete; - - /** - * Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length - * and `max_depth` depth. - * - * @param capacity The new capacity. - * @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH. - * @return The error, if there is one. - */ - simdjson_warn_unused inline error_code allocate(size_t capacity, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept; - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API - /** - * @private deprecated because it returns bool instead of error_code, which is our standard for - * failures. Use allocate() instead. - * - * Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length - * and `max_depth` depth. - * - * @param capacity The new capacity. - * @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH. - * @return true if successful, false if allocation failed. - */ - [[deprecated("Use allocate() instead.")]] - simdjson_warn_unused inline bool allocate_capacity(size_t capacity, size_t max_depth = DEFAULT_MAX_DEPTH) noexcept; -#endif // SIMDJSON_DISABLE_DEPRECATED_API - /** - * The largest document this parser can support without reallocating. - * - * @return Current capacity, in bytes. - */ - simdjson_inline size_t capacity() const noexcept; - - /** - * The largest document this parser can automatically support. - * - * The parser may reallocate internal buffers as needed up to this amount. - * - * @return Maximum capacity, in bytes. - */ - simdjson_inline size_t max_capacity() const noexcept; - - /** - * The maximum level of nested object and arrays supported by this parser. - * - * @return Maximum depth, in bytes. - */ - simdjson_inline size_t max_depth() const noexcept; - - /** - * Set max_capacity. This is the largest document this parser can automatically support. - * - * The parser may reallocate internal buffers as needed up to this amount as documents are passed - * to it. - * - * Note: To avoid limiting the memory to an absurd value, such as zero or two bytes, - * iff you try to set max_capacity to a value lower than MINIMAL_DOCUMENT_CAPACITY, - * then the maximal capacity is set to MINIMAL_DOCUMENT_CAPACITY. - * - * This call will not allocate or deallocate, even if capacity is currently above max_capacity. - * - * @param max_capacity The new maximum capacity, in bytes. - */ - simdjson_inline void set_max_capacity(size_t max_capacity) noexcept; - -#ifdef SIMDJSON_THREADS_ENABLED - /** - * The parser instance can use threads when they are available to speed up some - * operations. It is enabled by default. Changing this attribute will change the - * behavior of the parser for future operations. - */ - bool threaded{true}; -#endif - /** @private Use the new DOM API instead */ - class Iterator; - /** @private Use simdjson_error instead */ - using InvalidJSON [[deprecated("Use simdjson_error instead")]] = simdjson_error; - - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - - /** @private Use `if (parser.parse(...).error())` instead */ - bool valid{false}; - /** @private Use `parser.parse(...).error()` instead */ - error_code error{UNINITIALIZED}; - - /** @private Use `parser.parse(...).value()` instead */ - document doc{}; - - /** @private returns true if the document parsed was valid */ - [[deprecated("Use the result of parser.parse() instead")]] - inline bool is_valid() const noexcept; - - /** - * @private return an error code corresponding to the last parsing attempt, see - * simdjson.h will return UNINITIALIZED if no parsing was attempted - */ - [[deprecated("Use the result of parser.parse() instead")]] - inline int get_error_code() const noexcept; - - /** @private return the string equivalent of "get_error_code" */ - [[deprecated("Use error_message() on the result of parser.parse() instead, or cout << error")]] - inline std::string get_error_message() const noexcept; - - /** @private */ - [[deprecated("Use cout << on the result of parser.parse() instead")]] - inline bool print_json(std::ostream &os) const noexcept; - - /** @private Private and deprecated: use `parser.parse(...).doc.dump_raw_tape()` instead */ - inline bool dump_raw_tape(std::ostream &os) const noexcept; - - -private: - /** - * The maximum document length this parser will automatically support. - * - * The parser will not be automatically allocated above this amount. - */ - size_t _max_capacity; - - /** - * The loaded buffer (reused each time load() is called) - */ - std::unique_ptr loaded_bytes; - - /** Capacity of loaded_bytes buffer. */ - size_t _loaded_bytes_capacity{0}; - - // all nodes are stored on the doc.tape using a 64-bit word. - // - // strings, double and ints are stored as - // a 64-bit word with a pointer to the actual value - // - // - // - // for objects or arrays, store [ or { at the beginning and } and ] at the - // end. For the openings ([ or {), we annotate them with a reference to the - // location on the doc.tape of the end, and for then closings (} and ]), we - // annotate them with a reference to the location of the opening - // - // - - /** - * Ensure we have enough capacity to handle at least desired_capacity bytes, - * and auto-allocate if not. This also allocates memory if needed in the - * internal document. - */ - inline error_code ensure_capacity(size_t desired_capacity) noexcept; - /** - * Ensure we have enough capacity to handle at least desired_capacity bytes, - * and auto-allocate if not. This also allocates memory if needed in the - * provided document. - */ - inline error_code ensure_capacity(document& doc, size_t desired_capacity) noexcept; - - /** Read the file into loaded_bytes */ - inline simdjson_result read_file(const std::string &path) noexcept; - - friend class parser::Iterator; - friend class document_stream; - - -}; // class parser - -} // namespace dom -} // namespace simdjson - -#endif // SIMDJSON_DOM_PARSER_H -/* end file include/simdjson/dom/parser.h */ -#ifdef SIMDJSON_THREADS_ENABLED -#include -#include -#include -#endif - -namespace simdjson { -namespace dom { - - -#ifdef SIMDJSON_THREADS_ENABLED -/** @private Custom worker class **/ -struct stage1_worker { - stage1_worker() noexcept = default; - stage1_worker(const stage1_worker&) = delete; - stage1_worker(stage1_worker&&) = delete; - stage1_worker operator=(const stage1_worker&) = delete; - ~stage1_worker(); - /** - * We only start the thread when it is needed, not at object construction, this may throw. - * You should only call this once. - **/ - void start_thread(); - /** - * Start a stage 1 job. You should first call 'run', then 'finish'. - * You must call start_thread once before. - */ - void run(document_stream * ds, dom::parser * stage1, size_t next_batch_start); - /** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/ - void finish(); - -private: - - /** - * Normally, we would never stop the thread. But we do in the destructor. - * This function is only safe assuming that you are not waiting for results. You - * should have called run, then finish, and be done. - **/ - void stop_thread(); - - std::thread thread{}; - /** These three variables define the work done by the thread. **/ - dom::parser * stage1_thread_parser{}; - size_t _next_batch_start{}; - document_stream * owner{}; - /** - * We have two state variables. This could be streamlined to one variable in the future but - * we use two for clarity. - */ - bool has_work{false}; - bool can_work{true}; - - /** - * We lock using a mutex. - */ - std::mutex locking_mutex{}; - std::condition_variable cond_var{}; -}; -#endif - -/** - * A forward-only stream of documents. - * - * Produced by parser::parse_many. - * - */ -class document_stream { -public: - /** - * Construct an uninitialized document_stream. - * - * ```c++ - * document_stream docs; - * error = parser.parse_many(json).get(docs); - * ``` - */ - simdjson_inline document_stream() noexcept; - /** Move one document_stream to another. */ - simdjson_inline document_stream(document_stream &&other) noexcept = default; - /** Move one document_stream to another. */ - simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default; - - simdjson_inline ~document_stream() noexcept; - /** - * Returns the input size in bytes. - */ - inline size_t size_in_bytes() const noexcept; - /** - * After iterating through the stream, this method - * returns the number of bytes that were not parsed at the end - * of the stream. If truncated_bytes() differs from zero, - * then the input was truncated maybe because incomplete JSON - * documents were found at the end of the stream. You - * may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()). - * - * You should only call truncated_bytes() after streaming through all - * documents, like so: - * - * document_stream stream = parser.parse_many(json,window); - * for(auto doc : stream) { - * // do something with doc - * } - * size_t truncated = stream.truncated_bytes(); - * - */ - inline size_t truncated_bytes() const noexcept; - /** - * An iterator through a forward-only stream of documents. - */ - class iterator { - public: - using value_type = simdjson_result; - using reference = value_type; - - using difference_type = std::ptrdiff_t; - - using iterator_category = std::input_iterator_tag; - - /** - * Default constructor. - */ - simdjson_inline iterator() noexcept; - /** - * Get the current document (or error). - */ - simdjson_inline reference operator*() noexcept; - /** - * Advance to the next document (prefix). - */ - inline iterator& operator++() noexcept; - /** - * Check if we're at the end yet. - * @param other the end iterator to compare to. - */ - simdjson_inline bool operator!=(const iterator &other) const noexcept; - /** - * @private - * - * Gives the current index in the input document in bytes. - * - * document_stream stream = parser.parse_many(json,window); - * for(auto i = stream.begin(); i != stream.end(); ++i) { - * auto doc = *i; - * size_t index = i.current_index(); - * } - * - * This function (current_index()) is experimental and the usage - * may change in future versions of simdjson: we find the API somewhat - * awkward and we would like to offer something friendlier. - */ - simdjson_inline size_t current_index() const noexcept; - /** - * @private - * - * Gives a view of the current document. - * - * document_stream stream = parser.parse_many(json,window); - * for(auto i = stream.begin(); i != stream.end(); ++i) { - * auto doc = *i; - * std::string_view v = i->source(); - * } - * - * The returned string_view instance is simply a map to the (unparsed) - * source string: it may thus include white-space characters and all manner - * of padding. - * - * This function (source()) is experimental and the usage - * may change in future versions of simdjson: we find the API somewhat - * awkward and we would like to offer something friendlier. - */ - simdjson_inline std::string_view source() const noexcept; - - private: - simdjson_inline iterator(document_stream *s, bool finished) noexcept; - /** The document_stream we're iterating through. */ - document_stream* stream; - /** Whether we're finished or not. */ - bool finished; - friend class document_stream; - }; - - /** - * Start iterating the documents in the stream. - */ - simdjson_inline iterator begin() noexcept; - /** - * The end of the stream, for iterator comparison purposes. - */ - simdjson_inline iterator end() noexcept; - -private: - - document_stream &operator=(const document_stream &) = delete; // Disallow copying - document_stream(const document_stream &other) = delete; // Disallow copying - - /** - * Construct a document_stream. Does not allocate or parse anything until the iterator is - * used. - * - * @param parser is a reference to the parser instance used to generate this document_stream - * @param buf is the raw byte buffer we need to process - * @param len is the length of the raw byte buffer in bytes - * @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document) - */ - simdjson_inline document_stream( - dom::parser &parser, - const uint8_t *buf, - size_t len, - size_t batch_size - ) noexcept; - - /** - * Parse the first document in the buffer. Used by begin(), to handle allocation and - * initialization. - */ - inline void start() noexcept; - - /** - * Parse the next document found in the buffer previously given to document_stream. - * - * The content should be a valid JSON document encoded as UTF-8. If there is a - * UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are - * discouraged. - * - * You do NOT need to pre-allocate a parser. This function takes care of - * pre-allocating a capacity defined by the batch_size defined when creating the - * document_stream object. - * - * The function returns simdjson::EMPTY if there is no more data to be parsed. - * - * The function returns simdjson::SUCCESS (as integer = 0) in case of success - * and indicates that the buffer has successfully been parsed to the end. - * Every document it contained has been parsed without error. - * - * The function returns an error code from simdjson/simdjson.h in case of failure - * such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth; - * the simdjson::error_message function converts these error codes into a string). - * - * You can also check validity by calling parser.is_valid(). The same parser can - * and should be reused for the other documents in the buffer. - */ - inline void next() noexcept; - - /** - * Pass the next batch through stage 1 and return when finished. - * When threads are enabled, this may wait for the stage 1 thread to finish. - */ - inline void load_batch() noexcept; - - /** Get the next document index. */ - inline size_t next_batch_start() const noexcept; - - /** Pass the next batch through stage 1 with the given parser. */ - inline error_code run_stage1(dom::parser &p, size_t batch_start) noexcept; - - dom::parser *parser; - const uint8_t *buf; - size_t len; - size_t batch_size; - /** The error (or lack thereof) from the current document. */ - error_code error; - size_t batch_start{0}; - size_t doc_index{}; -#ifdef SIMDJSON_THREADS_ENABLED - /** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */ - bool use_thread; - - inline void load_from_stage1_thread() noexcept; - - /** Start a thread to run stage 1 on the next batch. */ - inline void start_stage1_thread() noexcept; - - /** Wait for the stage 1 thread to finish and capture the results. */ - inline void finish_stage1_thread() noexcept; - - /** The error returned from the stage 1 thread. */ - error_code stage1_thread_error{UNINITIALIZED}; - /** The thread used to run stage 1 against the next batch in the background. */ - friend struct stage1_worker; - std::unique_ptr worker{new(std::nothrow) stage1_worker()}; - /** - * The parser used to run stage 1 in the background. Will be swapped - * with the regular parser when finished. - */ - dom::parser stage1_thread_parser{}; -#endif // SIMDJSON_THREADS_ENABLED - - friend class dom::parser; - friend struct simdjson_result; - friend struct internal::simdjson_result_base; - -}; // class document_stream - -} // namespace dom - -template<> -struct simdjson_result : public internal::simdjson_result_base { -public: - simdjson_inline simdjson_result() noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result(dom::document_stream &&value) noexcept; ///< @private - -#if SIMDJSON_EXCEPTIONS - simdjson_inline dom::document_stream::iterator begin() noexcept(false); - simdjson_inline dom::document_stream::iterator end() noexcept(false); -#else // SIMDJSON_EXCEPTIONS -#ifndef SIMDJSON_DISABLE_DEPRECATED_API - [[deprecated("parse_many() and load_many() may return errors. Use document_stream stream; error = parser.parse_many().get(doc); instead.")]] - simdjson_inline dom::document_stream::iterator begin() noexcept; - [[deprecated("parse_many() and load_many() may return errors. Use document_stream stream; error = parser.parse_many().get(doc); instead.")]] - simdjson_inline dom::document_stream::iterator end() noexcept; -#endif // SIMDJSON_DISABLE_DEPRECATED_API -#endif // SIMDJSON_EXCEPTIONS -}; // struct simdjson_result - -} // namespace simdjson - -#endif // SIMDJSON_DOCUMENT_STREAM_H -/* end file include/simdjson/dom/document_stream.h */ -/* begin file include/simdjson/dom/element.h */ -#ifndef SIMDJSON_DOM_ELEMENT_H -#define SIMDJSON_DOM_ELEMENT_H - -#include - -namespace simdjson { -namespace internal { -template -class string_builder; -} -namespace dom { -class array; -class document; -class object; - -/** - * The actual concrete type of a JSON element - * This is the type it is most easily cast to with get<>. - */ -enum class element_type { - ARRAY = '[', ///< dom::array - OBJECT = '{', ///< dom::object - INT64 = 'l', ///< int64_t - UINT64 = 'u', ///< uint64_t: any integer that fits in uint64_t but *not* int64_t - DOUBLE = 'd', ///< double: Any number with a "." or "e" that fits in double. - STRING = '"', ///< std::string_view - BOOL = 't', ///< bool - NULL_VALUE = 'n' ///< null -}; - -/** - * A JSON element. - * - * References an element in a JSON document, representing a JSON null, boolean, string, number, - * array or object. - */ -class element { -public: - /** Create a new, invalid element. */ - simdjson_inline element() noexcept; - - /** The type of this element. */ - simdjson_inline element_type type() const noexcept; - - /** - * Cast this element to an array. - * - * @returns An object that can be used to iterate the array, or: - * INCORRECT_TYPE if the JSON element is not an array. - */ - inline simdjson_result get_array() const noexcept; - /** - * Cast this element to an object. - * - * @returns An object that can be used to look up or iterate the object's fields, or: - * INCORRECT_TYPE if the JSON element is not an object. - */ - inline simdjson_result get_object() const noexcept; - /** - * Cast this element to a null-terminated C string. - * - * The string is guaranteed to be valid UTF-8. - * - * The length of the string is given by get_string_length(). Because JSON strings - * may contain null characters, it may be incorrect to use strlen to determine the - * string length. - * - * It is possible to get a single string_view instance which represents both the string - * content and its length: see get_string(). - * - * @returns A pointer to a null-terminated UTF-8 string. This string is stored in the parser and will - * be invalidated the next time it parses a document or when it is destroyed. - * Returns INCORRECT_TYPE if the JSON element is not a string. - */ - inline simdjson_result get_c_str() const noexcept; - /** - * Gives the length in bytes of the string. - * - * It is possible to get a single string_view instance which represents both the string - * content and its length: see get_string(). - * - * @returns A string length in bytes. - * Returns INCORRECT_TYPE if the JSON element is not a string. - */ - inline simdjson_result get_string_length() const noexcept; - /** - * Cast this element to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next time it - * parses a document or when it is destroyed. - * Returns INCORRECT_TYPE if the JSON element is not a string. - */ - inline simdjson_result get_string() const noexcept; - /** - * Cast this element to a signed integer. - * - * @returns A signed 64-bit integer. - * Returns INCORRECT_TYPE if the JSON element is not an integer, or NUMBER_OUT_OF_RANGE - * if it is negative. - */ - inline simdjson_result get_int64() const noexcept; - /** - * Cast this element to an unsigned integer. - * - * @returns An unsigned 64-bit integer. - * Returns INCORRECT_TYPE if the JSON element is not an integer, or NUMBER_OUT_OF_RANGE - * if it is too large. - */ - inline simdjson_result get_uint64() const noexcept; - /** - * Cast this element to a double floating-point. - * - * @returns A double value. - * Returns INCORRECT_TYPE if the JSON element is not a number. - */ - inline simdjson_result get_double() const noexcept; - /** - * Cast this element to a bool. - * - * @returns A bool value. - * Returns INCORRECT_TYPE if the JSON element is not a boolean. - */ - inline simdjson_result get_bool() const noexcept; - - /** - * Whether this element is a json array. - * - * Equivalent to is(). - */ - inline bool is_array() const noexcept; - /** - * Whether this element is a json object. - * - * Equivalent to is(). - */ - inline bool is_object() const noexcept; - /** - * Whether this element is a json string. - * - * Equivalent to is() or is(). - */ - inline bool is_string() const noexcept; - /** - * Whether this element is a json number that fits in a signed 64-bit integer. - * - * Equivalent to is(). - */ - inline bool is_int64() const noexcept; - /** - * Whether this element is a json number that fits in an unsigned 64-bit integer. - * - * Equivalent to is(). - */ - inline bool is_uint64() const noexcept; - /** - * Whether this element is a json number that fits in a double. - * - * Equivalent to is(). - */ - inline bool is_double() const noexcept; - - /** - * Whether this element is a json number. - * - * Both integers and floating points will return true. - */ - inline bool is_number() const noexcept; - - /** - * Whether this element is a json `true` or `false`. - * - * Equivalent to is(). - */ - inline bool is_bool() const noexcept; - /** - * Whether this element is a json `null`. - */ - inline bool is_null() const noexcept; - - /** - * Tell whether the value can be cast to provided type (T). - * - * Supported types: - * - Boolean: bool - * - Number: double, uint64_t, int64_t - * - String: std::string_view, const char * - * - Array: dom::array - * - Object: dom::object - * - * @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object - */ - template - simdjson_inline bool is() const noexcept; - - /** - * Get the value as the provided type (T). - * - * Supported types: - * - Boolean: bool - * - Number: double, uint64_t, int64_t - * - String: std::string_view, const char * - * - Array: dom::array - * - Object: dom::object - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * get_object(), get_array() or get_string() instead. - * - * @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object - * - * @returns The value cast to the given type, or: - * INCORRECT_TYPE if the value cannot be cast to the given type. - */ - - template - inline simdjson_result get() const noexcept { - // Unless the simdjson library provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library."); - } - - /** - * Get the value as the provided type (T). - * - * Supported types: - * - Boolean: bool - * - Number: double, uint64_t, int64_t - * - String: std::string_view, const char * - * - Array: dom::array - * - Object: dom::object - * - * @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object - * - * @param value The variable to set to the value. May not be set if there is an error. - * - * @returns The error that occurred, or SUCCESS if there was no error. - */ - template - simdjson_warn_unused simdjson_inline error_code get(T &value) const noexcept; - - /** - * Get the value as the provided type (T), setting error if it's not the given type. - * - * Supported types: - * - Boolean: bool - * - Number: double, uint64_t, int64_t - * - String: std::string_view, const char * - * - Array: dom::array - * - Object: dom::object - * - * @tparam T bool, double, uint64_t, int64_t, std::string_view, const char *, dom::array, dom::object - * - * @param value The variable to set to the given type. value is undefined if there is an error. - * @param error The variable to store the error. error is set to error_code::SUCCEED if there is an error. - */ - template - inline void tie(T &value, error_code &error) && noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Read this element as a boolean. - * - * @return The boolean value - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a boolean. - */ - inline operator bool() const noexcept(false); - - /** - * Read this element as a null-terminated UTF-8 string. - * - * Be mindful that JSON allows strings to contain null characters. - * - * Does *not* convert other types to a string; requires that the JSON type of the element was - * an actual string. - * - * @return The string value. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a string. - */ - inline explicit operator const char*() const noexcept(false); - - /** - * Read this element as a null-terminated UTF-8 string. - * - * Does *not* convert other types to a string; requires that the JSON type of the element was - * an actual string. - * - * @return The string value. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a string. - */ - inline operator std::string_view() const noexcept(false); - - /** - * Read this element as an unsigned integer. - * - * @return The integer value. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative - */ - inline operator uint64_t() const noexcept(false); - /** - * Read this element as an signed integer. - * - * @return The integer value. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an integer - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits - */ - inline operator int64_t() const noexcept(false); - /** - * Read this element as an double. - * - * @return The double value. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not a number - * @exception simdjson_error(NUMBER_OUT_OF_RANGE) if the integer doesn't fit in 64 bits or is negative - */ - inline operator double() const noexcept(false); - /** - * Read this element as a JSON array. - * - * @return The JSON array. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array - */ - inline operator array() const noexcept(false); - /** - * Read this element as a JSON object (key/value pairs). - * - * @return The JSON object. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an object - */ - inline operator object() const noexcept(false); - - /** - * Iterate over each element in this array. - * - * @return The beginning of the iteration. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array - */ - inline dom::array::iterator begin() const noexcept(false); - - /** - * Iterate over each element in this array. - * - * @return The end of the iteration. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON element is not an array - */ - inline dom::array::iterator end() const noexcept(false); -#endif // SIMDJSON_EXCEPTIONS - - /** - * Get the value associated with the given key. - * - * The key will be matched against **unescaped** JSON: - * - * dom::parser parser; - * int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1 - * parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD - * - * @return The value associated with this field, or: - * - NO_SUCH_FIELD if the field does not exist in the object - * - INCORRECT_TYPE if this is not an object - */ - inline simdjson_result operator[](std::string_view key) const noexcept; - - /** - * Get the value associated with the given key. - * - * The key will be matched against **unescaped** JSON: - * - * dom::parser parser; - * int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1 - * parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD - * - * @return The value associated with this field, or: - * - NO_SUCH_FIELD if the field does not exist in the object - * - INCORRECT_TYPE if this is not an object - */ - inline simdjson_result operator[](const char *key) const noexcept; - - /** - * Get the value associated with the given JSON pointer. We use the RFC 6901 - * https://tools.ietf.org/html/rfc6901 standard. - * - * dom::parser parser; - * element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded); - * doc.at_pointer("/foo/a/1") == 20 - * doc.at_pointer("/foo")["a"].at(1) == 20 - * doc.at_pointer("")["foo"]["a"].at(1) == 20 - * - * It is allowed for a key to be the empty string: - * - * dom::parser parser; - * object obj = parser.parse(R"({ "": { "a": [ 10, 20, 30 ] }})"_padded); - * obj.at_pointer("//a/1") == 20 - * - * @return The value associated with the given JSON pointer, or: - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed - */ - inline simdjson_result at_pointer(const std::string_view json_pointer) const noexcept; - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API - /** - * - * Version 0.4 of simdjson used an incorrect interpretation of the JSON Pointer standard - * and allowed the following : - * - * dom::parser parser; - * element doc = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded); - * doc.at("foo/a/1") == 20 - * - * Though it is intuitive, it is not compliant with RFC 6901 - * https://tools.ietf.org/html/rfc6901 - * - * For standard compliance, use the at_pointer function instead. - * - * @return The value associated with the given JSON pointer, or: - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed - */ - [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] - inline simdjson_result at(const std::string_view json_pointer) const noexcept; -#endif // SIMDJSON_DISABLE_DEPRECATED_API - - /** - * Get the value at the given index. - * - * @return The value at the given index, or: - * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length - */ - inline simdjson_result at(size_t index) const noexcept; - - /** - * Get the value associated with the given key. - * - * The key will be matched against **unescaped** JSON: - * - * dom::parser parser; - * int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1 - * parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD - * - * @return The value associated with this field, or: - * - NO_SUCH_FIELD if the field does not exist in the object - */ - inline simdjson_result at_key(std::string_view key) const noexcept; - - /** - * Get the value associated with the given key in a case-insensitive manner. - * - * Note: The key will be matched against **unescaped** JSON. - * - * @return The value associated with this field, or: - * - NO_SUCH_FIELD if the field does not exist in the object - */ - inline simdjson_result at_key_case_insensitive(std::string_view key) const noexcept; - - /** @private for debugging. Prints out the root element. */ - inline bool dump_raw_tape(std::ostream &out) const noexcept; - -private: - simdjson_inline element(const internal::tape_ref &tape) noexcept; - internal::tape_ref tape; - friend class document; - friend class object; - friend class array; - friend struct simdjson_result; - template - friend class simdjson::internal::string_builder; - -}; - -} // namespace dom - -/** The result of a JSON navigation that may fail. */ -template<> -struct simdjson_result : public internal::simdjson_result_base { -public: - simdjson_inline simdjson_result() noexcept; ///< @private - simdjson_inline simdjson_result(dom::element &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - - simdjson_inline simdjson_result type() const noexcept; - template - simdjson_inline bool is() const noexcept; - template - simdjson_inline simdjson_result get() const noexcept; - template - simdjson_warn_unused simdjson_inline error_code get(T &value) const noexcept; - - simdjson_inline simdjson_result get_array() const noexcept; - simdjson_inline simdjson_result get_object() const noexcept; - simdjson_inline simdjson_result get_c_str() const noexcept; - simdjson_inline simdjson_result get_string_length() const noexcept; - simdjson_inline simdjson_result get_string() const noexcept; - simdjson_inline simdjson_result get_int64() const noexcept; - simdjson_inline simdjson_result get_uint64() const noexcept; - simdjson_inline simdjson_result get_double() const noexcept; - simdjson_inline simdjson_result get_bool() const noexcept; - - simdjson_inline bool is_array() const noexcept; - simdjson_inline bool is_object() const noexcept; - simdjson_inline bool is_string() const noexcept; - simdjson_inline bool is_int64() const noexcept; - simdjson_inline bool is_uint64() const noexcept; - simdjson_inline bool is_double() const noexcept; - simdjson_inline bool is_number() const noexcept; - simdjson_inline bool is_bool() const noexcept; - simdjson_inline bool is_null() const noexcept; - - simdjson_inline simdjson_result operator[](std::string_view key) const noexcept; - simdjson_inline simdjson_result operator[](const char *key) const noexcept; - simdjson_inline simdjson_result at_pointer(const std::string_view json_pointer) const noexcept; - [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] - simdjson_inline simdjson_result at(const std::string_view json_pointer) const noexcept; - simdjson_inline simdjson_result at(size_t index) const noexcept; - simdjson_inline simdjson_result at_key(std::string_view key) const noexcept; - simdjson_inline simdjson_result at_key_case_insensitive(std::string_view key) const noexcept; - -#if SIMDJSON_EXCEPTIONS - simdjson_inline operator bool() const noexcept(false); - simdjson_inline explicit operator const char*() const noexcept(false); - simdjson_inline operator std::string_view() const noexcept(false); - simdjson_inline operator uint64_t() const noexcept(false); - simdjson_inline operator int64_t() const noexcept(false); - simdjson_inline operator double() const noexcept(false); - simdjson_inline operator dom::array() const noexcept(false); - simdjson_inline operator dom::object() const noexcept(false); - - simdjson_inline dom::array::iterator begin() const noexcept(false); - simdjson_inline dom::array::iterator end() const noexcept(false); -#endif // SIMDJSON_EXCEPTIONS -}; - - -} // namespace simdjson - -#endif // SIMDJSON_DOM_DOCUMENT_H -/* end file include/simdjson/dom/element.h */ -/* begin file include/simdjson/dom/object.h */ -#ifndef SIMDJSON_DOM_OBJECT_H -#define SIMDJSON_DOM_OBJECT_H - - -namespace simdjson { -namespace internal { -template -class string_builder; -} -namespace dom { - -class document; -class element; -class key_value_pair; - -/** - * JSON object. - */ -class object { -public: - /** Create a new, invalid object */ - simdjson_inline object() noexcept; - - class iterator { - public: - using value_type = key_value_pair; - using difference_type = std::ptrdiff_t; - - /** - * Get the actual key/value pair - */ - inline const value_type operator*() const noexcept; - /** - * Get the next key/value pair. - * - * Part of the std::iterator interface. - * - */ - inline iterator& operator++() noexcept; - /** - * Get the next key/value pair. - * - * Part of the std::iterator interface. - * - */ - inline iterator operator++(int) noexcept; - /** - * Check if these values come from the same place in the JSON. - * - * Part of the std::iterator interface. - */ - inline bool operator!=(const iterator& other) const noexcept; - inline bool operator==(const iterator& other) const noexcept; - - inline bool operator<(const iterator& other) const noexcept; - inline bool operator<=(const iterator& other) const noexcept; - inline bool operator>=(const iterator& other) const noexcept; - inline bool operator>(const iterator& other) const noexcept; - /** - * Get the key of this key/value pair. - */ - inline std::string_view key() const noexcept; - /** - * Get the length (in bytes) of the key in this key/value pair. - * You should expect this function to be faster than key().size(). - */ - inline uint32_t key_length() const noexcept; - /** - * Returns true if the key in this key/value pair is equal - * to the provided string_view. - */ - inline bool key_equals(std::string_view o) const noexcept; - /** - * Returns true if the key in this key/value pair is equal - * to the provided string_view in a case-insensitive manner. - * Case comparisons may only be handled correctly for ASCII strings. - */ - inline bool key_equals_case_insensitive(std::string_view o) const noexcept; - /** - * Get the key of this key/value pair. - */ - inline const char *key_c_str() const noexcept; - /** - * Get the value of this key/value pair. - */ - inline element value() const noexcept; - - iterator() noexcept = default; - iterator(const iterator&) noexcept = default; - iterator& operator=(const iterator&) noexcept = default; - private: - simdjson_inline iterator(const internal::tape_ref &tape) noexcept; - - internal::tape_ref tape; - - friend class object; - }; - - /** - * Return the first key/value pair. - * - * Part of the std::iterable interface. - */ - inline iterator begin() const noexcept; - /** - * One past the last key/value pair. - * - * Part of the std::iterable interface. - */ - inline iterator end() const noexcept; - /** - * Get the size of the object (number of keys). - * It is a saturated value with a maximum of 0xFFFFFF: if the value - * is 0xFFFFFF then the size is 0xFFFFFF or greater. - */ - inline size_t size() const noexcept; - /** - * Get the value associated with the given key. - * - * The key will be matched against **unescaped** JSON: - * - * dom::parser parser; - * int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1 - * parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD - * - * This function has linear-time complexity: the keys are checked one by one. - * - * @return The value associated with this field, or: - * - NO_SUCH_FIELD if the field does not exist in the object - * - INCORRECT_TYPE if this is not an object - */ - inline simdjson_result operator[](std::string_view key) const noexcept; - - /** - * Get the value associated with the given key. - * - * The key will be matched against **unescaped** JSON: - * - * dom::parser parser; - * int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1 - * parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD - * - * This function has linear-time complexity: the keys are checked one by one. - * - * @return The value associated with this field, or: - * - NO_SUCH_FIELD if the field does not exist in the object - * - INCORRECT_TYPE if this is not an object - */ - inline simdjson_result operator[](const char *key) const noexcept; - - /** - * Get the value associated with the given JSON pointer. We use the RFC 6901 - * https://tools.ietf.org/html/rfc6901 standard, interpreting the current node - * as the root of its own JSON document. - * - * dom::parser parser; - * object obj = parser.parse(R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded); - * obj.at_pointer("/foo/a/1") == 20 - * obj.at_pointer("/foo")["a"].at(1) == 20 - * - * It is allowed for a key to be the empty string: - * - * dom::parser parser; - * object obj = parser.parse(R"({ "": { "a": [ 10, 20, 30 ] }})"_padded); - * obj.at_pointer("//a/1") == 20 - * obj.at_pointer("/")["a"].at(1) == 20 - * - * @return The value associated with the given JSON pointer, or: - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed - */ - inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; - - /** - * Get the value associated with the given key. - * - * The key will be matched against **unescaped** JSON: - * - * dom::parser parser; - * int64_t(parser.parse(R"({ "a\n": 1 })"_padded)["a\n"]) == 1 - * parser.parse(R"({ "a\n": 1 })"_padded)["a\\n"].get_uint64().error() == NO_SUCH_FIELD - * - * This function has linear-time complexity: the keys are checked one by one. - * - * @return The value associated with this field, or: - * - NO_SUCH_FIELD if the field does not exist in the object - */ - inline simdjson_result at_key(std::string_view key) const noexcept; - - /** - * Get the value associated with the given key in a case-insensitive manner. - * It is only guaranteed to work over ASCII inputs. - * - * Note: The key will be matched against **unescaped** JSON. - * - * This function has linear-time complexity: the keys are checked one by one. - * - * @return The value associated with this field, or: - * - NO_SUCH_FIELD if the field does not exist in the object - */ - inline simdjson_result at_key_case_insensitive(std::string_view key) const noexcept; - -private: - simdjson_inline object(const internal::tape_ref &tape) noexcept; - - internal::tape_ref tape; - - friend class element; - friend struct simdjson_result; - template - friend class simdjson::internal::string_builder; -}; - -/** - * Key/value pair in an object. - */ -class key_value_pair { -public: - /** key in the key-value pair **/ - std::string_view key; - /** value in the key-value pair **/ - element value; - -private: - simdjson_inline key_value_pair(std::string_view _key, element _value) noexcept; - friend class object; -}; - -} // namespace dom - -/** The result of a JSON conversion that may fail. */ -template<> -struct simdjson_result : public internal::simdjson_result_base { -public: - simdjson_inline simdjson_result() noexcept; ///< @private - simdjson_inline simdjson_result(dom::object value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - - inline simdjson_result operator[](std::string_view key) const noexcept; - inline simdjson_result operator[](const char *key) const noexcept; - inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; - inline simdjson_result at_key(std::string_view key) const noexcept; - inline simdjson_result at_key_case_insensitive(std::string_view key) const noexcept; - -#if SIMDJSON_EXCEPTIONS - inline dom::object::iterator begin() const noexcept(false); - inline dom::object::iterator end() const noexcept(false); - inline size_t size() const noexcept(false); -#endif // SIMDJSON_EXCEPTIONS -}; - -} // namespace simdjson - -#if defined(__cpp_lib_ranges) -#include - -namespace std { -namespace ranges { -template<> -inline constexpr bool enable_view = true; -#if SIMDJSON_EXCEPTIONS -template<> -inline constexpr bool enable_view> = true; -#endif // SIMDJSON_EXCEPTIONS -} // namespace ranges -} // namespace std -#endif // defined(__cpp_lib_ranges) - -#endif // SIMDJSON_DOM_OBJECT_H -/* end file include/simdjson/dom/object.h */ -/* begin file include/simdjson/dom/serialization.h */ -#ifndef SIMDJSON_SERIALIZATION_H -#define SIMDJSON_SERIALIZATION_H - -#include - -namespace simdjson { - -/** - * The string_builder template and mini_formatter class - * are not part of our public API and are subject to change - * at any time! - */ -namespace internal { - -class mini_formatter; - -/** - * @private The string_builder template allows us to construct - * a string from a document element. It is parametrized - * by a "formatter" which handles the details. Thus - * the string_builder template could support both minification - * and prettification, and various other tradeoffs. - */ -template -class string_builder { -public: - /** Construct an initially empty builder, would print the empty string **/ - string_builder() = default; - /** Append an element to the builder (to be printed) **/ - inline void append(simdjson::dom::element value); - /** Append an array to the builder (to be printed) **/ - inline void append(simdjson::dom::array value); - /** Append an object to the builder (to be printed) **/ - inline void append(simdjson::dom::object value); - /** Reset the builder (so that it would print the empty string) **/ - simdjson_inline void clear(); - /** - * Get access to the string. The string_view is owned by the builder - * and it is invalid to use it after the string_builder has been - * destroyed. - * However you can make a copy of the string_view on memory that you - * own. - */ - simdjson_inline std::string_view str() const; - /** Append a key_value_pair to the builder (to be printed) **/ - simdjson_inline void append(simdjson::dom::key_value_pair value); -private: - formatter format{}; -}; - -/** - * @private This is the class that we expect to use with the string_builder - * template. It tries to produce a compact version of the JSON element - * as quickly as possible. - */ -class mini_formatter { -public: - mini_formatter() = default; - /** Add a comma **/ - simdjson_inline void comma(); - /** Start an array, prints [ **/ - simdjson_inline void start_array(); - /** End an array, prints ] **/ - simdjson_inline void end_array(); - /** Start an array, prints { **/ - simdjson_inline void start_object(); - /** Start an array, prints } **/ - simdjson_inline void end_object(); - /** Prints a true **/ - simdjson_inline void true_atom(); - /** Prints a false **/ - simdjson_inline void false_atom(); - /** Prints a null **/ - simdjson_inline void null_atom(); - /** Prints a number **/ - simdjson_inline void number(int64_t x); - /** Prints a number **/ - simdjson_inline void number(uint64_t x); - /** Prints a number **/ - simdjson_inline void number(double x); - /** Prints a key (string + colon) **/ - simdjson_inline void key(std::string_view unescaped); - /** Prints a string. The string is escaped as needed. **/ - simdjson_inline void string(std::string_view unescaped); - /** Clears out the content. **/ - simdjson_inline void clear(); - /** - * Get access to the buffer, it is owned by the instance, but - * the user can make a copy. - **/ - simdjson_inline std::string_view str() const; - -private: - // implementation details (subject to change) - /** Prints one character **/ - simdjson_inline void one_char(char c); - /** Backing buffer **/ - std::vector buffer{}; // not ideal! -}; - -} // internal - -namespace dom { - -/** - * Print JSON to an output stream. - * - * @param out The output stream. - * @param value The element. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. - */ -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::element value) { - simdjson::internal::string_builder<> sb; - sb.append(value); - return (out << sb.str()); -} -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#endif -/** - * Print JSON to an output stream. - * - * @param out The output stream. - * @param value The array. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. - */ -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::array value) { - simdjson::internal::string_builder<> sb; - sb.append(value); - return (out << sb.str()); -} -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#endif -/** - * Print JSON to an output stream. - * - * @param out The output stream. - * @param value The object. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. - */ -inline std::ostream& operator<<(std::ostream& out, simdjson::dom::object value) { - simdjson::internal::string_builder<> sb; - sb.append(value); - return (out << sb.str()); -} -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#endif -} // namespace dom - -/** - * Converts JSON to a string. - * - * dom::parser parser; - * element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded); - * cout << to_string(doc) << endl; // prints [1,2,3] - * - */ -template -std::string to_string(T x) { - // in C++, to_string is standard: http://www.cplusplus.com/reference/string/to_string/ - // Currently minify and to_string are identical but in the future, they may - // differ. - simdjson::internal::string_builder<> sb; - sb.append(x); - std::string_view answer = sb.str(); - return std::string(answer.data(), answer.size()); -} -#if SIMDJSON_EXCEPTIONS -template -std::string to_string(simdjson_result x) { - if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); -} -#endif - -/** - * Minifies a JSON element or document, printing the smallest possible valid JSON. - * - * dom::parser parser; - * element doc = parser.parse(" [ 1 , 2 , 3 ] "_padded); - * cout << minify(doc) << endl; // prints [1,2,3] - * - */ -template -std::string minify(T x) { - return to_string(x); -} - -#if SIMDJSON_EXCEPTIONS -template -std::string minify(simdjson_result x) { - if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); -} -#endif - - -} // namespace simdjson - - -#endif -/* end file include/simdjson/dom/serialization.h */ - -// Deprecated API -/* begin file include/simdjson/dom/jsonparser.h */ -// TODO Remove this -- deprecated API and files - -#ifndef SIMDJSON_DOM_JSONPARSER_H -#define SIMDJSON_DOM_JSONPARSER_H - -/* begin file include/simdjson/dom/parsedjson.h */ -// TODO Remove this -- deprecated API and files - -#ifndef SIMDJSON_DOM_PARSEDJSON_H -#define SIMDJSON_DOM_PARSEDJSON_H - - -namespace simdjson { - -/** - * @deprecated Use `dom::parser` instead. - */ -using ParsedJson [[deprecated("Use dom::parser instead")]] = dom::parser; - -} // namespace simdjson - -#endif // SIMDJSON_DOM_PARSEDJSON_H -/* end file include/simdjson/dom/parsedjson.h */ -/* begin file include/simdjson/jsonioutil.h */ -#ifndef SIMDJSON_JSONIOUTIL_H -#define SIMDJSON_JSONIOUTIL_H - - -namespace simdjson { - -#if SIMDJSON_EXCEPTIONS -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -[[deprecated("Use padded_string::load() instead")]] -inline padded_string get_corpus(const char *path) { - return padded_string::load(path); -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API -#endif // SIMDJSON_EXCEPTIONS - -} // namespace simdjson - -#endif // SIMDJSON_JSONIOUTIL_H -/* end file include/simdjson/jsonioutil.h */ - -namespace simdjson { - -// -// C API (json_parse and build_parsed_json) declarations -// - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -[[deprecated("Use parser.parse() instead")]] -inline int json_parse(const uint8_t *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept { - error_code code = parser.parse(buf, len, realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return code; -} -[[deprecated("Use parser.parse() instead")]] -inline int json_parse(const char *buf, size_t len, dom::parser &parser, bool realloc_if_needed = true) noexcept { - error_code code = parser.parse(buf, len, realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return code; -} -[[deprecated("Use parser.parse() instead")]] -inline int json_parse(const std::string &s, dom::parser &parser, bool realloc_if_needed = true) noexcept { - error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return code; -} -[[deprecated("Use parser.parse() instead")]] -inline int json_parse(const padded_string &s, dom::parser &parser) noexcept { - error_code code = parser.parse(s).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return code; -} - -[[deprecated("Use parser.parse() instead")]] -simdjson_warn_unused inline dom::parser build_parsed_json(const uint8_t *buf, size_t len, bool realloc_if_needed = true) noexcept { - dom::parser parser; - error_code code = parser.parse(buf, len, realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return parser; -} -[[deprecated("Use parser.parse() instead")]] -simdjson_warn_unused inline dom::parser build_parsed_json(const char *buf, size_t len, bool realloc_if_needed = true) noexcept { - dom::parser parser; - error_code code = parser.parse(buf, len, realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return parser; -} -[[deprecated("Use parser.parse() instead")]] -simdjson_warn_unused inline dom::parser build_parsed_json(const std::string &s, bool realloc_if_needed = true) noexcept { - dom::parser parser; - error_code code = parser.parse(s.data(), s.length(), realloc_if_needed).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return parser; -} -[[deprecated("Use parser.parse() instead")]] -simdjson_warn_unused inline dom::parser build_parsed_json(const padded_string &s) noexcept { - dom::parser parser; - error_code code = parser.parse(s).error(); - // The deprecated json_parse API is a signal that the user plans to *use* the error code / valid - // bits in the parser instead of heeding the result code. The normal parser unsets those in - // anticipation of making the error code ephemeral. - // Here we put the code back into the parser, until we've removed this method. - parser.valid = code == SUCCESS; - parser.error = code; - return parser; -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API - -/** @private We do not want to allow implicit conversion from C string to std::string. */ -int json_parse(const char *buf, dom::parser &parser) noexcept = delete; -/** @private We do not want to allow implicit conversion from C string to std::string. */ -dom::parser build_parsed_json(const char *buf) noexcept = delete; - -} // namespace simdjson - -#endif // SIMDJSON_DOM_JSONPARSER_H -/* end file include/simdjson/dom/jsonparser.h */ -/* begin file include/simdjson/dom/parsedjson_iterator.h */ -// TODO Remove this -- deprecated API and files - -#ifndef SIMDJSON_DOM_PARSEDJSON_ITERATOR_H -#define SIMDJSON_DOM_PARSEDJSON_ITERATOR_H - -#include -#include -#include -#include -#include -#include - -/* begin file include/simdjson/internal/jsonformatutils.h */ -#ifndef SIMDJSON_INTERNAL_JSONFORMATUTILS_H -#define SIMDJSON_INTERNAL_JSONFORMATUTILS_H - -#include -#include -#include - -namespace simdjson { -namespace internal { - -class escape_json_string; - -inline std::ostream& operator<<(std::ostream& out, const escape_json_string &str); - -class escape_json_string { -public: - escape_json_string(std::string_view _str) noexcept : str{_str} {} - operator std::string() const noexcept { std::stringstream s; s << *this; return s.str(); } -private: - std::string_view str; - friend std::ostream& operator<<(std::ostream& out, const escape_json_string &unescaped); -}; - -inline std::ostream& operator<<(std::ostream& out, const escape_json_string &unescaped) { - for (size_t i=0; i(unescaped.str[i]) <= 0x1F) { - // TODO can this be done once at the beginning, or will it mess up << char? - std::ios::fmtflags f(out.flags()); - out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int(unescaped.str[i]); - out.flags(f); - } else { - out << unescaped.str[i]; - } - } - } - return out; -} - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_JSONFORMATUTILS_H -/* end file include/simdjson/internal/jsonformatutils.h */ - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API - -namespace simdjson { -/** @private **/ -class [[deprecated("Use the new DOM navigation API instead (see doc/basics.md)")]] dom::parser::Iterator { -public: - inline Iterator(const dom::parser &parser) noexcept(false); - inline Iterator(const Iterator &o) noexcept; - inline ~Iterator() noexcept; - - inline Iterator& operator=(const Iterator&) = delete; - - inline bool is_ok() const; - - // useful for debugging purposes - inline size_t get_tape_location() const; - - // useful for debugging purposes - inline size_t get_tape_length() const; - - // returns the current depth (start at 1 with 0 reserved for the fictitious - // root node) - inline size_t get_depth() const; - - // A scope is a series of nodes at the same depth, typically it is either an - // object ({) or an array ([). The root node has type 'r'. - inline uint8_t get_scope_type() const; - - // move forward in document order - inline bool move_forward(); - - // retrieve the character code of what we're looking at: - // [{"slutfn are the possibilities - inline uint8_t get_type() const { - return current_type; // short functions should be inlined! - } - - // get the int64_t value at this node; valid only if get_type is "l" - inline int64_t get_integer() const { - if (location + 1 >= tape_length) { - return 0; // default value in case of error - } - return static_cast(doc.tape[location + 1]); - } - - // get the value as uint64; valid only if if get_type is "u" - inline uint64_t get_unsigned_integer() const { - if (location + 1 >= tape_length) { - return 0; // default value in case of error - } - return doc.tape[location + 1]; - } - - // get the string value at this node (NULL ended); valid only if get_type is " - // note that tabs, and line endings are escaped in the returned value (see - // print_with_escapes) return value is valid UTF-8, it may contain NULL chars - // within the string: get_string_length determines the true string length. - inline const char *get_string() const { - return reinterpret_cast( - doc.string_buf.get() + (current_val & internal::JSON_VALUE_MASK) + sizeof(uint32_t)); - } - - // return the length of the string in bytes - inline uint32_t get_string_length() const { - uint32_t answer; - std::memcpy(&answer, - reinterpret_cast(doc.string_buf.get() + - (current_val & internal::JSON_VALUE_MASK)), - sizeof(uint32_t)); - return answer; - } - - // get the double value at this node; valid only if - // get_type() is "d" - inline double get_double() const { - if (location + 1 >= tape_length) { - return std::numeric_limits::quiet_NaN(); // default value in - // case of error - } - double answer; - std::memcpy(&answer, &doc.tape[location + 1], sizeof(answer)); - return answer; - } - - inline bool is_object_or_array() const { return is_object() || is_array(); } - - inline bool is_object() const { return get_type() == '{'; } - - inline bool is_array() const { return get_type() == '['; } - - inline bool is_string() const { return get_type() == '"'; } - - // Returns true if the current type of the node is an signed integer. - // You can get its value with `get_integer()`. - inline bool is_integer() const { return get_type() == 'l'; } - - // Returns true if the current type of the node is an unsigned integer. - // You can get its value with `get_unsigned_integer()`. - // - // NOTE: - // Only a large value, which is out of range of a 64-bit signed integer, is - // represented internally as an unsigned node. On the other hand, a typical - // positive integer, such as 1, 42, or 1000000, is as a signed node. - // Be aware this function returns false for a signed node. - inline bool is_unsigned_integer() const { return get_type() == 'u'; } - // Returns true if the current type of the node is a double floating-point number. - inline bool is_double() const { return get_type() == 'd'; } - // Returns true if the current type of the node is a number (integer or floating-point). - inline bool is_number() const { - return is_integer() || is_unsigned_integer() || is_double(); - } - // Returns true if the current type of the node is a bool with true value. - inline bool is_true() const { return get_type() == 't'; } - // Returns true if the current type of the node is a bool with false value. - inline bool is_false() const { return get_type() == 'f'; } - // Returns true if the current type of the node is null. - inline bool is_null() const { return get_type() == 'n'; } - // Returns true if the type byte represents an object of an array - static bool is_object_or_array(uint8_t type) { - return ((type == '[') || (type == '{')); - } - - // when at {, go one level deep, looking for a given key - // if successful, we are left pointing at the value, - // if not, we are still pointing at the object ({) - // (in case of repeated keys, this only finds the first one). - // We seek the key using C's strcmp so if your JSON strings contain - // NULL chars, this would trigger a false positive: if you expect that - // to be the case, take extra precautions. - // Furthermore, we do the comparison character-by-character - // without taking into account Unicode equivalence. - inline bool move_to_key(const char *key); - - // as above, but case insensitive lookup (strcmpi instead of strcmp) - inline bool move_to_key_insensitive(const char *key); - - // when at {, go one level deep, looking for a given key - // if successful, we are left pointing at the value, - // if not, we are still pointing at the object ({) - // (in case of repeated keys, this only finds the first one). - // The string we search for can contain NULL values. - // Furthermore, we do the comparison character-by-character - // without taking into account Unicode equivalence. - inline bool move_to_key(const char *key, uint32_t length); - - // when at a key location within an object, this moves to the accompanying - // value (located next to it). This is equivalent but much faster than - // calling "next()". - inline void move_to_value(); - - // when at [, go one level deep, and advance to the given index. - // if successful, we are left pointing at the value, - // if not, we are still pointing at the array ([) - inline bool move_to_index(uint32_t index); - - // Moves the iterator to the value corresponding to the json pointer. - // Always search from the root of the document. - // if successful, we are left pointing at the value, - // if not, we are still pointing the same value we were pointing before the - // call. The json pointer follows the rfc6901 standard's syntax: - // https://tools.ietf.org/html/rfc6901 However, the standard says "If a - // referenced member name is not unique in an object, the member that is - // referenced is undefined, and evaluation fails". Here we just return the - // first corresponding value. The length parameter is the length of the - // jsonpointer string ('pointer'). - inline bool move_to(const char *pointer, uint32_t length); - - // Moves the iterator to the value corresponding to the json pointer. - // Always search from the root of the document. - // if successful, we are left pointing at the value, - // if not, we are still pointing the same value we were pointing before the - // call. The json pointer implementation follows the rfc6901 standard's - // syntax: https://tools.ietf.org/html/rfc6901 However, the standard says - // "If a referenced member name is not unique in an object, the member that - // is referenced is undefined, and evaluation fails". Here we just return - // the first corresponding value. - inline bool move_to(const std::string &pointer) { - return move_to(pointer.c_str(), uint32_t(pointer.length())); - } - - private: - // Almost the same as move_to(), except it searches from the current - // position. The pointer's syntax is identical, though that case is not - // handled by the rfc6901 standard. The '/' is still required at the - // beginning. However, contrary to move_to(), the URI Fragment Identifier - // Representation is not supported here. Also, in case of failure, we are - // left pointing at the closest value it could reach. For these reasons it - // is private. It exists because it is used by move_to(). - inline bool relative_move_to(const char *pointer, uint32_t length); - - public: - // throughout return true if we can do the navigation, false - // otherwise - - // Within a given scope (series of nodes at the same depth within either an - // array or an object), we move forward. - // Thus, given [true, null, {"a":1}, [1,2]], we would visit true, null, { - // and [. At the object ({) or at the array ([), you can issue a "down" to - // visit their content. valid if we're not at the end of a scope (returns - // true). - inline bool next(); - - // Within a given scope (series of nodes at the same depth within either an - // array or an object), we move backward. - // Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true - // when starting at the end of the scope. At the object ({) or at the array - // ([), you can issue a "down" to visit their content. - // Performance warning: This function is implemented by starting again - // from the beginning of the scope and scanning forward. You should expect - // it to be relatively slow. - inline bool prev(); - - // Moves back to either the containing array or object (type { or [) from - // within a contained scope. - // Valid unless we are at the first level of the document - inline bool up(); - - // Valid if we're at a [ or { and it starts a non-empty scope; moves us to - // start of that deeper scope if it not empty. Thus, given [true, null, - // {"a":1}, [1,2]], if we are at the { node, we would move to the "a" node. - inline bool down(); - - // move us to the start of our current scope, - // a scope is a series of nodes at the same level - inline void to_start_scope(); - - inline void rewind() { - while (up()) - ; - } - - - - // print the node we are currently pointing at - inline bool print(std::ostream &os, bool escape_strings = true) const; - - private: - const document &doc; - size_t max_depth{}; - size_t depth{}; - size_t location{}; // our current location on a tape - size_t tape_length{}; - uint8_t current_type{}; - uint64_t current_val{}; - typedef struct { - size_t start_of_scope; - uint8_t scope_type; - } scopeindex_t; - - scopeindex_t *depth_index{}; -}; - -} // namespace simdjson -#endif // SIMDJSON_DISABLE_DEPRECATED_API - -#endif // SIMDJSON_DOM_PARSEDJSON_ITERATOR_H -/* end file include/simdjson/dom/parsedjson_iterator.h */ - -// Inline functions -/* begin file include/simdjson/dom/array-inl.h */ -#ifndef SIMDJSON_INLINE_ARRAY_H -#define SIMDJSON_INLINE_ARRAY_H - -// Inline implementations go in here. - -#include - -namespace simdjson { - -// -// simdjson_result inline implementation -// -simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} -simdjson_inline simdjson_result::simdjson_result(dom::array value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} - -#if SIMDJSON_EXCEPTIONS - -inline dom::array::iterator simdjson_result::begin() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.begin(); -} -inline dom::array::iterator simdjson_result::end() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.end(); -} -inline size_t simdjson_result::size() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.size(); -} - -#endif // SIMDJSON_EXCEPTIONS - -inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) const noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} -inline simdjson_result simdjson_result::at(size_t index) const noexcept { - if (error()) { return error(); } - return first.at(index); -} - -namespace dom { - -// -// array inline implementation -// -simdjson_inline array::array() noexcept : tape{} {} -simdjson_inline array::array(const internal::tape_ref &_tape) noexcept : tape{_tape} {} -inline array::iterator array::begin() const noexcept { - return internal::tape_ref(tape.doc, tape.json_index + 1); -} -inline array::iterator array::end() const noexcept { - return internal::tape_ref(tape.doc, tape.after_element() - 1); -} -inline size_t array::size() const noexcept { - return tape.scope_count(); -} -inline size_t array::number_of_slots() const noexcept { - return tape.matching_brace_index() - tape.json_index; -} -inline simdjson_result array::at_pointer(std::string_view json_pointer) const noexcept { - if(json_pointer.empty()) { // an empty string means that we return the current node - return element(this->tape); // copy the current node - } else if(json_pointer[0] != '/') { // otherwise there is an error - return INVALID_JSON_POINTER; - } - json_pointer = json_pointer.substr(1); - // - means "the append position" or "the element after the end of the array" - // We don't support this, because we're returning a real element, not a position. - if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; } - - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '0'); - // Check for non-digit in array index. If it's there, we're trying to get a field in an object - if (digit > 9) { return INCORRECT_TYPE; } - array_index = array_index*10 + digit; - } - - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - - // Get the child - auto child = array(tape).at(array_index); - // If there is an error, it ends here - if(child.error()) { - return child; - } - // If there is a /, we're not done yet, call recursively. - if (i < json_pointer.length()) { - child = child.at_pointer(json_pointer.substr(i)); - } - return child; -} - -inline simdjson_result array::at(size_t index) const noexcept { - size_t i=0; - for (auto element : *this) { - if (i == index) { return element; } - i++; - } - return INDEX_OUT_OF_BOUNDS; -} - -// -// array::iterator inline implementation -// -simdjson_inline array::iterator::iterator(const internal::tape_ref &_tape) noexcept : tape{_tape} { } -inline element array::iterator::operator*() const noexcept { - return element(tape); -} -inline array::iterator& array::iterator::operator++() noexcept { - tape.json_index = tape.after_element(); - return *this; -} -inline array::iterator array::iterator::operator++(int) noexcept { - array::iterator out = *this; - ++*this; - return out; -} -inline bool array::iterator::operator!=(const array::iterator& other) const noexcept { - return tape.json_index != other.tape.json_index; -} -inline bool array::iterator::operator==(const array::iterator& other) const noexcept { - return tape.json_index == other.tape.json_index; -} -inline bool array::iterator::operator<(const array::iterator& other) const noexcept { - return tape.json_index < other.tape.json_index; -} -inline bool array::iterator::operator<=(const array::iterator& other) const noexcept { - return tape.json_index <= other.tape.json_index; -} -inline bool array::iterator::operator>=(const array::iterator& other) const noexcept { - return tape.json_index >= other.tape.json_index; -} -inline bool array::iterator::operator>(const array::iterator& other) const noexcept { - return tape.json_index > other.tape.json_index; -} - -} // namespace dom - - -} // namespace simdjson - -/* begin file include/simdjson/dom/element-inl.h */ -#ifndef SIMDJSON_INLINE_ELEMENT_H -#define SIMDJSON_INLINE_ELEMENT_H - -#include -#include - -namespace simdjson { - -// -// simdjson_result inline implementation -// -simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} -simdjson_inline simdjson_result::simdjson_result(dom::element &&value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} -inline simdjson_result simdjson_result::type() const noexcept { - if (error()) { return error(); } - return first.type(); -} - -template -simdjson_inline bool simdjson_result::is() const noexcept { - return !error() && first.is(); -} -template -simdjson_inline simdjson_result simdjson_result::get() const noexcept { - if (error()) { return error(); } - return first.get(); -} -template -simdjson_warn_unused simdjson_inline error_code simdjson_result::get(T &value) const noexcept { - if (error()) { return error(); } - return first.get(value); -} - -simdjson_inline simdjson_result simdjson_result::get_array() const noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() const noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_c_str() const noexcept { - if (error()) { return error(); } - return first.get_c_str(); -} -simdjson_inline simdjson_result simdjson_result::get_string_length() const noexcept { - if (error()) { return error(); } - return first.get_string_length(); -} -simdjson_inline simdjson_result simdjson_result::get_string() const noexcept { - if (error()) { return error(); } - return first.get_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() const noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() const noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_double() const noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() const noexcept { - if (error()) { return error(); } - return first.get_bool(); -} - -simdjson_inline bool simdjson_result::is_array() const noexcept { - return !error() && first.is_array(); -} -simdjson_inline bool simdjson_result::is_object() const noexcept { - return !error() && first.is_object(); -} -simdjson_inline bool simdjson_result::is_string() const noexcept { - return !error() && first.is_string(); -} -simdjson_inline bool simdjson_result::is_int64() const noexcept { - return !error() && first.is_int64(); -} -simdjson_inline bool simdjson_result::is_uint64() const noexcept { - return !error() && first.is_uint64(); -} -simdjson_inline bool simdjson_result::is_double() const noexcept { - return !error() && first.is_double(); -} -simdjson_inline bool simdjson_result::is_number() const noexcept { - return !error() && first.is_number(); -} -simdjson_inline bool simdjson_result::is_bool() const noexcept { - return !error() && first.is_bool(); -} - -simdjson_inline bool simdjson_result::is_null() const noexcept { - return !error() && first.is_null(); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) const noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::at_pointer(const std::string_view json_pointer) const noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] -simdjson_inline simdjson_result simdjson_result::at(const std::string_view json_pointer) const noexcept { -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_DEPRECATED_WARNING - if (error()) { return error(); } - return first.at(json_pointer); -SIMDJSON_POP_DISABLE_WARNINGS -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API -simdjson_inline simdjson_result simdjson_result::at(size_t index) const noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { - if (error()) { return error(); } - return first.at_key(key); -} -simdjson_inline simdjson_result simdjson_result::at_key_case_insensitive(std::string_view key) const noexcept { - if (error()) { return error(); } - return first.at_key_case_insensitive(key); -} - -#if SIMDJSON_EXCEPTIONS - -simdjson_inline simdjson_result::operator bool() const noexcept(false) { - return get(); -} -simdjson_inline simdjson_result::operator const char *() const noexcept(false) { - return get(); -} -simdjson_inline simdjson_result::operator std::string_view() const noexcept(false) { - return get(); -} -simdjson_inline simdjson_result::operator uint64_t() const noexcept(false) { - return get(); -} -simdjson_inline simdjson_result::operator int64_t() const noexcept(false) { - return get(); -} -simdjson_inline simdjson_result::operator double() const noexcept(false) { - return get(); -} -simdjson_inline simdjson_result::operator dom::array() const noexcept(false) { - return get(); -} -simdjson_inline simdjson_result::operator dom::object() const noexcept(false) { - return get(); -} - -simdjson_inline dom::array::iterator simdjson_result::begin() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.begin(); -} -simdjson_inline dom::array::iterator simdjson_result::end() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.end(); -} - -#endif // SIMDJSON_EXCEPTIONS - -namespace dom { - -// -// element inline implementation -// -simdjson_inline element::element() noexcept : tape{} {} -simdjson_inline element::element(const internal::tape_ref &_tape) noexcept : tape{_tape} { } - -inline element_type element::type() const noexcept { - auto tape_type = tape.tape_ref_type(); - return tape_type == internal::tape_type::FALSE_VALUE ? element_type::BOOL : static_cast(tape_type); -} - -inline simdjson_result element::get_bool() const noexcept { - if(tape.is_true()) { - return true; - } else if(tape.is_false()) { - return false; - } - return INCORRECT_TYPE; -} -inline simdjson_result element::get_c_str() const noexcept { - switch (tape.tape_ref_type()) { - case internal::tape_type::STRING: { - return tape.get_c_str(); - } - default: - return INCORRECT_TYPE; - } -} -inline simdjson_result element::get_string_length() const noexcept { - switch (tape.tape_ref_type()) { - case internal::tape_type::STRING: { - return tape.get_string_length(); - } - default: - return INCORRECT_TYPE; - } -} -inline simdjson_result element::get_string() const noexcept { - switch (tape.tape_ref_type()) { - case internal::tape_type::STRING: - return tape.get_string_view(); - default: - return INCORRECT_TYPE; - } -} -inline simdjson_result element::get_uint64() const noexcept { - if(simdjson_unlikely(!tape.is_uint64())) { // branch rarely taken - if(tape.is_int64()) { - int64_t result = tape.next_tape_value(); - if (result < 0) { - return NUMBER_OUT_OF_RANGE; - } - return uint64_t(result); - } - return INCORRECT_TYPE; - } - return tape.next_tape_value(); -} -inline simdjson_result element::get_int64() const noexcept { - if(simdjson_unlikely(!tape.is_int64())) { // branch rarely taken - if(tape.is_uint64()) { - uint64_t result = tape.next_tape_value(); - // Wrapping max in parens to handle Windows issue: https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std - if (result > uint64_t((std::numeric_limits::max)())) { - return NUMBER_OUT_OF_RANGE; - } - return static_cast(result); - } - return INCORRECT_TYPE; - } - return tape.next_tape_value(); -} -inline simdjson_result element::get_double() const noexcept { - // Performance considerations: - // 1. Querying tape_ref_type() implies doing a shift, it is fast to just do a straight - // comparison. - // 2. Using a switch-case relies on the compiler guessing what kind of code generation - // we want... But the compiler cannot know that we expect the type to be "double" - // most of the time. - // We can expect get to refer to a double type almost all the time. - // It is important to craft the code accordingly so that the compiler can use this - // information. (This could also be solved with profile-guided optimization.) - if(simdjson_unlikely(!tape.is_double())) { // branch rarely taken - if(tape.is_uint64()) { - return double(tape.next_tape_value()); - } else if(tape.is_int64()) { - return double(tape.next_tape_value()); - } - return INCORRECT_TYPE; - } - // this is common: - return tape.next_tape_value(); -} -inline simdjson_result element::get_array() const noexcept { - switch (tape.tape_ref_type()) { - case internal::tape_type::START_ARRAY: - return array(tape); - default: - return INCORRECT_TYPE; - } -} -inline simdjson_result element::get_object() const noexcept { - switch (tape.tape_ref_type()) { - case internal::tape_type::START_OBJECT: - return object(tape); - default: - return INCORRECT_TYPE; - } -} - -template -simdjson_warn_unused simdjson_inline error_code element::get(T &value) const noexcept { - return get().get(value); -} -// An element-specific version prevents recursion with simdjson_result::get(value) -template<> -simdjson_warn_unused simdjson_inline error_code element::get(element &value) const noexcept { - value = element(tape); - return SUCCESS; -} -template -inline void element::tie(T &value, error_code &error) && noexcept { - error = get(value); -} - -template -simdjson_inline bool element::is() const noexcept { - auto result = get(); - return !result.error(); -} - -template<> inline simdjson_result element::get() const noexcept { return get_array(); } -template<> inline simdjson_result element::get() const noexcept { return get_object(); } -template<> inline simdjson_result element::get() const noexcept { return get_c_str(); } -template<> inline simdjson_result element::get() const noexcept { return get_string(); } -template<> inline simdjson_result element::get() const noexcept { return get_int64(); } -template<> inline simdjson_result element::get() const noexcept { return get_uint64(); } -template<> inline simdjson_result element::get() const noexcept { return get_double(); } -template<> inline simdjson_result element::get() const noexcept { return get_bool(); } - -inline bool element::is_array() const noexcept { return is(); } -inline bool element::is_object() const noexcept { return is(); } -inline bool element::is_string() const noexcept { return is(); } -inline bool element::is_int64() const noexcept { return is(); } -inline bool element::is_uint64() const noexcept { return is(); } -inline bool element::is_double() const noexcept { return is(); } -inline bool element::is_bool() const noexcept { return is(); } -inline bool element::is_number() const noexcept { return is_int64() || is_uint64() || is_double(); } - -inline bool element::is_null() const noexcept { - return tape.is_null_on_tape(); -} - -#if SIMDJSON_EXCEPTIONS - -inline element::operator bool() const noexcept(false) { return get(); } -inline element::operator const char*() const noexcept(false) { return get(); } -inline element::operator std::string_view() const noexcept(false) { return get(); } -inline element::operator uint64_t() const noexcept(false) { return get(); } -inline element::operator int64_t() const noexcept(false) { return get(); } -inline element::operator double() const noexcept(false) { return get(); } -inline element::operator array() const noexcept(false) { return get(); } -inline element::operator object() const noexcept(false) { return get(); } - -inline array::iterator element::begin() const noexcept(false) { - return get().begin(); -} -inline array::iterator element::end() const noexcept(false) { - return get().end(); -} - -#endif // SIMDJSON_EXCEPTIONS - -inline simdjson_result element::operator[](std::string_view key) const noexcept { - return at_key(key); -} -inline simdjson_result element::operator[](const char *key) const noexcept { - return at_key(key); -} - -inline simdjson_result element::at_pointer(std::string_view json_pointer) const noexcept { - switch (tape.tape_ref_type()) { - case internal::tape_type::START_OBJECT: - return object(tape).at_pointer(json_pointer); - case internal::tape_type::START_ARRAY: - return array(tape).at_pointer(json_pointer); - default: { - if(!json_pointer.empty()) { // a non-empty string is invalid on an atom - return INVALID_JSON_POINTER; - } - // an empty string means that we return the current node - dom::element copy(*this); - return simdjson_result(std::move(copy)); - } - } -} -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -[[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] -inline simdjson_result element::at(std::string_view json_pointer) const noexcept { - // version 0.4 of simdjson allowed non-compliant pointers - auto std_pointer = (json_pointer.empty() ? "" : "/") + std::string(json_pointer.begin(), json_pointer.end()); - return at_pointer(std_pointer); -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API - -inline simdjson_result element::at(size_t index) const noexcept { - return get().at(index); -} -inline simdjson_result element::at_key(std::string_view key) const noexcept { - return get().at_key(key); -} -inline simdjson_result element::at_key_case_insensitive(std::string_view key) const noexcept { - return get().at_key_case_insensitive(key); -} - -inline bool element::dump_raw_tape(std::ostream &out) const noexcept { - return tape.doc->dump_raw_tape(out); -} - - -inline std::ostream& operator<<(std::ostream& out, element_type type) { - switch (type) { - case element_type::ARRAY: - return out << "array"; - case element_type::OBJECT: - return out << "object"; - case element_type::INT64: - return out << "int64_t"; - case element_type::UINT64: - return out << "uint64_t"; - case element_type::DOUBLE: - return out << "double"; - case element_type::STRING: - return out << "string"; - case element_type::BOOL: - return out << "bool"; - case element_type::NULL_VALUE: - return out << "null"; - default: - return out << "unexpected content!!!"; // abort() usage is forbidden in the library - } -} - -} // namespace dom - -} // namespace simdjson - -#endif // SIMDJSON_INLINE_ELEMENT_H -/* end file include/simdjson/dom/element-inl.h */ - -#if defined(__cpp_lib_ranges) -static_assert(std::ranges::view); -static_assert(std::ranges::sized_range); -#if SIMDJSON_EXCEPTIONS -static_assert(std::ranges::view>); -static_assert(std::ranges::sized_range>); -#endif // SIMDJSON_EXCEPTIONS -#endif // defined(__cpp_lib_ranges) - -#endif // SIMDJSON_INLINE_ARRAY_H -/* end file include/simdjson/dom/array-inl.h */ -/* begin file include/simdjson/dom/document_stream-inl.h */ -#ifndef SIMDJSON_INLINE_DOCUMENT_STREAM_H -#define SIMDJSON_INLINE_DOCUMENT_STREAM_H - -#include -#include -#include -namespace simdjson { -namespace dom { - -#ifdef SIMDJSON_THREADS_ENABLED -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, dom::parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} -#endif - -simdjson_inline document_stream::document_stream( - dom::parser &_parser, - const uint8_t *_buf, - size_t _len, - size_t _batch_size -) noexcept - : parser{&_parser}, - buf{_buf}, - len{_len}, - batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size}, - error{SUCCESS} -#ifdef SIMDJSON_THREADS_ENABLED - , use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change -#endif -{ -#ifdef SIMDJSON_THREADS_ENABLED - if(worker.get() == nullptr) { - error = MEMALLOC; - } -#endif -} - -simdjson_inline document_stream::document_stream() noexcept - : parser{nullptr}, - buf{nullptr}, - len{0}, - batch_size{0}, - error{UNINITIALIZED} -#ifdef SIMDJSON_THREADS_ENABLED - , use_thread(false) -#endif -{ -} - -simdjson_inline document_stream::~document_stream() noexcept { -#ifdef SIMDJSON_THREADS_ENABLED - worker.reset(); -#endif -} - -simdjson_inline document_stream::iterator::iterator() noexcept - : stream{nullptr}, finished{true} { -} - -simdjson_inline document_stream::iterator document_stream::begin() noexcept { - start(); - // If there are no documents, we're finished. - return iterator(this, error == EMPTY); -} - -simdjson_inline document_stream::iterator document_stream::end() noexcept { - return iterator(this, true); -} - -simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept - : stream{_stream}, finished{is_end} { -} - -simdjson_inline document_stream::iterator::reference document_stream::iterator::operator*() noexcept { - // Note that in case of error, we do not yet mark - // the iterator as "finished": this detection is done - // in the operator++ function since it is possible - // to call operator++ repeatedly while omitting - // calls to operator*. - if (stream->error) { return stream->error; } - return stream->parser->doc.root(); -} - -simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept { - // If there is an error, then we want the iterator - // to be finished, no matter what. (E.g., we do not - // keep generating documents with errors, or go beyond - // a document with errors.) - // - // Users do not have to call "operator*()" when they use operator++, - // so we need to end the stream in the operator++ function. - // - // Note that setting finished = true is essential otherwise - // we would enter an infinite loop. - if (stream->error) { finished = true; } - // Note that stream->error() is guarded against error conditions - // (it will immediately return if stream->error casts to false). - // In effect, this next function does nothing when (stream->error) - // is true (hence the risk of an infinite loop). - stream->next(); - // If that was the last document, we're finished. - // It is the only type of error we do not want to appear - // in operator*. - if (stream->error == EMPTY) { finished = true; } - // If we had any other kind of error (not EMPTY) then we want - // to pass it along to the operator* and we cannot mark the result - // as "finished" just yet. - return *this; -} - -simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { - return finished != other.finished; -} - -inline void document_stream::start() noexcept { - if (error) { return; } - error = parser->ensure_capacity(batch_size); - if (error) { return; } - // Always run the first stage 1 parse immediately - batch_start = 0; - error = run_stage1(*parser, batch_start); - while(error == EMPTY) { - // In exceptional cases, we may start with an empty block - batch_start = next_batch_start(); - if (batch_start >= len) { return; } - error = run_stage1(*parser, batch_start); - } - if (error) { return; } -#ifdef SIMDJSON_THREADS_ENABLED - if (use_thread && next_batch_start() < len) { - // Kick off the first thread if needed - error = stage1_thread_parser.ensure_capacity(batch_size); - if (error) { return; } - worker->start_thread(); - start_stage1_thread(); - if (error) { return; } - } -#endif // SIMDJSON_THREADS_ENABLED - next(); -} - -simdjson_inline size_t document_stream::iterator::current_index() const noexcept { - return stream->doc_index; -} - -simdjson_inline std::string_view document_stream::iterator::source() const noexcept { - const char* start = reinterpret_cast(stream->buf) + current_index(); - bool object_or_array = ((*start == '[') || (*start == '{')); - if(object_or_array) { - size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index - 1]; - return std::string_view(start, next_doc_index - current_index() + 1); - } else { - size_t next_doc_index = stream->batch_start + stream->parser->implementation->structural_indexes[stream->parser->implementation->next_structural_index]; - return std::string_view(reinterpret_cast(stream->buf) + current_index(), next_doc_index - current_index() - 1); - } -} - - -inline void document_stream::next() noexcept { - // We always exit at once, once in an error condition. - if (error) { return; } - - // Load the next document from the batch - doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index]; - error = parser->implementation->stage2_next(parser->doc); - // If that was the last document in the batch, load another batch (if available) - while (error == EMPTY) { - batch_start = next_batch_start(); - if (batch_start >= len) { break; } - -#ifdef SIMDJSON_THREADS_ENABLED - if(use_thread) { - load_from_stage1_thread(); - } else { - error = run_stage1(*parser, batch_start); - } -#else - error = run_stage1(*parser, batch_start); -#endif - if (error) { continue; } // If the error was EMPTY, we may want to load another batch. - // Run stage 2 on the first document in the batch - doc_index = batch_start + parser->implementation->structural_indexes[parser->implementation->next_structural_index]; - error = parser->implementation->stage2_next(parser->doc); - } -} -inline size_t document_stream::size_in_bytes() const noexcept { - return len; -} - -inline size_t document_stream::truncated_bytes() const noexcept { - if(error == CAPACITY) { return len - batch_start; } - return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1]; -} - -inline size_t document_stream::next_batch_start() const noexcept { - return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; -} - -inline error_code document_stream::run_stage1(dom::parser &p, size_t _batch_start) noexcept { - size_t remaining = len - _batch_start; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } -} - -#ifdef SIMDJSON_THREADS_ENABLED - -inline void document_stream::load_from_stage1_thread() noexcept { - worker->finish(); - // Swap to the parser that was loaded up in the thread. Make sure the parser has - // enough memory to swap to, as well. - std::swap(*parser, stage1_thread_parser); - error = stage1_thread_error; - if (error) { return; } - - // If there's anything left, start the stage 1 thread! - if (next_batch_start() < len) { - start_stage1_thread(); - } -} - -inline void document_stream::start_stage1_thread() noexcept { - // we call the thread on a lambda that will update - // this->stage1_thread_error - // there is only one thread that may write to this value - // TODO this is NOT exception-safe. - this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error - size_t _next_batch_start = this->next_batch_start(); - - worker->run(this, & this->stage1_thread_parser, _next_batch_start); -} - -#endif // SIMDJSON_THREADS_ENABLED - -} // namespace dom - -simdjson_inline simdjson_result::simdjson_result() noexcept - : simdjson_result_base() { -} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : simdjson_result_base(error) { -} -simdjson_inline simdjson_result::simdjson_result(dom::document_stream &&value) noexcept - : simdjson_result_base(std::forward(value)) { -} - -#if SIMDJSON_EXCEPTIONS -simdjson_inline dom::document_stream::iterator simdjson_result::begin() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.begin(); -} -simdjson_inline dom::document_stream::iterator simdjson_result::end() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.end(); -} -#else // SIMDJSON_EXCEPTIONS -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -simdjson_inline dom::document_stream::iterator simdjson_result::begin() noexcept { - first.error = error(); - return first.begin(); -} -simdjson_inline dom::document_stream::iterator simdjson_result::end() noexcept { - first.error = error(); - return first.end(); -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API -#endif // SIMDJSON_EXCEPTIONS - -} // namespace simdjson -#endif // SIMDJSON_INLINE_DOCUMENT_STREAM_H -/* end file include/simdjson/dom/document_stream-inl.h */ -/* begin file include/simdjson/dom/document-inl.h */ -#ifndef SIMDJSON_INLINE_DOCUMENT_H -#define SIMDJSON_INLINE_DOCUMENT_H - -// Inline implementations go in here. - -#include -#include - -namespace simdjson { -namespace dom { - -// -// document inline implementation -// -inline element document::root() const noexcept { - return element(internal::tape_ref(this, 1)); -} -simdjson_warn_unused -inline size_t document::capacity() const noexcept { - return allocated_capacity; -} - -simdjson_warn_unused -inline error_code document::allocate(size_t capacity) noexcept { - if (capacity == 0) { - string_buf.reset(); - tape.reset(); - allocated_capacity = 0; - return SUCCESS; - } - - // a pathological input like "[[[[..." would generate capacity tape elements, so - // need a capacity of at least capacity + 1, but it is also possible to do - // worse with "[7,7,7,7,6,7,7,7,6,7,7,6,[7,7,7,7,6,7,7,7,6,7,7,6,7,7,7,7,7,7,6" - //where capacity + 1 tape elements are - // generated, see issue https://github.com/simdjson/simdjson/issues/345 - size_t tape_capacity = SIMDJSON_ROUNDUP_N(capacity + 3, 64); - // a document with only zero-length strings... could have capacity/3 string - // and we would need capacity/3 * 5 bytes on the string buffer - size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * capacity / 3 + SIMDJSON_PADDING, 64); - string_buf.reset( new (std::nothrow) uint8_t[string_capacity]); - tape.reset(new (std::nothrow) uint64_t[tape_capacity]); - if(!(string_buf && tape)) { - allocated_capacity = 0; - string_buf.reset(); - tape.reset(); - return MEMALLOC; - } - // Technically the allocated_capacity might be larger than capacity - // so the next line is pessimistic. - allocated_capacity = capacity; - return SUCCESS; -} - -inline bool document::dump_raw_tape(std::ostream &os) const noexcept { - uint32_t string_length; - size_t tape_idx = 0; - uint64_t tape_val = tape[tape_idx]; - uint8_t type = uint8_t(tape_val >> 56); - os << tape_idx << " : " << type; - tape_idx++; - size_t how_many = 0; - if (type == 'r') { - how_many = size_t(tape_val & internal::JSON_VALUE_MASK); - } else { - // Error: no starting root node? - return false; - } - os << "\t// pointing to " << how_many << " (right after last node)\n"; - uint64_t payload; - for (; tape_idx < how_many; tape_idx++) { - os << tape_idx << " : "; - tape_val = tape[tape_idx]; - payload = tape_val & internal::JSON_VALUE_MASK; - type = uint8_t(tape_val >> 56); - switch (type) { - case '"': // we have a string - os << "string \""; - std::memcpy(&string_length, string_buf.get() + payload, sizeof(uint32_t)); - os << internal::escape_json_string(std::string_view( - reinterpret_cast(string_buf.get() + payload + sizeof(uint32_t)), - string_length - )); - os << '"'; - os << '\n'; - break; - case 'l': // we have a long int - if (tape_idx + 1 >= how_many) { - return false; - } - os << "integer " << static_cast(tape[++tape_idx]) << "\n"; - break; - case 'u': // we have a long uint - if (tape_idx + 1 >= how_many) { - return false; - } - os << "unsigned integer " << tape[++tape_idx] << "\n"; - break; - case 'd': // we have a double - os << "float "; - if (tape_idx + 1 >= how_many) { - return false; - } - double answer; - std::memcpy(&answer, &tape[++tape_idx], sizeof(answer)); - os << answer << '\n'; - break; - case 'n': // we have a null - os << "null\n"; - break; - case 't': // we have a true - os << "true\n"; - break; - case 'f': // we have a false - os << "false\n"; - break; - case '{': // we have an object - os << "{\t// pointing to next tape location " << uint32_t(payload) - << " (first node after the scope), " - << " saturated count " - << ((payload >> 32) & internal::JSON_COUNT_MASK)<< "\n"; - break; case '}': // we end an object - os << "}\t// pointing to previous tape location " << uint32_t(payload) - << " (start of the scope)\n"; - break; - case '[': // we start an array - os << "[\t// pointing to next tape location " << uint32_t(payload) - << " (first node after the scope), " - << " saturated count " - << ((payload >> 32) & internal::JSON_COUNT_MASK)<< "\n"; - break; - case ']': // we end an array - os << "]\t// pointing to previous tape location " << uint32_t(payload) - << " (start of the scope)\n"; - break; - case 'r': // we start and end with the root node - // should we be hitting the root node? - return false; - default: - return false; - } - } - tape_val = tape[tape_idx]; - payload = tape_val & internal::JSON_VALUE_MASK; - type = uint8_t(tape_val >> 56); - os << tape_idx << " : " << type << "\t// pointing to " << payload - << " (start root)\n"; - return true; -} - -} // namespace dom -} // namespace simdjson - -#endif // SIMDJSON_INLINE_DOCUMENT_H -/* end file include/simdjson/dom/document-inl.h */ -/* begin file include/simdjson/dom/object-inl.h */ -#ifndef SIMDJSON_INLINE_OBJECT_H -#define SIMDJSON_INLINE_OBJECT_H - -#include -#include - -namespace simdjson { - -// -// simdjson_result inline implementation -// -simdjson_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base() {} -simdjson_inline simdjson_result::simdjson_result(dom::object value) noexcept - : internal::simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base(error) {} - -inline simdjson_result simdjson_result::operator[](std::string_view key) const noexcept { - if (error()) { return error(); } - return first[key]; -} -inline simdjson_result simdjson_result::operator[](const char *key) const noexcept { - if (error()) { return error(); } - return first[key]; -} -inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) const noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} -inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { - if (error()) { return error(); } - return first.at_key(key); -} -inline simdjson_result simdjson_result::at_key_case_insensitive(std::string_view key) const noexcept { - if (error()) { return error(); } - return first.at_key_case_insensitive(key); -} - -#if SIMDJSON_EXCEPTIONS - -inline dom::object::iterator simdjson_result::begin() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.begin(); -} -inline dom::object::iterator simdjson_result::end() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.end(); -} -inline size_t simdjson_result::size() const noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first.size(); -} - -#endif // SIMDJSON_EXCEPTIONS - -namespace dom { - -// -// object inline implementation -// -simdjson_inline object::object() noexcept : tape{} {} -simdjson_inline object::object(const internal::tape_ref &_tape) noexcept : tape{_tape} { } -inline object::iterator object::begin() const noexcept { - return internal::tape_ref(tape.doc, tape.json_index + 1); -} -inline object::iterator object::end() const noexcept { - return internal::tape_ref(tape.doc, tape.after_element() - 1); -} -inline size_t object::size() const noexcept { - return tape.scope_count(); -} - -inline simdjson_result object::operator[](std::string_view key) const noexcept { - return at_key(key); -} -inline simdjson_result object::operator[](const char *key) const noexcept { - return at_key(key); -} -inline simdjson_result object::at_pointer(std::string_view json_pointer) const noexcept { - if(json_pointer.empty()) { // an empty string means that we return the current node - return element(this->tape); // copy the current node - } else if(json_pointer[0] != '/') { // otherwise there is an error - return INVALID_JSON_POINTER; - } - json_pointer = json_pointer.substr(1); - size_t slash = json_pointer.find('/'); - std::string_view key = json_pointer.substr(0, slash); - // Grab the child with the given key - simdjson_result child; - - // If there is an escape character in the key, unescape it and then get the child. - size_t escape = key.find('~'); - if (escape != std::string_view::npos) { - // Unescape the key - std::string unescaped(key); - do { - switch (unescaped[escape+1]) { - case '0': - unescaped.replace(escape, 2, "~"); - break; - case '1': - unescaped.replace(escape, 2, "/"); - break; - default: - return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer"); - } - escape = unescaped.find('~', escape+1); - } while (escape != std::string::npos); - child = at_key(unescaped); - } else { - child = at_key(key); - } - if(child.error()) { - return child; // we do not continue if there was an error - } - // If there is a /, we have to recurse and look up more of the path - if (slash != std::string_view::npos) { - child = child.at_pointer(json_pointer.substr(slash)); - } - return child; -} - -inline simdjson_result object::at_key(std::string_view key) const noexcept { - iterator end_field = end(); - for (iterator field = begin(); field != end_field; ++field) { - if (field.key_equals(key)) { - return field.value(); - } - } - return NO_SUCH_FIELD; -} -// In case you wonder why we need this, please see -// https://github.com/simdjson/simdjson/issues/323 -// People do seek keys in a case-insensitive manner. -inline simdjson_result object::at_key_case_insensitive(std::string_view key) const noexcept { - iterator end_field = end(); - for (iterator field = begin(); field != end_field; ++field) { - if (field.key_equals_case_insensitive(key)) { - return field.value(); - } - } - return NO_SUCH_FIELD; -} - -// -// object::iterator inline implementation -// -simdjson_inline object::iterator::iterator(const internal::tape_ref &_tape) noexcept : tape{_tape} { } -inline const key_value_pair object::iterator::operator*() const noexcept { - return key_value_pair(key(), value()); -} -inline bool object::iterator::operator!=(const object::iterator& other) const noexcept { - return tape.json_index != other.tape.json_index; -} -inline bool object::iterator::operator==(const object::iterator& other) const noexcept { - return tape.json_index == other.tape.json_index; -} -inline bool object::iterator::operator<(const object::iterator& other) const noexcept { - return tape.json_index < other.tape.json_index; -} -inline bool object::iterator::operator<=(const object::iterator& other) const noexcept { - return tape.json_index <= other.tape.json_index; -} -inline bool object::iterator::operator>=(const object::iterator& other) const noexcept { - return tape.json_index >= other.tape.json_index; -} -inline bool object::iterator::operator>(const object::iterator& other) const noexcept { - return tape.json_index > other.tape.json_index; -} -inline object::iterator& object::iterator::operator++() noexcept { - tape.json_index++; - tape.json_index = tape.after_element(); - return *this; -} -inline object::iterator object::iterator::operator++(int) noexcept { - object::iterator out = *this; - ++*this; - return out; -} -inline std::string_view object::iterator::key() const noexcept { - return tape.get_string_view(); -} -inline uint32_t object::iterator::key_length() const noexcept { - return tape.get_string_length(); -} -inline const char* object::iterator::key_c_str() const noexcept { - return reinterpret_cast(&tape.doc->string_buf[size_t(tape.tape_value()) + sizeof(uint32_t)]); -} -inline element object::iterator::value() const noexcept { - return element(internal::tape_ref(tape.doc, tape.json_index + 1)); -} - -/** - * Design notes: - * Instead of constructing a string_view and then comparing it with a - * user-provided strings, it is probably more performant to have dedicated - * functions taking as a parameter the string we want to compare against - * and return true when they are equal. That avoids the creation of a temporary - * std::string_view. Though it is possible for the compiler to avoid entirely - * any overhead due to string_view, relying too much on compiler magic is - * problematic: compiler magic sometimes fail, and then what do you do? - * Also, enticing users to rely on high-performance function is probably better - * on the long run. - */ - -inline bool object::iterator::key_equals(std::string_view o) const noexcept { - // We use the fact that the key length can be computed quickly - // without access to the string buffer. - const uint32_t len = key_length(); - if(o.size() == len) { - // We avoid construction of a temporary string_view instance. - return (memcmp(o.data(), key_c_str(), len) == 0); - } - return false; -} - -inline bool object::iterator::key_equals_case_insensitive(std::string_view o) const noexcept { - // We use the fact that the key length can be computed quickly - // without access to the string buffer. - const uint32_t len = key_length(); - if(o.size() == len) { - // See For case-insensitive string comparisons, avoid char-by-char functions - // https://lemire.me/blog/2020/04/30/for-case-insensitive-string-comparisons-avoid-char-by-char-functions/ - // Note that it might be worth rolling our own strncasecmp function, with vectorization. - return (simdjson_strncasecmp(o.data(), key_c_str(), len) == 0); - } - return false; -} -// -// key_value_pair inline implementation -// -inline key_value_pair::key_value_pair(std::string_view _key, element _value) noexcept : - key(_key), value(_value) {} - -} // namespace dom - -} // namespace simdjson - -#if defined(__cpp_lib_ranges) -static_assert(std::ranges::view); -static_assert(std::ranges::sized_range); -#if SIMDJSON_EXCEPTIONS -static_assert(std::ranges::view>); -static_assert(std::ranges::sized_range>); -#endif // SIMDJSON_EXCEPTIONS -#endif // defined(__cpp_lib_ranges) - -#endif // SIMDJSON_INLINE_OBJECT_H -/* end file include/simdjson/dom/object-inl.h */ -/* begin file include/simdjson/dom/parsedjson_iterator-inl.h */ -#ifndef SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H -#define SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H - -#include - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API - -namespace simdjson { - -// VS2017 reports deprecated warnings when you define a deprecated class's methods. -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_DEPRECATED_WARNING - -// Because of template weirdness, the actual class definition is inline in the document class -simdjson_warn_unused bool dom::parser::Iterator::is_ok() const { - return location < tape_length; -} - -// useful for debugging purposes -size_t dom::parser::Iterator::get_tape_location() const { - return location; -} - -// useful for debugging purposes -size_t dom::parser::Iterator::get_tape_length() const { - return tape_length; -} - -// returns the current depth (start at 1 with 0 reserved for the fictitious root -// node) -size_t dom::parser::Iterator::get_depth() const { - return depth; -} - -// A scope is a series of nodes at the same depth, typically it is either an -// object ({) or an array ([). The root node has type 'r'. -uint8_t dom::parser::Iterator::get_scope_type() const { - return depth_index[depth].scope_type; -} - -bool dom::parser::Iterator::move_forward() { - if (location + 1 >= tape_length) { - return false; // we are at the end! - } - - if ((current_type == '[') || (current_type == '{')) { - // We are entering a new scope - depth++; - assert(depth < max_depth); - depth_index[depth].start_of_scope = location; - depth_index[depth].scope_type = current_type; - } else if ((current_type == ']') || (current_type == '}')) { - // Leaving a scope. - depth--; - } else if (is_number()) { - // these types use 2 locations on the tape, not just one. - location += 1; - } - - location += 1; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - return true; -} - -void dom::parser::Iterator::move_to_value() { - // assume that we are on a key, so move by 1. - location += 1; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); -} - -bool dom::parser::Iterator::move_to_key(const char *key) { - if (down()) { - do { - const bool right_key = (strcmp(get_string(), key) == 0); - move_to_value(); - if (right_key) { - return true; - } - } while (next()); - up(); - } - return false; -} - -bool dom::parser::Iterator::move_to_key_insensitive( - const char *key) { - if (down()) { - do { - const bool right_key = (simdjson_strcasecmp(get_string(), key) == 0); - move_to_value(); - if (right_key) { - return true; - } - } while (next()); - up(); - } - return false; -} - -bool dom::parser::Iterator::move_to_key(const char *key, - uint32_t length) { - if (down()) { - do { - bool right_key = ((get_string_length() == length) && - (memcmp(get_string(), key, length) == 0)); - move_to_value(); - if (right_key) { - return true; - } - } while (next()); - up(); - } - return false; -} - -bool dom::parser::Iterator::move_to_index(uint32_t index) { - if (down()) { - uint32_t i = 0; - for (; i < index; i++) { - if (!next()) { - break; - } - } - if (i == index) { - return true; - } - up(); - } - return false; -} - -bool dom::parser::Iterator::prev() { - size_t target_location = location; - to_start_scope(); - size_t npos = location; - if (target_location == npos) { - return false; // we were already at the start - } - size_t oldnpos; - // we have that npos < target_location here - do { - oldnpos = npos; - if ((current_type == '[') || (current_type == '{')) { - // we need to jump - npos = uint32_t(current_val); - } else { - npos = npos + ((current_type == 'd' || current_type == 'l') ? 2 : 1); - } - } while (npos < target_location); - location = oldnpos; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - return true; -} - -bool dom::parser::Iterator::up() { - if (depth == 1) { - return false; // don't allow moving back to root - } - to_start_scope(); - // next we just move to the previous value - depth--; - location -= 1; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - return true; -} - -bool dom::parser::Iterator::down() { - if (location + 1 >= tape_length) { - return false; - } - if ((current_type == '[') || (current_type == '{')) { - size_t npos = uint32_t(current_val); - if (npos == location + 2) { - return false; // we have an empty scope - } - depth++; - assert(depth < max_depth); - location = location + 1; - depth_index[depth].start_of_scope = location; - depth_index[depth].scope_type = current_type; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - return true; - } - return false; -} - -void dom::parser::Iterator::to_start_scope() { - location = depth_index[depth].start_of_scope; - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); -} - -bool dom::parser::Iterator::next() { - size_t npos; - if ((current_type == '[') || (current_type == '{')) { - // we need to jump - npos = uint32_t(current_val); - } else { - npos = location + (is_number() ? 2 : 1); - } - uint64_t next_val = doc.tape[npos]; - uint8_t next_type = uint8_t(next_val >> 56); - if ((next_type == ']') || (next_type == '}')) { - return false; // we reached the end of the scope - } - location = npos; - current_val = next_val; - current_type = next_type; - return true; -} -dom::parser::Iterator::Iterator(const dom::parser &pj) noexcept(false) - : doc(pj.doc) -{ -#if SIMDJSON_EXCEPTIONS - if (!pj.valid) { throw simdjson_error(pj.error); } -#else - if (!pj.valid) { return; } // abort() usage is forbidden in the library -#endif - - max_depth = pj.max_depth(); - depth_index = new scopeindex_t[max_depth + 1]; - depth_index[0].start_of_scope = location; - current_val = doc.tape[location++]; - current_type = uint8_t(current_val >> 56); - depth_index[0].scope_type = current_type; - tape_length = size_t(current_val & internal::JSON_VALUE_MASK); - if (location < tape_length) { - // If we make it here, then depth_capacity must >=2, but the compiler - // may not know this. - current_val = doc.tape[location]; - current_type = uint8_t(current_val >> 56); - depth++; - assert(depth < max_depth); - depth_index[depth].start_of_scope = location; - depth_index[depth].scope_type = current_type; - } -} -dom::parser::Iterator::Iterator( - const dom::parser::Iterator &o) noexcept - : doc(o.doc), - max_depth(o.depth), - depth(o.depth), - location(o.location), - tape_length(o.tape_length), - current_type(o.current_type), - current_val(o.current_val) -{ - depth_index = new scopeindex_t[max_depth+1]; - std::memcpy(depth_index, o.depth_index, (depth + 1) * sizeof(depth_index[0])); -} - -dom::parser::Iterator::~Iterator() noexcept { - if (depth_index) { delete[] depth_index; } -} - -bool dom::parser::Iterator::print(std::ostream &os, bool escape_strings) const { - if (!is_ok()) { - return false; - } - switch (current_type) { - case '"': // we have a string - os << '"'; - if (escape_strings) { - os << internal::escape_json_string(std::string_view(get_string(), get_string_length())); - } else { - // was: os << get_string();, but given that we can include null chars, we - // have to do something crazier: - std::copy(get_string(), get_string() + get_string_length(), std::ostream_iterator(os)); - } - os << '"'; - break; - case 'l': // we have a long int - os << get_integer(); - break; - case 'u': - os << get_unsigned_integer(); - break; - case 'd': - os << get_double(); - break; - case 'n': // we have a null - os << "null"; - break; - case 't': // we have a true - os << "true"; - break; - case 'f': // we have a false - os << "false"; - break; - case '{': // we have an object - case '}': // we end an object - case '[': // we start an array - case ']': // we end an array - os << char(current_type); - break; - default: - return false; - } - return true; -} - -bool dom::parser::Iterator::move_to(const char *pointer, - uint32_t length) { - char *new_pointer = nullptr; - if (pointer[0] == '#') { - // Converting fragment representation to string representation - new_pointer = new char[length]; - uint32_t new_length = 0; - for (uint32_t i = 1; i < length; i++) { - if (pointer[i] == '%' && pointer[i + 1] == 'x') { -#if __cpp_exceptions - try { -#endif - int fragment = - std::stoi(std::string(&pointer[i + 2], 2), nullptr, 16); - if (fragment == '\\' || fragment == '"' || (fragment <= 0x1F)) { - // escaping the character - new_pointer[new_length] = '\\'; - new_length++; - } - new_pointer[new_length] = char(fragment); - i += 3; -#if __cpp_exceptions - } catch (std::invalid_argument &) { - delete[] new_pointer; - return false; // the fragment is invalid - } -#endif - } else { - new_pointer[new_length] = pointer[i]; - } - new_length++; - } - length = new_length; - pointer = new_pointer; - } - - // saving the current state - size_t depth_s = depth; - size_t location_s = location; - uint8_t current_type_s = current_type; - uint64_t current_val_s = current_val; - - rewind(); // The json pointer is used from the root of the document. - - bool found = relative_move_to(pointer, length); - delete[] new_pointer; - - if (!found) { - // since the pointer has found nothing, we get back to the original - // position. - depth = depth_s; - location = location_s; - current_type = current_type_s; - current_val = current_val_s; - } - - return found; -} - -bool dom::parser::Iterator::relative_move_to(const char *pointer, - uint32_t length) { - if (length == 0) { - // returns the whole document - return true; - } - - if (pointer[0] != '/') { - // '/' must be the first character - return false; - } - - // finding the key in an object or the index in an array - std::string key_or_index; - uint32_t offset = 1; - - // checking for the "-" case - if (is_array() && pointer[1] == '-') { - if (length != 2) { - // the pointer must be exactly "/-" - // there can't be anything more after '-' as an index - return false; - } - key_or_index = '-'; - offset = length; // will skip the loop coming right after - } - - // We either transform the first reference token to a valid json key - // or we make sure it is a valid index in an array. - for (; offset < length; offset++) { - if (pointer[offset] == '/') { - // beginning of the next key or index - break; - } - if (is_array() && (pointer[offset] < '0' || pointer[offset] > '9')) { - // the index of an array must be an integer - // we also make sure std::stoi won't discard whitespaces later - return false; - } - if (pointer[offset] == '~') { - // "~1" represents "/" - if (pointer[offset + 1] == '1') { - key_or_index += '/'; - offset++; - continue; - } - // "~0" represents "~" - if (pointer[offset + 1] == '0') { - key_or_index += '~'; - offset++; - continue; - } - } - if (pointer[offset] == '\\') { - if (pointer[offset + 1] == '\\' || pointer[offset + 1] == '"' || - (pointer[offset + 1] <= 0x1F)) { - key_or_index += pointer[offset + 1]; - offset++; - continue; - } - return false; // invalid escaped character - } - if (pointer[offset] == '\"') { - // unescaped quote character. this is an invalid case. - // lets do nothing and assume most pointers will be valid. - // it won't find any corresponding json key anyway. - // return false; - } - key_or_index += pointer[offset]; - } - - bool found = false; - if (is_object()) { - if (move_to_key(key_or_index.c_str(), uint32_t(key_or_index.length()))) { - found = relative_move_to(pointer + offset, length - offset); - } - } else if (is_array()) { - if (key_or_index == "-") { // handling "-" case first - if (down()) { - while (next()) - ; // moving to the end of the array - // moving to the nonexistent value right after... - size_t npos; - if ((current_type == '[') || (current_type == '{')) { - // we need to jump - npos = uint32_t(current_val); - } else { - npos = - location + ((current_type == 'd' || current_type == 'l') ? 2 : 1); - } - location = npos; - current_val = doc.tape[npos]; - current_type = uint8_t(current_val >> 56); - return true; // how could it fail ? - } - } else { // regular numeric index - // The index can't have a leading '0' - if (key_or_index[0] == '0' && key_or_index.length() > 1) { - return false; - } - // it cannot be empty - if (key_or_index.length() == 0) { - return false; - } - // we already checked the index contains only valid digits - uint32_t index = std::stoi(key_or_index); - if (move_to_index(index)) { - found = relative_move_to(pointer + offset, length - offset); - } - } - } - - return found; -} - -SIMDJSON_POP_DISABLE_WARNINGS -} // namespace simdjson - -#endif // SIMDJSON_DISABLE_DEPRECATED_API - - -#endif // SIMDJSON_INLINE_PARSEDJSON_ITERATOR_H -/* end file include/simdjson/dom/parsedjson_iterator-inl.h */ -/* begin file include/simdjson/dom/parser-inl.h */ -#ifndef SIMDJSON_INLINE_PARSER_H -#define SIMDJSON_INLINE_PARSER_H - -#include -#include - -namespace simdjson { -namespace dom { - -// -// parser inline implementation -// -simdjson_inline parser::parser(size_t max_capacity) noexcept - : _max_capacity{max_capacity}, - loaded_bytes(nullptr) { -} -simdjson_inline parser::parser(parser &&other) noexcept = default; -simdjson_inline parser &parser::operator=(parser &&other) noexcept = default; - -inline bool parser::is_valid() const noexcept { return valid; } -inline int parser::get_error_code() const noexcept { return error; } -inline std::string parser::get_error_message() const noexcept { return error_message(error); } - -inline bool parser::dump_raw_tape(std::ostream &os) const noexcept { - return valid ? doc.dump_raw_tape(os) : false; -} - -inline simdjson_result parser::read_file(const std::string &path) noexcept { - // Open the file - SIMDJSON_PUSH_DISABLE_WARNINGS - SIMDJSON_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe - std::FILE *fp = std::fopen(path.c_str(), "rb"); - SIMDJSON_POP_DISABLE_WARNINGS - - if (fp == nullptr) { - return IO_ERROR; - } - - // Get the file size - if(std::fseek(fp, 0, SEEK_END) < 0) { - std::fclose(fp); - return IO_ERROR; - } -#if defined(SIMDJSON_VISUAL_STUDIO) && !SIMDJSON_IS_32BITS - __int64 len = _ftelli64(fp); - if(len == -1L) { - std::fclose(fp); - return IO_ERROR; - } -#else - long len = std::ftell(fp); - if((len < 0) || (len == LONG_MAX)) { - std::fclose(fp); - return IO_ERROR; - } -#endif - - // Make sure we have enough capacity to load the file - if (_loaded_bytes_capacity < size_t(len)) { - loaded_bytes.reset( internal::allocate_padded_buffer(len) ); - if (!loaded_bytes) { - std::fclose(fp); - return MEMALLOC; - } - _loaded_bytes_capacity = len; - } - - // Read the string - std::rewind(fp); - size_t bytes_read = std::fread(loaded_bytes.get(), 1, len, fp); - if (std::fclose(fp) != 0 || bytes_read != size_t(len)) { - return IO_ERROR; - } - - return bytes_read; -} - -inline simdjson_result parser::load(const std::string &path) & noexcept { - size_t len; - auto _error = read_file(path).get(len); - if (_error) { return _error; } - return parse(loaded_bytes.get(), len, false); -} - -inline simdjson_result parser::load_many(const std::string &path, size_t batch_size) noexcept { - size_t len; - auto _error = read_file(path).get(len); - if (_error) { return _error; } - if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } - return document_stream(*this, reinterpret_cast(loaded_bytes.get()), len, batch_size); -} - -inline simdjson_result parser::parse_into_document(document& provided_doc, const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept { - // Important: we need to ensure that document has enough capacity. - // Important: It is possible that provided_doc is actually the internal 'doc' within the parser!!! - error_code _error = ensure_capacity(provided_doc, len); - if (_error) { return _error; } - if (realloc_if_needed) { - // Make sure we have enough capacity to copy len bytes - if (!loaded_bytes || _loaded_bytes_capacity < len) { - loaded_bytes.reset( internal::allocate_padded_buffer(len) ); - if (!loaded_bytes) { - return MEMALLOC; - } - _loaded_bytes_capacity = len; - } - std::memcpy(static_cast(loaded_bytes.get()), buf, len); - } - _error = implementation->parse(realloc_if_needed ? reinterpret_cast(loaded_bytes.get()): buf, len, provided_doc); - - if (_error) { return _error; } - - return provided_doc.root(); -} - -simdjson_inline simdjson_result parser::parse_into_document(document& provided_doc, const char *buf, size_t len, bool realloc_if_needed) & noexcept { - return parse_into_document(provided_doc, reinterpret_cast(buf), len, realloc_if_needed); -} -simdjson_inline simdjson_result parser::parse_into_document(document& provided_doc, const std::string &s) & noexcept { - return parse_into_document(provided_doc, s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING); -} -simdjson_inline simdjson_result parser::parse_into_document(document& provided_doc, const padded_string &s) & noexcept { - return parse_into_document(provided_doc, s.data(), s.length(), false); -} - - -inline simdjson_result parser::parse(const uint8_t *buf, size_t len, bool realloc_if_needed) & noexcept { - return parse_into_document(doc, buf, len, realloc_if_needed); -} - -simdjson_inline simdjson_result parser::parse(const char *buf, size_t len, bool realloc_if_needed) & noexcept { - return parse(reinterpret_cast(buf), len, realloc_if_needed); -} -simdjson_inline simdjson_result parser::parse(const std::string &s) & noexcept { - return parse(s.data(), s.length(), s.capacity() - s.length() < SIMDJSON_PADDING); -} -simdjson_inline simdjson_result parser::parse(const padded_string &s) & noexcept { - return parse(s.data(), s.length(), false); -} - -inline simdjson_result parser::parse_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept { - if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } - return document_stream(*this, buf, len, batch_size); -} -inline simdjson_result parser::parse_many(const char *buf, size_t len, size_t batch_size) noexcept { - return parse_many(reinterpret_cast(buf), len, batch_size); -} -inline simdjson_result parser::parse_many(const std::string &s, size_t batch_size) noexcept { - return parse_many(s.data(), s.length(), batch_size); -} -inline simdjson_result parser::parse_many(const padded_string &s, size_t batch_size) noexcept { - return parse_many(s.data(), s.length(), batch_size); -} - -simdjson_inline size_t parser::capacity() const noexcept { - return implementation ? implementation->capacity() : 0; -} -simdjson_inline size_t parser::max_capacity() const noexcept { - return _max_capacity; -} -simdjson_inline size_t parser::max_depth() const noexcept { - return implementation ? implementation->max_depth() : DEFAULT_MAX_DEPTH; -} - -simdjson_warn_unused -inline error_code parser::allocate(size_t capacity, size_t max_depth) noexcept { - // - // Reallocate implementation if needed - // - error_code err; - if (implementation) { - err = implementation->allocate(capacity, max_depth); - } else { - err = simdjson::get_active_implementation()->create_dom_parser_implementation(capacity, max_depth, implementation); - } - if (err) { return err; } - return SUCCESS; -} - -#ifndef SIMDJSON_DISABLE_DEPRECATED_API -simdjson_warn_unused -inline bool parser::allocate_capacity(size_t capacity, size_t max_depth) noexcept { - return !allocate(capacity, max_depth); -} -#endif // SIMDJSON_DISABLE_DEPRECATED_API - -inline error_code parser::ensure_capacity(size_t desired_capacity) noexcept { - return ensure_capacity(doc, desired_capacity); -} - - -inline error_code parser::ensure_capacity(document& target_document, size_t desired_capacity) noexcept { - // 1. It is wasteful to allocate a document and a parser for documents spanning less than MINIMAL_DOCUMENT_CAPACITY bytes. - // 2. If we allow desired_capacity = 0 then it is possible to exit this function with implementation == nullptr. - if(desired_capacity < MINIMAL_DOCUMENT_CAPACITY) { desired_capacity = MINIMAL_DOCUMENT_CAPACITY; } - // If we don't have enough capacity, (try to) automatically bump it. - // If the document needs allocation, do it too. - // Both in one if statement to minimize unlikely branching. - // - // Note: we must make sure that this function is called if capacity() == 0. We do so because we - // ensure that desired_capacity > 0. - if (simdjson_unlikely(capacity() < desired_capacity || target_document.capacity() < desired_capacity)) { - if (desired_capacity > max_capacity()) { - return error = CAPACITY; - } - error_code err1 = target_document.capacity() < desired_capacity ? target_document.allocate(desired_capacity) : SUCCESS; - error_code err2 = capacity() < desired_capacity ? allocate(desired_capacity, max_depth()) : SUCCESS; - if(err1 != SUCCESS) { return error = err1; } - if(err2 != SUCCESS) { return error = err2; } - } - return SUCCESS; -} - -simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept { - if(max_capacity < MINIMAL_DOCUMENT_CAPACITY) { - _max_capacity = max_capacity; - } else { - _max_capacity = MINIMAL_DOCUMENT_CAPACITY; - } -} - -} // namespace dom -} // namespace simdjson - -#endif // SIMDJSON_INLINE_PARSER_H -/* end file include/simdjson/dom/parser-inl.h */ -/* begin file include/simdjson/internal/tape_ref-inl.h */ -#ifndef SIMDJSON_INLINE_TAPE_REF_H -#define SIMDJSON_INLINE_TAPE_REF_H - -#include - -namespace simdjson { -namespace internal { - -// -// tape_ref inline implementation -// -simdjson_inline tape_ref::tape_ref() noexcept : doc{nullptr}, json_index{0} {} -simdjson_inline tape_ref::tape_ref(const dom::document *_doc, size_t _json_index) noexcept : doc{_doc}, json_index{_json_index} {} - - -simdjson_inline bool tape_ref::is_document_root() const noexcept { - return json_index == 1; // should we ever change the structure of the tape, this should get updated. -} - -// Some value types have a specific on-tape word value. It can be faster -// to check the type by doing a word-to-word comparison instead of extracting the -// most significant 8 bits. - -simdjson_inline bool tape_ref::is_double() const noexcept { - constexpr uint64_t tape_double = uint64_t(tape_type::DOUBLE)<<56; - return doc->tape[json_index] == tape_double; -} -simdjson_inline bool tape_ref::is_int64() const noexcept { - constexpr uint64_t tape_int64 = uint64_t(tape_type::INT64)<<56; - return doc->tape[json_index] == tape_int64; -} -simdjson_inline bool tape_ref::is_uint64() const noexcept { - constexpr uint64_t tape_uint64 = uint64_t(tape_type::UINT64)<<56; - return doc->tape[json_index] == tape_uint64; -} -simdjson_inline bool tape_ref::is_false() const noexcept { - constexpr uint64_t tape_false = uint64_t(tape_type::FALSE_VALUE)<<56; - return doc->tape[json_index] == tape_false; -} -simdjson_inline bool tape_ref::is_true() const noexcept { - constexpr uint64_t tape_true = uint64_t(tape_type::TRUE_VALUE)<<56; - return doc->tape[json_index] == tape_true; -} -simdjson_inline bool tape_ref::is_null_on_tape() const noexcept { - constexpr uint64_t tape_null = uint64_t(tape_type::NULL_VALUE)<<56; - return doc->tape[json_index] == tape_null; -} - -inline size_t tape_ref::after_element() const noexcept { - switch (tape_ref_type()) { - case tape_type::START_ARRAY: - case tape_type::START_OBJECT: - return matching_brace_index(); - case tape_type::UINT64: - case tape_type::INT64: - case tape_type::DOUBLE: - return json_index + 2; - default: - return json_index + 1; - } -} -simdjson_inline tape_type tape_ref::tape_ref_type() const noexcept { - return static_cast(doc->tape[json_index] >> 56); -} -simdjson_inline uint64_t internal::tape_ref::tape_value() const noexcept { - return doc->tape[json_index] & internal::JSON_VALUE_MASK; -} -simdjson_inline uint32_t internal::tape_ref::matching_brace_index() const noexcept { - return uint32_t(doc->tape[json_index]); -} -simdjson_inline uint32_t internal::tape_ref::scope_count() const noexcept { - return uint32_t((doc->tape[json_index] >> 32) & internal::JSON_COUNT_MASK); -} - -template -simdjson_inline T tape_ref::next_tape_value() const noexcept { - static_assert(sizeof(T) == sizeof(uint64_t), "next_tape_value() template parameter must be 64-bit"); - // Though the following is tempting... - // return *reinterpret_cast(&doc->tape[json_index + 1]); - // It is not generally safe. It is safer, and often faster to rely - // on memcpy. Yes, it is uglier, but it is also encapsulated. - T x; - std::memcpy(&x,&doc->tape[json_index + 1],sizeof(uint64_t)); - return x; -} - -simdjson_inline uint32_t internal::tape_ref::get_string_length() const noexcept { - size_t string_buf_index = size_t(tape_value()); - uint32_t len; - std::memcpy(&len, &doc->string_buf[string_buf_index], sizeof(len)); - return len; -} - -simdjson_inline const char * internal::tape_ref::get_c_str() const noexcept { - size_t string_buf_index = size_t(tape_value()); - return reinterpret_cast(&doc->string_buf[string_buf_index + sizeof(uint32_t)]); -} - -inline std::string_view internal::tape_ref::get_string_view() const noexcept { - return std::string_view( - get_c_str(), - get_string_length() - ); -} - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INLINE_TAPE_REF_H -/* end file include/simdjson/internal/tape_ref-inl.h */ -/* begin file include/simdjson/dom/serialization-inl.h */ - -#ifndef SIMDJSON_SERIALIZATION_INL_H -#define SIMDJSON_SERIALIZATION_INL_H - - -#include -#include - -namespace simdjson { -namespace dom { -inline bool parser::print_json(std::ostream &os) const noexcept { - if (!valid) { return false; } - simdjson::internal::string_builder<> sb; - sb.append(doc.root()); - std::string_view answer = sb.str(); - os << answer; - return true; -} -} -/*** - * Number utility functions - **/ - - -namespace { -/**@private - * Escape sequence like \b or \u0001 - * We expect that most compilers will use 8 bytes for this data structure. - **/ -struct escape_sequence { - uint8_t length; - const char string[7]; // technically, we only ever need 6 characters, we pad to 8 -}; -/**@private - * This converts a signed integer into a character sequence. - * The caller is responsible for providing enough memory (at least - * 20 characters.) - * Though various runtime libraries provide itoa functions, - * it is not part of the C++ standard. The C++17 standard - * adds the to_chars functions which would do as well, but - * we want to support C++11. - */ -char *fast_itoa(char *output, int64_t value) noexcept { - // This is a standard implementation of itoa. - char buffer[20]; - uint64_t value_positive; - // In general, negating a signed integer is unsafe. - if(value < 0) { - *output++ = '-'; - // Doing value_positive = -value; while avoiding - // undefined behavior warnings. - // It assumes two complement's which is universal at this - // point in time. - std::memcpy(&value_positive, &value, sizeof(value)); - value_positive = (~value_positive) + 1; // this is a negation - } else { - value_positive = value; - } - // We work solely with value_positive. It *might* be easier - // for an optimizing compiler to deal with an unsigned variable - // as far as performance goes. - const char *const end_buffer = buffer + 20; - char *write_pointer = buffer + 19; - // A faster approach is possible if we expect large integers: - // unroll the loop (work in 100s, 1000s) and use some kind of - // memoization. - while(value_positive >= 10) { - *write_pointer-- = char('0' + (value_positive % 10)); - value_positive /= 10; - } - *write_pointer = char('0' + value_positive); - size_t len = end_buffer - write_pointer; - std::memcpy(output, write_pointer, len); - return output + len; -} -/**@private - * This converts an unsigned integer into a character sequence. - * The caller is responsible for providing enough memory (at least - * 19 characters.) - * Though various runtime libraries provide itoa functions, - * it is not part of the C++ standard. The C++17 standard - * adds the to_chars functions which would do as well, but - * we want to support C++11. - */ -char *fast_itoa(char *output, uint64_t value) noexcept { - // This is a standard implementation of itoa. - char buffer[20]; - const char *const end_buffer = buffer + 20; - char *write_pointer = buffer + 19; - // A faster approach is possible if we expect large integers: - // unroll the loop (work in 100s, 1000s) and use some kind of - // memoization. - while(value >= 10) { - *write_pointer-- = char('0' + (value % 10)); - value /= 10; - }; - *write_pointer = char('0' + value); - size_t len = end_buffer - write_pointer; - std::memcpy(output, write_pointer, len); - return output + len; -} -} // anonymous namespace -namespace internal { - -/*** - * Minifier/formatter code. - **/ - -simdjson_inline void mini_formatter::number(uint64_t x) { - char number_buffer[24]; - char *newp = fast_itoa(number_buffer, x); - buffer.insert(buffer.end(), number_buffer, newp); -} - -simdjson_inline void mini_formatter::number(int64_t x) { - char number_buffer[24]; - char *newp = fast_itoa(number_buffer, x); - buffer.insert(buffer.end(), number_buffer, newp); -} - -simdjson_inline void mini_formatter::number(double x) { - char number_buffer[24]; - // Currently, passing the nullptr to the second argument is - // safe because our implementation does not check the second - // argument. - char *newp = internal::to_chars(number_buffer, nullptr, x); - buffer.insert(buffer.end(), number_buffer, newp); -} - -simdjson_inline void mini_formatter::start_array() { one_char('['); } -simdjson_inline void mini_formatter::end_array() { one_char(']'); } -simdjson_inline void mini_formatter::start_object() { one_char('{'); } -simdjson_inline void mini_formatter::end_object() { one_char('}'); } -simdjson_inline void mini_formatter::comma() { one_char(','); } - - -simdjson_inline void mini_formatter::true_atom() { - const char * s = "true"; - buffer.insert(buffer.end(), s, s + 4); -} -simdjson_inline void mini_formatter::false_atom() { - const char * s = "false"; - buffer.insert(buffer.end(), s, s + 5); -} -simdjson_inline void mini_formatter::null_atom() { - const char * s = "null"; - buffer.insert(buffer.end(), s, s + 4); -} -simdjson_inline void mini_formatter::one_char(char c) { buffer.push_back(c); } -simdjson_inline void mini_formatter::key(std::string_view unescaped) { - string(unescaped); - one_char(':'); -} -simdjson_inline void mini_formatter::string(std::string_view unescaped) { - one_char('\"'); - size_t i = 0; - // Fast path for the case where we have no control character, no ", and no backslash. - // This should include most keys. - // - // We would like to use 'bool' but some compilers take offense to bitwise operation - // with bool types. - constexpr static char needs_escaping[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - for(;i + 8 <= unescaped.length(); i += 8) { - // Poor's man vectorization. This could get much faster if we used SIMD. - // - // It is not the case that replacing '|' with '||' would be neutral performance-wise. - if(needs_escaping[uint8_t(unescaped[i])] | needs_escaping[uint8_t(unescaped[i+1])] - | needs_escaping[uint8_t(unescaped[i+2])] | needs_escaping[uint8_t(unescaped[i+3])] - | needs_escaping[uint8_t(unescaped[i+4])] | needs_escaping[uint8_t(unescaped[i+5])] - | needs_escaping[uint8_t(unescaped[i+6])] | needs_escaping[uint8_t(unescaped[i+7])] - ) { break; } - } - for(;i < unescaped.length(); i++) { - if(needs_escaping[uint8_t(unescaped[i])]) { break; } - } - // The following is also possible and omits a 256-byte table, but it is slower: - // for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F) - // && (unescaped[i] != '\"') && (unescaped[i] != '\\'); i++) {} - - // At least for long strings, the following should be fast. We could - // do better by integrating the checks and the insertion. - buffer.insert(buffer.end(), unescaped.data(), unescaped.data() + i); - // We caught a control character if we enter this loop (slow). - // Note that we are do not restart from the beginning, but rather we continue - // from the point where we encountered something that requires escaping. - for (; i < unescaped.length(); i++) { - switch (unescaped[i]) { - case '\"': - { - const char * s = "\\\""; - buffer.insert(buffer.end(), s, s + 2); - } - break; - case '\\': - { - const char * s = "\\\\"; - buffer.insert(buffer.end(), s, s + 2); - } - break; - default: - if (uint8_t(unescaped[i]) <= 0x1F) { - // If packed, this uses 8 * 32 bytes. - // Note that we expect most compilers to embed this code in the data - // section. - constexpr static escape_sequence escaped[32] = { - {6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"}, - {6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"}, - {2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"}, - {2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"}, - {6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"}, - {6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"}, - {6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"}, - {6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}}; - auto u = escaped[uint8_t(unescaped[i])]; - buffer.insert(buffer.end(), u.string, u.string + u.length); - } else { - one_char(unescaped[i]); - } - } // switch - } // for - one_char('\"'); -} - -inline void mini_formatter::clear() { - buffer.clear(); -} - -simdjson_inline std::string_view mini_formatter::str() const { - return std::string_view(buffer.data(), buffer.size()); -} - - -/*** - * String building code. - **/ - -template -inline void string_builder::append(simdjson::dom::element value) { - // using tape_type = simdjson::internal::tape_type; - size_t depth = 0; - constexpr size_t MAX_DEPTH = 16; - bool is_object[MAX_DEPTH]; - is_object[0] = false; - bool after_value = false; - - internal::tape_ref iter(value.tape); - do { - // print commas after each value - if (after_value) { - format.comma(); - } - // If we are in an object, print the next key and :, and skip to the next - // value. - if (is_object[depth]) { - format.key(iter.get_string_view()); - iter.json_index++; - } - switch (iter.tape_ref_type()) { - - // Arrays - case tape_type::START_ARRAY: { - // If we're too deep, we need to recurse to go deeper. - depth++; - if (simdjson_unlikely(depth >= MAX_DEPTH)) { - append(simdjson::dom::array(iter)); - iter.json_index = iter.matching_brace_index() - 1; // Jump to the ] - depth--; - break; - } - - // Output start [ - format.start_array(); - iter.json_index++; - - // Handle empty [] (we don't want to come back around and print commas) - if (iter.tape_ref_type() == tape_type::END_ARRAY) { - format.end_array(); - depth--; - break; - } - - is_object[depth] = false; - after_value = false; - continue; - } - - // Objects - case tape_type::START_OBJECT: { - // If we're too deep, we need to recurse to go deeper. - depth++; - if (simdjson_unlikely(depth >= MAX_DEPTH)) { - append(simdjson::dom::object(iter)); - iter.json_index = iter.matching_brace_index() - 1; // Jump to the } - depth--; - break; - } - - // Output start { - format.start_object(); - iter.json_index++; - - // Handle empty {} (we don't want to come back around and print commas) - if (iter.tape_ref_type() == tape_type::END_OBJECT) { - format.end_object(); - depth--; - break; - } - - is_object[depth] = true; - after_value = false; - continue; - } - - // Scalars - case tape_type::STRING: - format.string(iter.get_string_view()); - break; - case tape_type::INT64: - format.number(iter.next_tape_value()); - iter.json_index++; // numbers take up 2 spots, so we need to increment - // extra - break; - case tape_type::UINT64: - format.number(iter.next_tape_value()); - iter.json_index++; // numbers take up 2 spots, so we need to increment - // extra - break; - case tape_type::DOUBLE: - format.number(iter.next_tape_value()); - iter.json_index++; // numbers take up 2 spots, so we need to increment - // extra - break; - case tape_type::TRUE_VALUE: - format.true_atom(); - break; - case tape_type::FALSE_VALUE: - format.false_atom(); - break; - case tape_type::NULL_VALUE: - format.null_atom(); - break; - - // These are impossible - case tape_type::END_ARRAY: - case tape_type::END_OBJECT: - case tape_type::ROOT: - SIMDJSON_UNREACHABLE(); - } - iter.json_index++; - after_value = true; - - // Handle multiple ends in a row - while (depth != 0 && (iter.tape_ref_type() == tape_type::END_ARRAY || - iter.tape_ref_type() == tape_type::END_OBJECT)) { - if (iter.tape_ref_type() == tape_type::END_ARRAY) { - format.end_array(); - } else { - format.end_object(); - } - depth--; - iter.json_index++; - } - - // Stop when we're at depth 0 - } while (depth != 0); -} - -template -inline void string_builder::append(simdjson::dom::object value) { - format.start_object(); - auto pair = value.begin(); - auto end = value.end(); - if (pair != end) { - append(*pair); - for (++pair; pair != end; ++pair) { - format.comma(); - append(*pair); - } - } - format.end_object(); -} - -template -inline void string_builder::append(simdjson::dom::array value) { - format.start_array(); - auto iter = value.begin(); - auto end = value.end(); - if (iter != end) { - append(*iter); - for (++iter; iter != end; ++iter) { - format.comma(); - append(*iter); - } - } - format.end_array(); -} - -template -simdjson_inline void string_builder::append(simdjson::dom::key_value_pair kv) { - format.key(kv.key); - append(kv.value); -} - -template -simdjson_inline void string_builder::clear() { - format.clear(); -} - -template -simdjson_inline std::string_view string_builder::str() const { - return format.str(); -} - - -} // namespace internal -} // namespace simdjson - -#endif -/* end file include/simdjson/dom/serialization-inl.h */ - -SIMDJSON_POP_DISABLE_WARNINGS - -#endif // SIMDJSON_DOM_H -/* end file include/simdjson/dom.h */ -/* begin file include/simdjson/builtin.h */ -#ifndef SIMDJSON_BUILTIN_H -#define SIMDJSON_BUILTIN_H - -/* begin file include/simdjson/implementations.h */ -#ifndef SIMDJSON_IMPLEMENTATIONS_H -#define SIMDJSON_IMPLEMENTATIONS_H - -/* begin file include/simdjson/implementation-base.h */ -#ifndef SIMDJSON_IMPLEMENTATION_BASE_H -#define SIMDJSON_IMPLEMENTATION_BASE_H - -/** - * @file - * - * Includes common stuff needed for implementations. - */ - - -// Implementation-internal files (must be included before the implementations themselves, to keep -// amalgamation working--otherwise, the first time a file is included, it might be put inside the -// #ifdef SIMDJSON_IMPLEMENTATION_ARM64/FALLBACK/etc., which means the other implementations can't -// compile unless that implementation is turned on). -/* begin file include/simdjson/internal/jsoncharutils_tables.h */ -#ifndef SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H -#define SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H - - -#ifdef JSON_TEST_STRINGS -void found_string(const uint8_t *buf, const uint8_t *parsed_begin, - const uint8_t *parsed_end); -void found_bad_string(const uint8_t *buf); -#endif - -namespace simdjson { -namespace internal { -// structural chars here are -// they are { 0x7b } 0x7d : 0x3a [ 0x5b ] 0x5d , 0x2c (and NULL) -// we are also interested in the four whitespace characters -// space 0x20, linefeed 0x0a, horizontal tab 0x09 and carriage return 0x0d - -extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace_negated[256]; -extern SIMDJSON_DLLIMPORTEXPORT const bool structural_or_whitespace[256]; -extern SIMDJSON_DLLIMPORTEXPORT const uint32_t digit_to_val32[886]; - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_JSONCHARUTILS_TABLES_H -/* end file include/simdjson/internal/jsoncharutils_tables.h */ -/* begin file include/simdjson/internal/numberparsing_tables.h */ -#ifndef SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H -#define SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H - - -namespace simdjson { -namespace internal { -/** - * The smallest non-zero float (binary64) is 2^-1074. - * We take as input numbers of the form w x 10^q where w < 2^64. - * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. - * However, we have that - * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074. - * Thus it is possible for a number of the form w * 10^-342 where - * w is a 64-bit value to be a non-zero floating-point number. - ********* - * Any number of form w * 10^309 where w>= 1 is going to be - * infinite in binary64 so we never need to worry about powers - * of 5 greater than 308. - */ -constexpr int smallest_power = -342; -constexpr int largest_power = 308; - -/** - * Represents a 128-bit value. - * low: least significant 64 bits. - * high: most significant 64 bits. - */ -struct value128 { - uint64_t low; - uint64_t high; -}; - - -// Precomputed powers of ten from 10^0 to 10^22. These -// can be represented exactly using the double type. -extern SIMDJSON_DLLIMPORTEXPORT const double power_of_ten[]; - - -/** - * When mapping numbers from decimal to binary, - * we go from w * 10^q to m * 2^p but we have - * 10^q = 5^q * 2^q, so effectively - * we are trying to match - * w * 2^q * 5^q to m * 2^p. Thus the powers of two - * are not a concern since they can be represented - * exactly using the binary notation, only the powers of five - * affect the binary significand. - */ - - -// The truncated powers of five from 5^-342 all the way to 5^308 -// The mantissa is truncated to 128 bits, and -// never rounded up. Uses about 10KB. -extern SIMDJSON_DLLIMPORTEXPORT const uint64_t power_of_five_128[]; -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_NUMBERPARSING_TABLES_H -/* end file include/simdjson/internal/numberparsing_tables.h */ -/* begin file include/simdjson/internal/simdprune_tables.h */ -#ifndef SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H -#define SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H - -#include - -namespace simdjson { // table modified and copied from -namespace internal { // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable - -extern SIMDJSON_DLLIMPORTEXPORT const unsigned char BitsSetTable256mul2[256]; - -extern SIMDJSON_DLLIMPORTEXPORT const uint8_t pshufb_combine_table[272]; - -// 256 * 8 bytes = 2kB, easily fits in cache. -extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; - -} // namespace internal -} // namespace simdjson - -#endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H -/* end file include/simdjson/internal/simdprune_tables.h */ - -#endif // SIMDJSON_IMPLEMENTATION_BASE_H -/* end file include/simdjson/implementation-base.h */ - -// -// First, figure out which implementations can be run. Doing it here makes it so we don't have to worry about the order -// in which we include them. -// - -#ifndef SIMDJSON_IMPLEMENTATION_ARM64 -#define SIMDJSON_IMPLEMENTATION_ARM64 (SIMDJSON_IS_ARM64) -#endif -#define SIMDJSON_CAN_ALWAYS_RUN_ARM64 SIMDJSON_IMPLEMENTATION_ARM64 && SIMDJSON_IS_ARM64 - -#ifdef __has_include -// How do we detect that a compiler supports vbmi2? -// For sure if the following header is found, we are ok? -#if __has_include() -#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 -#endif -#endif - -#ifdef _MSC_VER -#if _MSC_VER >= 1920 -// Visual Studio 2019 and up support VBMI2 under x64 even if the header -// avx512vbmi2intrin.h is not found. -#define SIMDJSON_COMPILER_SUPPORTS_VBMI2 1 -#endif -#endif - -// By default, we allow AVX512. -#ifndef SIMDJSON_AVX512_ALLOWED -#define SIMDJSON_AVX512_ALLOWED 1 -#endif - -// Default Icelake to on if this is x86-64. Even if we're not compiled for it, it could be selected -// at runtime. -#ifndef SIMDJSON_IMPLEMENTATION_ICELAKE -#define SIMDJSON_IMPLEMENTATION_ICELAKE ((SIMDJSON_IS_X86_64) && (SIMDJSON_AVX512_ALLOWED) && (SIMDJSON_COMPILER_SUPPORTS_VBMI2)) -#endif - -#ifdef _MSC_VER -// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see -// https://github.com/simdjson/simdjson/issues/1247 -#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) -#else -#define SIMDJSON_CAN_ALWAYS_RUN_ICELAKE ((SIMDJSON_IMPLEMENTATION_ICELAKE) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__) && (__AVX512F__) && (__AVX512DQ__) && (__AVX512CD__) && (__AVX512BW__) && (__AVX512VL__) && (__AVX512VBMI2__)) -#endif - -// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected -// at runtime. -#ifndef SIMDJSON_IMPLEMENTATION_HASWELL -#define SIMDJSON_IMPLEMENTATION_HASWELL SIMDJSON_IS_X86_64 -#endif -#ifdef _MSC_VER -// To see why (__BMI__) && (__PCLMUL__) && (__LZCNT__) are not part of this next line, see -// https://github.com/simdjson/simdjson/issues/1247 -#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__)) -#else -#define SIMDJSON_CAN_ALWAYS_RUN_HASWELL ((SIMDJSON_IMPLEMENTATION_HASWELL) && (SIMDJSON_IS_X86_64) && (__AVX2__) && (__BMI__) && (__PCLMUL__) && (__LZCNT__)) -#endif - -// Default Westmere to on if this is x86-64. Note that the macro SIMDJSON_REQUIRES_HASWELL appears unused. -#ifndef SIMDJSON_IMPLEMENTATION_WESTMERE -#define SIMDJSON_IMPLEMENTATION_WESTMERE (SIMDJSON_IS_X86_64 && !SIMDJSON_REQUIRES_HASWELL) -#endif -#define SIMDJSON_CAN_ALWAYS_RUN_WESTMERE (SIMDJSON_IMPLEMENTATION_WESTMERE && SIMDJSON_IS_X86_64 && __SSE4_2__ && __PCLMUL__) - -#ifndef SIMDJSON_IMPLEMENTATION_PPC64 -#define SIMDJSON_IMPLEMENTATION_PPC64 (SIMDJSON_IS_PPC64) -#endif -#define SIMDJSON_CAN_ALWAYS_RUN_PPC64 SIMDJSON_IMPLEMENTATION_PPC64 && SIMDJSON_IS_PPC64 - -// Default Fallback to on unless a builtin implementation has already been selected. -#ifndef SIMDJSON_IMPLEMENTATION_FALLBACK -#define SIMDJSON_IMPLEMENTATION_FALLBACK 1 // (!SIMDJSON_CAN_ALWAYS_RUN_ARM64 && !SIMDJSON_CAN_ALWAYS_RUN_HASWELL && !SIMDJSON_CAN_ALWAYS_RUN_WESTMERE && !SIMDJSON_CAN_ALWAYS_RUN_PPC64) -#endif -#define SIMDJSON_CAN_ALWAYS_RUN_FALLBACK SIMDJSON_IMPLEMENTATION_FALLBACK - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_UNDESIRED_WARNINGS - -// Implementations -/* begin file include/simdjson/arm64.h */ -#ifndef SIMDJSON_ARM64_H -#define SIMDJSON_ARM64_H - - -#if SIMDJSON_IMPLEMENTATION_ARM64 - -namespace simdjson { -/** - * Implementation for NEON (ARMv8). - */ -namespace arm64 { -} // namespace arm64 -} // namespace simdjson - -/* begin file include/simdjson/arm64/implementation.h */ -#ifndef SIMDJSON_ARM64_IMPLEMENTATION_H -#define SIMDJSON_ARM64_IMPLEMENTATION_H - - -namespace simdjson { -namespace arm64 { - -namespace { -using namespace simdjson; -using namespace simdjson::dom; -} - -class implementation final : public simdjson::implementation { -public: - simdjson_inline implementation() : simdjson::implementation("arm64", "ARM NEON", internal::instruction_set::NEON) {} - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t capacity, - size_t max_length, - std::unique_ptr& dst - ) const noexcept final; - simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; - simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; -}; - -} // namespace arm64 -} // namespace simdjson - -#endif // SIMDJSON_ARM64_IMPLEMENTATION_H -/* end file include/simdjson/arm64/implementation.h */ - -/* begin file include/simdjson/arm64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "arm64" -// #define SIMDJSON_IMPLEMENTATION arm64 -/* end file include/simdjson/arm64/begin.h */ - -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ - -namespace simdjson { -namespace arm64 { - -// expectation: sizeof(open_container) = 64/8. -struct open_container { - uint32_t tape_index; // where, on the tape, does the scope ([,{) begins - uint32_t count; // how many elements in the scope -}; // struct open_container - -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); - -class dom_parser_implementation final : public internal::dom_parser_implementation { -public: - /** Tape location of each open { or [ */ - std::unique_ptr open_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr is_array{}; - /** Buffer passed to stage 1 */ - const uint8_t *buf{}; - /** Length passed to stage 1 */ - size_t len{0}; - /** Document passed to stage 2 */ - dom::document *doc{}; - - inline dom_parser_implementation() noexcept; - inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; - inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; - dom_parser_implementation(const dom_parser_implementation &) = delete; - dom_parser_implementation &operator=(const dom_parser_implementation &) = delete; - - simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final; - simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final; - simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept final; - inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final; - inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final; -private: - simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity); - -}; - -} // namespace arm64 -} // namespace simdjson - -namespace simdjson { -namespace arm64 { - -inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -// Leaving these here so they can be inlined if so desired -inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { - if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] ); - if (!structural_indexes) { _capacity = 0; return MEMALLOC; } - structural_indexes[0] = 0; - n_structural_indexes = 0; - - _capacity = capacity; - return SUCCESS; -} - -inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // Stage 2 stacks - open_containers.reset(new (std::nothrow) open_container[max_depth]); - is_array.reset(new (std::nothrow) bool[max_depth]); - if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; } - - _max_depth = max_depth; - return SUCCESS; -} - -} // namespace arm64 -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/arm64/intrinsics.h */ -#ifndef SIMDJSON_ARM64_INTRINSICS_H -#define SIMDJSON_ARM64_INTRINSICS_H - -// This should be the correct header whether -// you use visual studio or other compilers. -#include - -static_assert(sizeof(uint8x16_t) <= simdjson::SIMDJSON_PADDING, "insufficient padding for arm64"); - -#endif // SIMDJSON_ARM64_INTRINSICS_H -/* end file include/simdjson/arm64/intrinsics.h */ -/* begin file include/simdjson/arm64/bitmanipulation.h */ -#ifndef SIMDJSON_ARM64_BITMANIPULATION_H -#define SIMDJSON_ARM64_BITMANIPULATION_H - -namespace simdjson { -namespace arm64 { -namespace { - -// We sometimes call trailing_zero on inputs that are zero, -// but the algorithms do not end up using the returned value. -// Sadly, sanitizers are not smart enough to figure it out. -SIMDJSON_NO_SANITIZE_UNDEFINED -simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - unsigned long ret; - // Search the mask data from least significant bit (LSB) - // to the most significant bit (MSB) for a set bit (1). - _BitScanForward64(&ret, input_num); - return (int)ret; -#else // SIMDJSON_REGULAR_VISUAL_STUDIO - return __builtin_ctzll(input_num); -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO -} - -/* result might be undefined when input_num is zero */ -simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { - return input_num & (input_num-1); -} - -/* result might be undefined when input_num is zero */ -simdjson_inline int leading_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - unsigned long leading_zero = 0; - // Search the mask data from most significant bit (MSB) - // to least significant bit (LSB) for a set bit (1). - if (_BitScanReverse64(&leading_zero, input_num)) - return (int)(63 - leading_zero); - else - return 64; -#else - return __builtin_clzll(input_num); -#endif// SIMDJSON_REGULAR_VISUAL_STUDIO -} - -/* result might be undefined when input_num is zero */ -simdjson_inline int count_ones(uint64_t input_num) { - return vaddv_u8(vcnt_u8(vcreate_u8(input_num))); -} - - -#if defined(__GNUC__) // catches clang and gcc -/** - * ARM has a fast 64-bit "bit reversal function" that is handy. However, - * it is not generally available as an intrinsic function under Visual - * Studio (though this might be changing). Even under clang/gcc, we - * apparently need to invoke inline assembly. - */ -/* - * We use SIMDJSON_PREFER_REVERSE_BITS as a hint that algorithms that - * work well with bit reversal may use it. - */ -#define SIMDJSON_PREFER_REVERSE_BITS 1 - -/* reverse the bits */ -simdjson_inline uint64_t reverse_bits(uint64_t input_num) { - uint64_t rev_bits; - __asm("rbit %0, %1" : "=r"(rev_bits) : "r"(input_num)); - return rev_bits; -} - -/** - * Flips bit at index 63 - lz. Thus if you have 'leading_zeroes' leading zeroes, - * then this will set to zero the leading bit. It is possible for leading_zeroes to be - * greating or equal to 63 in which case we trigger undefined behavior, but the output - * of such undefined behavior is never used. - **/ -SIMDJSON_NO_SANITIZE_UNDEFINED -simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) { - return rev_bits ^ (uint64_t(0x8000000000000000) >> leading_zeroes); -} - -#endif - -simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - *result = value1 + value2; - return *result < value1; -#else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); -#endif -} - -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson - -#endif // SIMDJSON_ARM64_BITMANIPULATION_H -/* end file include/simdjson/arm64/bitmanipulation.h */ -/* begin file include/simdjson/arm64/bitmask.h */ -#ifndef SIMDJSON_ARM64_BITMASK_H -#define SIMDJSON_ARM64_BITMASK_H - -namespace simdjson { -namespace arm64 { -namespace { - -// -// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered. -// -// For example, prefix_xor(00100100) == 00011100 -// -simdjson_inline uint64_t prefix_xor(uint64_t bitmask) { - ///////////// - // We could do this with PMULL, but it is apparently slow. - // - //#ifdef __ARM_FEATURE_CRYPTO // some ARM processors lack this extension - //return vmull_p64(-1ULL, bitmask); - //#else - // Analysis by @sebpop: - // When diffing the assembly for src/stage1_find_marks.cpp I see that the eors are all spread out - // in between other vector code, so effectively the extra cycles of the sequence do not matter - // because the GPR units are idle otherwise and the critical path is on the FP side. - // Also the PMULL requires two extra fmovs: GPR->FP (3 cycles in N1, 5 cycles in A72 ) - // and FP->GPR (2 cycles on N1 and 5 cycles on A72.) - /////////// - bitmask ^= bitmask << 1; - bitmask ^= bitmask << 2; - bitmask ^= bitmask << 4; - bitmask ^= bitmask << 8; - bitmask ^= bitmask << 16; - bitmask ^= bitmask << 32; - return bitmask; -} - -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson - -#endif -/* end file include/simdjson/arm64/bitmask.h */ -/* begin file include/simdjson/arm64/simd.h */ -#ifndef SIMDJSON_ARM64_SIMD_H -#define SIMDJSON_ARM64_SIMD_H - -#include - - -namespace simdjson { -namespace arm64 { -namespace { -namespace simd { - -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO -namespace { -// Start of private section with Visual Studio workaround - - -/** - * make_uint8x16_t initializes a SIMD register (uint8x16_t). - * This is needed because, incredibly, the syntax uint8x16_t x = {1,2,3...} - * is not recognized under Visual Studio! This is a workaround. - * Using a std::initializer_list as a parameter resulted in - * inefficient code. With the current approach, if the parameters are - * compile-time constants, - * GNU GCC compiles it to ldr, the same as uint8x16_t x = {1,2,3...}. - * You should not use this function except for compile-time constants: - * it is not efficient. - */ -simdjson_inline uint8x16_t make_uint8x16_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, - uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8, - uint8_t x9, uint8_t x10, uint8_t x11, uint8_t x12, - uint8_t x13, uint8_t x14, uint8_t x15, uint8_t x16) { - // Doing a load like so end ups generating worse code. - // uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, - // x9, x10,x11,x12,x13,x14,x15,x16}; - // return vld1q_u8(array); - uint8x16_t x{}; - // incredibly, Visual Studio does not allow x[0] = x1 - x = vsetq_lane_u8(x1, x, 0); - x = vsetq_lane_u8(x2, x, 1); - x = vsetq_lane_u8(x3, x, 2); - x = vsetq_lane_u8(x4, x, 3); - x = vsetq_lane_u8(x5, x, 4); - x = vsetq_lane_u8(x6, x, 5); - x = vsetq_lane_u8(x7, x, 6); - x = vsetq_lane_u8(x8, x, 7); - x = vsetq_lane_u8(x9, x, 8); - x = vsetq_lane_u8(x10, x, 9); - x = vsetq_lane_u8(x11, x, 10); - x = vsetq_lane_u8(x12, x, 11); - x = vsetq_lane_u8(x13, x, 12); - x = vsetq_lane_u8(x14, x, 13); - x = vsetq_lane_u8(x15, x, 14); - x = vsetq_lane_u8(x16, x, 15); - return x; -} - -simdjson_inline uint8x8_t make_uint8x8_t(uint8_t x1, uint8_t x2, uint8_t x3, uint8_t x4, - uint8_t x5, uint8_t x6, uint8_t x7, uint8_t x8) { - uint8x8_t x{}; - x = vset_lane_u8(x1, x, 0); - x = vset_lane_u8(x2, x, 1); - x = vset_lane_u8(x3, x, 2); - x = vset_lane_u8(x4, x, 3); - x = vset_lane_u8(x5, x, 4); - x = vset_lane_u8(x6, x, 5); - x = vset_lane_u8(x7, x, 6); - x = vset_lane_u8(x8, x, 7); - return x; -} - -// We have to do the same work for make_int8x16_t -simdjson_inline int8x16_t make_int8x16_t(int8_t x1, int8_t x2, int8_t x3, int8_t x4, - int8_t x5, int8_t x6, int8_t x7, int8_t x8, - int8_t x9, int8_t x10, int8_t x11, int8_t x12, - int8_t x13, int8_t x14, int8_t x15, int8_t x16) { - // Doing a load like so end ups generating worse code. - // int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, - // x9, x10,x11,x12,x13,x14,x15,x16}; - // return vld1q_s8(array); - int8x16_t x{}; - // incredibly, Visual Studio does not allow x[0] = x1 - x = vsetq_lane_s8(x1, x, 0); - x = vsetq_lane_s8(x2, x, 1); - x = vsetq_lane_s8(x3, x, 2); - x = vsetq_lane_s8(x4, x, 3); - x = vsetq_lane_s8(x5, x, 4); - x = vsetq_lane_s8(x6, x, 5); - x = vsetq_lane_s8(x7, x, 6); - x = vsetq_lane_s8(x8, x, 7); - x = vsetq_lane_s8(x9, x, 8); - x = vsetq_lane_s8(x10, x, 9); - x = vsetq_lane_s8(x11, x, 10); - x = vsetq_lane_s8(x12, x, 11); - x = vsetq_lane_s8(x13, x, 12); - x = vsetq_lane_s8(x14, x, 13); - x = vsetq_lane_s8(x15, x, 14); - x = vsetq_lane_s8(x16, x, 15); - return x; -} - -// End of private section with Visual Studio workaround -} // namespace -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO - - - template - struct simd8; - - // - // Base class of simd8 and simd8, both of which use uint8x16_t internally. - // - template> - struct base_u8 { - uint8x16_t value; - static const int SIZE = sizeof(value); - - // Conversion from/to SIMD register - simdjson_inline base_u8(const uint8x16_t _value) : value(_value) {} - simdjson_inline operator const uint8x16_t&() const { return this->value; } - simdjson_inline operator uint8x16_t&() { return this->value; } - - // Bit operations - simdjson_inline simd8 operator|(const simd8 other) const { return vorrq_u8(*this, other); } - simdjson_inline simd8 operator&(const simd8 other) const { return vandq_u8(*this, other); } - simdjson_inline simd8 operator^(const simd8 other) const { return veorq_u8(*this, other); } - simdjson_inline simd8 bit_andnot(const simd8 other) const { return vbicq_u8(*this, other); } - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - simdjson_inline simd8& operator|=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline simd8& operator&=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline simd8& operator^=(const simd8 other) { auto this_cast = static_cast*>(this); *this_cast = *this_cast ^ other; return *this_cast; } - - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return vceqq_u8(lhs, rhs); } - - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_u8(prev_chunk, *this, 16 - N); - } - }; - - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8: base_u8 { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; - - static simdjson_inline simd8 splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); } - - simdjson_inline simd8(const uint8x16_t _value) : base_u8(_value) {} - // False constructor - simdjson_inline simd8() : simd8(vdupq_n_u8(0)) {} - // Splat constructor - simdjson_inline simd8(bool _value) : simd8(splat(_value)) {} - - // We return uint32_t instead of uint16_t because that seems to be more efficient for most - // purposes (cutting it down to uint16_t costs performance in some compilers). - simdjson_inline uint32_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - const uint8x16_t bit_mask = make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80); -#else - const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; -#endif - auto minput = *this & bit_mask; - uint8x16_t tmp = vpaddq_u8(minput, minput); - tmp = vpaddq_u8(tmp, tmp); - tmp = vpaddq_u8(tmp, tmp); - return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0); - } - simdjson_inline bool any() const { return vmaxvq_u8(*this) != 0; } - }; - - // Unsigned bytes - template<> - struct simd8: base_u8 { - static simdjson_inline uint8x16_t splat(uint8_t _value) { return vmovq_n_u8(_value); } - static simdjson_inline uint8x16_t zero() { return vdupq_n_u8(0); } - static simdjson_inline uint8x16_t load(const uint8_t* values) { return vld1q_u8(values); } - - simdjson_inline simd8(const uint8x16_t _value) : base_u8(_value) {} - // Zero constructor - simdjson_inline simd8() : simd8(zero()) {} - // Array constructor - simdjson_inline simd8(const uint8_t values[16]) : simd8(load(values)) {} - // Splat constructor - simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - simdjson_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) : simd8(make_uint8x16_t( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - )) {} -#else - simdjson_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) : simd8(uint8x16_t{ - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - }) {} -#endif - - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 repeat_16( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Store to array - simdjson_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); } - - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return vqaddq_u8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return vqsubq_u8(*this, other); } - - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_u8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_u8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } - - // Order-specific operations - simdjson_inline uint8_t max_val() const { return vmaxvq_u8(*this); } - simdjson_inline uint8_t min_val() const { return vminvq_u8(*this); } - simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_u8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_u8(*this, other); } - simdjson_inline simd8 operator<=(const simd8 other) const { return vcleq_u8(*this, other); } - simdjson_inline simd8 operator>=(const simd8 other) const { return vcgeq_u8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_u8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_u8(*this, other); } - // Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's. - simdjson_inline simd8 gt_bits(const simd8 other) const { return simd8(*this > other); } - // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's. - simdjson_inline simd8 lt_bits(const simd8 other) const { return simd8(*this < other); } - - // Bit-specific operations - simdjson_inline simd8 any_bits_set(simd8 bits) const { return vtstq_u8(*this, bits); } - simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return (*this & bits).any_bits_set_anywhere(); } - template - simdjson_inline simd8 shr() const { return vshrq_n_u8(*this, N); } - template - simdjson_inline simd8 shl() const { return vshlq_n_u8(*this, N); } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } - - - // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset). - // Passing a 0 value for mask would be equivalent to writing out every byte to output. - // Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes - // get written. - // Design consideration: it seems like a function with the - // signature simd8 compress(uint16_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint16_t mask, L * output) const { - using internal::thintable_epi8; - using internal::BitsSetTable256mul2; - using internal::pshufb_combine_table; - // this particular implementation was inspired by work done by @animetosho - // we do it in two steps, first 8 bytes and then second 8 bytes - uint8_t mask1 = uint8_t(mask); // least significant 8 bits - uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits - // next line just loads the 64-bit values thintable_epi8[mask1] and - // thintable_epi8[mask2] into a 128-bit register, using only - // two instructions on most compilers. - uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]}; - uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64); - // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - uint8x16_t inc = make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); -#else - uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; -#endif - shufmask = vaddq_u8(shufmask, inc); - // this is the version "nearly pruned" - uint8x16_t pruned = vqtbl1q_u8(*this, shufmask); - // we still need to put the two halves together. - // we compute the popcount of the first half: - int pop1 = BitsSetTable256mul2[mask1]; - // then load the corresponding mask, what it does is to write - // only the first pop1 bytes from the first 8 bytes, and then - // it fills in with the bytes from the second 8 bytes + some filling - // at the end. - uint8x16_t compactmask = vld1q_u8(reinterpret_cast(pshufb_combine_table + pop1 * 8)); - uint8x16_t answer = vqtbl1q_u8(pruned, compactmask); - vst1q_u8(reinterpret_cast(output), answer); - } - - // Copies all bytes corresponding to a 0 in the low half of the mask (interpreted as a - // bitset) to output1, then those corresponding to a 0 in the high half to output2. - template - simdjson_inline void compress_halves(uint16_t mask, L *output1, L *output2) const { - using internal::thintable_epi8; - uint8_t mask1 = uint8_t(mask); // least significant 8 bits - uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits - uint8x8_t compactmask1 = vcreate_u8(thintable_epi8[mask1]); - uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]); - // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - uint8x8_t inc = make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); -#else - uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; -#endif - compactmask2 = vadd_u8(compactmask2, inc); - // store each result (with the second store possibly overlapping the first) - vst1_u8((uint8_t*)output1, vqtbl1_u8(*this, compactmask1)); - vst1_u8((uint8_t*)output2, vqtbl1_u8(*this, compactmask2)); - } - - template - simdjson_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_u8(*this, simd8(original)); - } - }; - - // Signed bytes - template<> - struct simd8 { - int8x16_t value; - - static simdjson_inline simd8 splat(int8_t _value) { return vmovq_n_s8(_value); } - static simdjson_inline simd8 zero() { return vdupq_n_s8(0); } - static simdjson_inline simd8 load(const int8_t values[16]) { return vld1q_s8(values); } - - // Conversion from/to SIMD register - simdjson_inline simd8(const int8x16_t _value) : value{_value} {} - simdjson_inline operator const int8x16_t&() const { return this->value; } - simdjson_inline operator int8x16_t&() { return this->value; } - - // Zero constructor - simdjson_inline simd8() : simd8(zero()) {} - // Splat constructor - simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {} - // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - simdjson_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) : simd8(make_int8x16_t( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - )) {} -#else - simdjson_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) : simd8(int8x16_t{ - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - }) {} -#endif - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 repeat_16( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Store to array - simdjson_inline void store(int8_t dst[16]) const { return vst1q_s8(dst, *this); } - - // Explicit conversion to/from unsigned - // - // Under Visual Studio/ARM64 uint8x16_t and int8x16_t are apparently the same type. - // In theory, we could check this occurrence with std::same_as and std::enabled_if but it is C++14 - // and relatively ugly and hard to read. -#ifndef SIMDJSON_REGULAR_VISUAL_STUDIO - simdjson_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {} -#endif - simdjson_inline explicit operator simd8() const { return vreinterpretq_u8_s8(this->value); } - - // Math - simdjson_inline simd8 operator+(const simd8 other) const { return vaddq_s8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return vsubq_s8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *this; } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *this; } - - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return vmaxq_s8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return vminq_s8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return vcgtq_s8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return vcltq_s8(*this, other); } - simdjson_inline simd8 operator==(const simd8 other) const { return vceqq_s8(*this, other); } - - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return vextq_s8(prev_chunk, *this, 16 - N); - } - - // Perform a lookup assuming no value is larger than 16 - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); - } - template - simdjson_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - - template - simdjson_inline simd8 apply_lookup_16_to(const simd8 original) { - return vqtbl1q_s8(*this, simd8(original)); - } - }; - - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr+sizeof(simd8)*0); - this->chunks[1].store(ptr+sizeof(simd8)*1); - this->chunks[2].store(ptr+sizeof(simd8)*2); - this->chunks[3].store(ptr+sizeof(simd8)*3); - } - - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } - - - simdjson_inline uint64_t compress(uint64_t mask, T * output) const { - uint64_t popcounts = vget_lane_u64(vreinterpret_u64_u8(vcnt_u8(vcreate_u8(~mask))), 0); - // compute the prefix sum of the popcounts of each byte - uint64_t offsets = popcounts * 0x0101010101010101; - this->chunks[0].compress_halves(uint16_t(mask), output, &output[popcounts & 0xFF]); - this->chunks[1].compress_halves(uint16_t(mask >> 16), &output[(offsets >> 8) & 0xFF], &output[(offsets >> 16) & 0xFF]); - this->chunks[2].compress_halves(uint16_t(mask >> 32), &output[(offsets >> 24) & 0xFF], &output[(offsets >> 32) & 0xFF]); - this->chunks[3].compress_halves(uint16_t(mask >> 48), &output[(offsets >> 40) & 0xFF], &output[(offsets >> 48) & 0xFF]); - return offsets >> 56; - } - - simdjson_inline uint64_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - const uint8x16_t bit_mask = make_uint8x16_t( - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 - ); -#else - const uint8x16_t bit_mask = { - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, - 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 - }; -#endif - // Add each of the elements next to each other, successively, to stuff each 8 byte mask into one. - uint8x16_t sum0 = vpaddq_u8(this->chunks[0] & bit_mask, this->chunks[1] & bit_mask); - uint8x16_t sum1 = vpaddq_u8(this->chunks[2] & bit_mask, this->chunks[3] & bit_mask); - sum0 = vpaddq_u8(sum0, sum1); - sum0 = vpaddq_u8(sum0, sum0); - return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0); - } - - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } - - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 - -} // namespace simd -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson - -#endif // SIMDJSON_ARM64_SIMD_H -/* end file include/simdjson/arm64/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ - -namespace simdjson { -namespace arm64 { -namespace { -namespace jsoncharutils { - -// return non-zero if not a structural or whitespace char -// zero otherwise -simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace_negated[c]; -} - -simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; -} - -// returns a value with the high 16 bits set if not valid -// otherwise returns the conversion of the 4 hex digits at src into the bottom -// 16 bits of the 32-bit return register -// -// see -// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ -static inline uint32_t hex_to_u32_nocheck( - const uint8_t *src) { // strictly speaking, static inline is a C-ism - uint32_t v1 = internal::digit_to_val32[630 + src[0]]; - uint32_t v2 = internal::digit_to_val32[420 + src[1]]; - uint32_t v3 = internal::digit_to_val32[210 + src[2]]; - uint32_t v4 = internal::digit_to_val32[0 + src[3]]; - return v1 | v2 | v3 | v4; -} - -// given a code point cp, writes to c -// the utf-8 code, outputting the length in -// bytes, if the length is zero, the code point -// is invalid -// -// This can possibly be made faster using pdep -// and clz and table lookups, but JSON documents -// have few escaped code points, and the following -// function looks cheap. -// -// Note: we assume that surrogates are treated separately -// -simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { - if (cp <= 0x7F) { - c[0] = uint8_t(cp); - return 1; // ascii - } - if (cp <= 0x7FF) { - c[0] = uint8_t((cp >> 6) + 192); - c[1] = uint8_t((cp & 63) + 128); - return 2; // universal plane - // Surrogates are treated elsewhere... - //} //else if (0xd800 <= cp && cp <= 0xdfff) { - // return 0; // surrogates // could put assert here - } else if (cp <= 0xFFFF) { - c[0] = uint8_t((cp >> 12) + 224); - c[1] = uint8_t(((cp >> 6) & 63) + 128); - c[2] = uint8_t((cp & 63) + 128); - return 3; - } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this - // is not needed - c[0] = uint8_t((cp >> 18) + 240); - c[1] = uint8_t(((cp >> 12) & 63) + 128); - c[2] = uint8_t(((cp >> 6) & 63) + 128); - c[3] = uint8_t((cp & 63) + 128); - return 4; - } - // will return 0 when the code point was too large. - return 0; // bad r -} - -#ifdef SIMDJSON_IS_32BITS // _umul128 for x86, arm -// this is a slow emulation routine for 32-bit -// -static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { - return x * (uint64_t)y; -} -static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { - uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); - uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); - uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); - uint64_t adbc_carry = !!(adbc < ad); - uint64_t lo = bd + (adbc << 32); - *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + - (adbc_carry << 32) + !!(lo < bd); - return lo; -} -#endif - -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) -#ifdef _M_ARM64 - // ARM64 has native support for 64-bit multiplications, no need to emultate - answer.high = __umulh(value1, value2); - answer.low = value1 * value2; -#else - answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64 -#endif // _M_ARM64 -#else // defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) - __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; - answer.low = uint64_t(r); - answer.high = uint64_t(r >> 64); -#endif - return answer; -} - -} // namespace jsoncharutils -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace arm64 { -namespace { -/// @private -namespace atomparsing { - -// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. -// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot -// be certain that the character pointer will be properly aligned. -// You might think that using memcpy makes this function expensive, but you'd be wrong. -// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); -// to the compile-time constant 1936482662. -simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } - - -// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive. -// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about. -simdjson_warn_unused -simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) { - uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++) - static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes"); - std::memcpy(&srcval, src, sizeof(uint32_t)); - return srcval ^ string_to_uint32(atom); -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src) { - return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_true_atom(src); } - else if (len == 4) { return !str4ncmp(src, "true"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src) { - return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) { - if (len > 5) { return is_valid_false_atom(src); } - else if (len == 5) { return !str4ncmp(src+1, "alse"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src) { - return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_null_atom(src); } - else if (len == 4) { return !str4ncmp(src, "null"); } - else { return false; } -} - -} // namespace atomparsing -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/arm64/stringparsing.h */ -#ifndef SIMDJSON_ARM64_STRINGPARSING_H -#define SIMDJSON_ARM64_STRINGPARSING_H - - -namespace simdjson { -namespace arm64 { -namespace { - -using namespace simd; - -// Holds backslashes and quotes locations. -struct backslash_and_quote { -public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); - - simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } - simdjson_inline bool has_backslash() { return bs_bits != 0; } - simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); } - simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); } - - uint32_t bs_bits; - uint32_t quote_bits; -}; // struct backslash_and_quote - -simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { - // this can read up to 31 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes"); - simd8 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); - - // Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on ARM; therefore, we - // smash them together into a 64-byte mask and get the bitmask from there. - uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; -} - -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson - -#endif // SIMDJSON_ARM64_STRINGPARSING_H -/* end file include/simdjson/arm64/stringparsing.h */ -/* begin file include/simdjson/arm64/numberparsing.h */ -#ifndef SIMDJSON_ARM64_NUMBERPARSING_H -#define SIMDJSON_ARM64_NUMBERPARSING_H - -namespace simdjson { -namespace arm64 { -namespace { - -// we don't have SSE, so let us use a scalar function -// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ -static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { - uint64_t val; - std::memcpy(&val, chars, sizeof(uint64_t)); - val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); -} - -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson - -#define SIMDJSON_SWAR_NUMBER_PARSING 1 - -/* begin file include/simdjson/generic/numberparsing.h */ -#include - -namespace simdjson { -namespace arm64 { - -namespace ondemand { -/** - * The type of a JSON number - */ -enum class number_type { - floating_point_number=1, /// a binary64 number - signed_integer, /// a signed integer that fits in a 64-bit word using two's complement - unsigned_integer /// a positive integer larger or equal to 1<<63 -}; -} - -namespace { -/// @private -namespace numberparsing { - - - -#ifdef JSON_TEST_NUMBERS -#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) -#else -#define INVALID_NUMBER(SRC) (NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE)) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE)) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE)) -#endif - -namespace { -// Convert a mantissa, an exponent and a sign bit into an ieee64 double. -// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable). -// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed. -simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) { - double d; - mantissa &= ~(1ULL << 52); - mantissa |= real_exponent << 52; - mantissa |= ((static_cast(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; -} -} -// Attempts to compute i * 10^(power) exactly; and if "negative" is -// true, negate the result. -// This function will only work in some cases, when it does not work, success is -// set to false. This should work *most of the time* (like 99% of the time). -// We assume that power is in the [smallest_power, -// largest_power] interval: the caller is responsible for this check. -simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) { - // we start with a fast path - // It was described in - // Clinger WD. How to read floating point numbers accurately. - // ACM SIGPLAN Notices. 1990 -#ifndef FLT_EVAL_METHOD -#error "FLT_EVAL_METHOD should be defined, please include cfloat." -#endif -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - // We cannot be certain that x/y is rounded to nearest. - if (0 <= power && power <= 22 && i <= 9007199254740991) { -#else - if (-22 <= power && power <= 22 && i <= 9007199254740991) { -#endif - // convert the integer into a double. This is lossless since - // 0 <= i <= 2^53 - 1. - d = double(i); - // - // The general idea is as follows. - // If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then - // 1) Both s and p can be represented exactly as 64-bit floating-point - // values - // (binary64). - // 2) Because s and p can be represented exactly as floating-point values, - // then s * p - // and s / p will produce correctly rounded values. - // - if (power < 0) { - d = d / simdjson::internal::power_of_ten[-power]; - } else { - d = d * simdjson::internal::power_of_ten[power]; - } - if (negative) { - d = -d; - } - return true; - } - // When 22 < power && power < 22 + 16, we could - // hope for another, secondary fast path. It was - // described by David M. Gay in "Correctly rounded - // binary-decimal and decimal-binary conversions." (1990) - // If you need to compute i * 10^(22 + x) for x < 16, - // first compute i * 10^x, if you know that result is exact - // (e.g., when i * 10^x < 2^53), - // then you can still proceed and do (i * 10^x) * 10^22. - // Is this worth your time? - // You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53) - // for this second fast path to work. - // If you you have 22 < power *and* power < 22 + 16, and then you - // optimistically compute "i * 10^(x-22)", there is still a chance that you - // have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of - // this optimization maybe less common than we would like. Source: - // http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - // also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html - - // The fast path has now failed, so we are failing back on the slower path. - - // In the slow path, we need to adjust i so that it is > 1<<63 which is always - // possible, except if i == 0, so we handle i == 0 separately. - if(i == 0) { - d = 0.0; - return true; - } - - - // The exponent is 1024 + 63 + power - // + floor(log(5**power)/log(2)). - // The 1024 comes from the ieee64 standard. - // The 63 comes from the fact that we use a 64-bit word. - // - // Computing floor(log(5**power)/log(2)) could be - // slow. Instead we use a fast function. - // - // For power in (-400,350), we have that - // (((152170 + 65536) * power ) >> 16); - // is equal to - // floor(log(5**power)/log(2)) + power when power >= 0 - // and it is equal to - // ceil(log(5**-power)/log(2)) + power when power < 0 - // - // The 65536 is (1<<16) and corresponds to - // (65536 * power) >> 16 ---> power - // - // ((152170 * power ) >> 16) is equal to - // floor(log(5**power)/log(2)) - // - // Note that this is not magic: 152170/(1<<16) is - // approximatively equal to log(5)/log(2). - // The 1<<16 value is a power of two; we could use a - // larger power of 2 if we wanted to. - // - int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63; - - - // We want the most significant bit of i to be 1. Shift if needed. - int lz = leading_zeroes(i); - i <<= lz; - - - // We are going to need to do some 64-bit arithmetic to get a precise product. - // We use a table lookup approach. - // It is safe because - // power >= smallest_power - // and power <= largest_power - // We recover the mantissa of the power, it has a leading 1. It is always - // rounded down. - // - // We want the most significant 64 bits of the product. We know - // this will be non-zero because the most significant bit of i is - // 1. - const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power); - // Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.) - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 firstproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // Both i and power_of_five_128[index] have their most significant bit set to 1 which - // implies that the either the most or the second most significant bit of the product - // is 1. We pack values in this manner for efficiency reasons: it maximizes the use - // we make of the product. It also makes it easy to reason about the product: there - // is 0 or 1 leading zero in the product. - - // Unless the least significant 9 bits of the high (64-bit) part of the full - // product are all 1s, then we know that the most significant 55 bits are - // exact and no further work is needed. Having 55 bits is necessary because - // we need 53 bits for the mantissa but we have to have one rounding bit and - // we can waste a bit if the most significant bit of the product is zero. - if((firstproduct.high & 0x1FF) == 0x1FF) { - // We want to compute i * 5^q, but only care about the top 55 bits at most. - // Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing - // the full computation is wasteful. So we do what is called a "truncated - // multiplication". - // We take the most significant 64-bits, and we put them in - // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q - // to the desired approximation using one multiplication. Sometimes it does not suffice. - // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and - // then we get a better approximation to i * 5^q. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat - // more complicated. - // - // There is an extra layer of complexity in that we need more than 55 bits of - // accuracy in the round-to-even scenario. - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 secondproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - uint64_t lower = firstproduct.low; - uint64_t upper = firstproduct.high; - // The final mantissa should be 53 bits with a leading 1. - // We shift it so that it occupies 54 bits with a leading 1. - /////// - uint64_t upperbit = upper >> 63; - uint64_t mantissa = upper >> (upperbit + 9); - lz += int(1 ^ upperbit); - - // Here we have mantissa < (1<<54). - int64_t real_exponent = exponent - lz; - if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal? - // Here have that real_exponent <= 0 so -real_exponent >= 0 - if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. - d = 0.0; - return true; - } - // next line is safe because -real_exponent + 1 < 0 - mantissa >>= -real_exponent + 1; - // Thankfully, we can't have both "round-to-even" and subnormals because - // "round-to-even" only occurs for powers close to 0. - mantissa += (mantissa & 1); // round up - mantissa >>= 1; - // There is a weird scenario where we don't have a subnormal but just. - // Suppose we start with 2.2250738585072013e-308, we end up - // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal - // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round - // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer - // subnormal, but we can only know this after rounding. - // So we only declare a subnormal if we are smaller than the threshold. - real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1; - d = to_double(mantissa, real_exponent, negative); - return true; - } - // We have to round to even. The "to even" part - // is only a problem when we are right in between two floats - // which we guard against. - // If we have lots of trailing zeros, we may fall right between two - // floating-point values. - // - // The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54] - // times a power of two. That is, it is right between a number with binary significand - // m and another number with binary significand m+1; and it must be the case - // that it cannot be represented by a float itself. - // - // We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p. - // Recall that 10^q = 5^q * 2^q. - // When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that - // 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23. - // When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so - // (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have - // 2^{53} x 5^{-q} < 2^{64}. - // Hence we have 5^{-q} < 2^{11}$ or q>= -4. - // - // We require lower <= 1 and not lower == 0 because we could not prove that - // that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test. - if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) { - if((mantissa << (upperbit + 64 - 53 - 2)) == upper) { - mantissa &= ~1; // flip it so that we do not round up - } - } - - mantissa += mantissa & 1; - mantissa >>= 1; - - // Here we have mantissa < (1<<53), unless there was an overflow - if (mantissa >= (1ULL << 53)) { - ////////// - // This will happen when parsing values such as 7.2057594037927933e+16 - //////// - mantissa = (1ULL << 52); - real_exponent++; - } - mantissa &= ~(1ULL << 52); - // we have to check that real_exponent is in range, otherwise we bail out - if (simdjson_unlikely(real_exponent > 2046)) { - // We have an infinite value!!! We could actually throw an error here if we could. - return false; - } - d = to_double(mantissa, real_exponent, negative); - return true; -} - -// We call a fallback floating-point parser that might be slow. Note -// it will accept JSON numbers, but the JSON spec. is more restrictive so -// before you call parse_float_fallback, you need to have validated the input -// string with the JSON grammar. -// It will return an error (false) if the parsed number is infinite. -// The string parsing itself always succeeds. We know that there is at least -// one digit. -static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} -static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} - -// check quickly whether the next 8 chars are made of digits -// at a glance, it looks better than Mula's -// http://0x80.pl/articles/swar-digits-validate.html -simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) { - uint64_t val; - // this can read up to 7 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7"); - std::memcpy(&val, chars, 8); - // a branchy method might be faster: - // return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030) - // && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) == - // 0x3030303030303030); - return (((val & 0xF0F0F0F0F0F0F0F0) | - (((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) == - 0x3333333333333333); -} - -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; - } - return INVALID_NUMBER(src); -} - -template -SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later -simdjson_inline bool parse_digit(const uint8_t c, I &i) { - const uint8_t digit = static_cast(c - '0'); - if (digit > 9) { - return false; - } - // PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication - i = 10 * i + digit; // might overflow, we will handle the overflow later - return true; -} - -simdjson_inline error_code parse_decimal(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { - // we continue with the fiction that we have an integer. If the - // floating point number is representable as x * 10^z for some integer - // z that fits in 53 bits, then we will be able to convert back the - // the integer into a float in a lossless manner. - const uint8_t *const first_after_period = p; - -#ifdef SIMDJSON_SWAR_NUMBER_PARSING -#if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } -#endif // SIMDJSON_SWAR_NUMBER_PARSING -#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; -} - -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { - // Exp Sign: -123.456e[-]78 - bool neg_exp = ('-' == *p); - if (neg_exp || '+' == *p) { p++; } // Skip + as well - - // Exponent: -123.456e-[78] - auto start_exp = p; - int64_t exp_number = 0; - while (parse_digit(*p, exp_number)) { ++p; } - // It is possible for parse_digit to overflow. - // In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN. - // Thus we *must* check for possible overflow before we negate exp_number. - - // Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into - // a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may - // not oblige and may, in fact, generate two distinct paths in any case. It might be - // possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off - // instructions for a simdjson_likely branch, an unconclusive gain. - - // If there were no digits, it's an error. - if (simdjson_unlikely(p == start_exp)) { - return INVALID_NUMBER(src); - } - // We have a valid positive exponent in exp_number at this point, except that - // it may have overflowed. - - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - while (*start_exp == '0') { start_exp++; } - // 19 digits could overflow int64_t and is kind of absurd anyway. We don't - // support exponents smaller than -999,999,999,999,999,999 and bigger - // than 999,999,999,999,999,999. - // We can truncate. - // Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before - // infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could - // truncate at 324. - // Note that there is no reason to fail per se at this point in time. - // E.g., 0e999999999999999999999 is a fine number. - if (p > start_exp+18) { exp_number = 999999999999999999; } - } - // At this point, we know that exp_number is a sane, positive, signed integer. - // It is <= 999,999,999,999,999,999. As long as 'exponent' is in - // [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent' - // is bounded in magnitude by the size of the JSON input, we are fine in this universe. - // To sum it up: the next line should never overflow. - exponent += (neg_exp ? -exp_number : exp_number); - return SUCCESS; -} - -simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) { - // It is possible that the integer had an overflow. - // We have to handle the case where we have 0.0000somenumber. - const uint8_t *start = start_digits; - while ((*start == '0') || (*start == '.')) { ++start; } - // we over-decrement by one when there is a '.' - return digit_count - size_t(start - start_digits); -} - -template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { - // If we frequently had to deal with long strings of digits, - // we could extend our code by using a 128-bit integer instead - // of a 64-bit integer. However, this is uncommon in practice. - // - // 9999999999999999999 < 2**64 so we can accommodate 19 digits. - // If we have a decimal separator, then digit_count - 1 is the number of digits, but we - // may not have a decimal separator! - if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) { - // Ok, chances are good that we had an overflow! - // this is almost never going to get called!!! - // we start anew, going slowly!!! - // This will happen in the following examples: - // 10000000000000000000000000000000000000000000e+308 - // 3.1415926535897932384626433832795028841971693993751 - // - // NOTE: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. If we passed our writer reference to - // it, it would force it to be stored in memory, preventing the compiler from picking it apart - // and putting into registers. i.e. if we pass it as reference, it gets slow. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - return error; - } - // NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other - // way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331 - // To future reader: we'd love if someone found a better way, or at least could explain this result! - if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) { - // - // Important: smallest_power is such that it leads to a zero value. - // Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero - // so something x 10^-343 goes to zero, but not so with something x 10^-342. - static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough"); - // - if((exponent < simdjson::internal::smallest_power) || (i == 0)) { - WRITE_DOUBLE(0, src, writer); - return SUCCESS; - } else { // (exponent > largest_power) and (i != 0) - // We have, for sure, an infinite value and simdjson refuses to parse infinite values. - return INVALID_NUMBER(src); - } - } - double d; - if (!compute_float_64(exponent, i, negative, d)) { - // we are almost never going to get here. - if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); } - } - WRITE_DOUBLE(d, src, writer); - return SUCCESS; -} - -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING - -template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds -} - -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return ondemand::number_type::signed_integer; } -#else - -// parse the number at src -// define JSON_TEST_NUMBERS for unit testing -// -// It is assumed that the number is followed by a structural ({,},],[) character -// or a white space character. If that is not the case (e.g., when the JSON -// document is made of a single number), then it is necessary to copy the -// content and append a space before calling this function. -// -// Our objective is accurate parsing (ULP of 0) at high speed. -template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); } - - // - // Handle floats if there is a . or e (or both) - // - int64_t exponent = 0; - bool is_float = false; - if ('.' == *p) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_decimal(src, p, i, exponent) ); - digit_count = int(p - start_digits); // used later to guard against overflows - } - if (('e' == *p) || ('E' == *p)) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_exponent(src, p, exponent) ); - } - if (is_float) { - const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p); - SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) ); - if (dirty_end) { return INVALID_NUMBER(src); } - return SUCCESS; - } - - // The longest negative 64-bit number is 19 digits. - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - size_t longest_digit_count = negative ? 19 : 20; - if (digit_count > longest_digit_count) { return INVALID_NUMBER(src); } - if (digit_count == longest_digit_count) { - if (negative) { - // Anything negative above INT64_MAX+1 is invalid - if (i > uint64_t(INT64_MAX)+1) { return INVALID_NUMBER(src); } - WRITE_INTEGER(~i+1, src, writer); - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } - } - - // Write unsigned if it doesn't fit in a signed integer. - if (i > uint64_t(INT64_MAX)) { - WRITE_UNSIGNED(i, src, writer); - } else { - WRITE_INTEGER(negative ? (~i+1) : i, src, writer); - } - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; -} - -// Inlineable functions -namespace { - -// This table can be used to characterize the final character of an integer -// string. For JSON structural character and allowable white space characters, -// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise -// we return NUMBER_ERROR. -// Optimization note: we could easily reduce the size of the table by half (to 128) -// at the cost of an extra branch. -// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits): -static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast"); - -const uint8_t integer_string_finisher[256] = { - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR}; - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - - -// Parse any number from 0 to 18,446,744,073,709,551,615 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { - const uint8_t *p = src + 1; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (*p != '"') { return NUMBER_ERROR; } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - // Note: we use src[1] and not src[0] because src[0] is the quote character in this - // instance. - if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept { - // - // Check for minus sign - // - if(src == src_end) { return NUMBER_ERROR; } - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - const uint8_t *p = src + negative + 1; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(*p != '"') { return NUMBER_ERROR; } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { - return (*src == '-'); -} - -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; } - return false; -} - -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return ondemand::number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) { - return ondemand::number_type::unsigned_integer; - } - } - return ondemand::number_type::signed_integer; - } - // Hopefully, we have 'e' or 'E' or '.'. - return ondemand::number_type::floating_point_number; -} - -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept { - if(src == src_end) { return NUMBER_ERROR; } - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - if(p == src_end) { return NUMBER_ERROR; } - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while ((p != src_end) && parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely((p != src_end) && (*p == '.'))) { - p++; - const uint8_t *start_decimal_digits = p; - if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if ((p != src_end) && (*p == 'e' || *p == 'E')) { - p++; - if(p == src_end) { return NUMBER_ERROR; } - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while ((p != src_end) && parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, src_end, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - src += negative + 1; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (*p != '"') { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} -} //namespace {} -#endif // SIMDJSON_SKIPNUMBERPARSING - -} // namespace numberparsing -} // unnamed namespace -} // namespace arm64 -} // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_ARM64_NUMBERPARSING_H -/* end file include/simdjson/arm64/numberparsing.h */ -/* begin file include/simdjson/arm64/end.h */ -/* end file include/simdjson/arm64/end.h */ - -#endif // SIMDJSON_IMPLEMENTATION_ARM64 - -#endif // SIMDJSON_ARM64_H -/* end file include/simdjson/arm64.h */ -/* begin file include/simdjson/fallback.h */ -#ifndef SIMDJSON_FALLBACK_H -#define SIMDJSON_FALLBACK_H - - -#if SIMDJSON_IMPLEMENTATION_FALLBACK - -namespace simdjson { -/** - * Fallback implementation (runs on any machine). - */ -namespace fallback { -} // namespace fallback -} // namespace simdjson - -/* begin file include/simdjson/fallback/implementation.h */ -#ifndef SIMDJSON_FALLBACK_IMPLEMENTATION_H -#define SIMDJSON_FALLBACK_IMPLEMENTATION_H - - -namespace simdjson { -namespace fallback { - -namespace { -using namespace simdjson; -using namespace simdjson::dom; -} - -class implementation final : public simdjson::implementation { -public: - simdjson_inline implementation() : simdjson::implementation( - "fallback", - "Generic fallback implementation", - 0 - ) {} - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t capacity, - size_t max_length, - std::unique_ptr& dst - ) const noexcept final; - simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; - simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; -}; - -} // namespace fallback -} // namespace simdjson - -#endif // SIMDJSON_FALLBACK_IMPLEMENTATION_H -/* end file include/simdjson/fallback/implementation.h */ - -/* begin file include/simdjson/fallback/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "fallback" -// #define SIMDJSON_IMPLEMENTATION fallback -/* end file include/simdjson/fallback/begin.h */ - -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ - -namespace simdjson { -namespace fallback { - -// expectation: sizeof(open_container) = 64/8. -struct open_container { - uint32_t tape_index; // where, on the tape, does the scope ([,{) begins - uint32_t count; // how many elements in the scope -}; // struct open_container - -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); - -class dom_parser_implementation final : public internal::dom_parser_implementation { -public: - /** Tape location of each open { or [ */ - std::unique_ptr open_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr is_array{}; - /** Buffer passed to stage 1 */ - const uint8_t *buf{}; - /** Length passed to stage 1 */ - size_t len{0}; - /** Document passed to stage 2 */ - dom::document *doc{}; - - inline dom_parser_implementation() noexcept; - inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; - inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; - dom_parser_implementation(const dom_parser_implementation &) = delete; - dom_parser_implementation &operator=(const dom_parser_implementation &) = delete; - - simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final; - simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final; - simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept final; - inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final; - inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final; -private: - simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity); - -}; - -} // namespace fallback -} // namespace simdjson - -namespace simdjson { -namespace fallback { - -inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -// Leaving these here so they can be inlined if so desired -inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { - if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] ); - if (!structural_indexes) { _capacity = 0; return MEMALLOC; } - structural_indexes[0] = 0; - n_structural_indexes = 0; - - _capacity = capacity; - return SUCCESS; -} - -inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // Stage 2 stacks - open_containers.reset(new (std::nothrow) open_container[max_depth]); - is_array.reset(new (std::nothrow) bool[max_depth]); - if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; } - - _max_depth = max_depth; - return SUCCESS; -} - -} // namespace fallback -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/fallback/bitmanipulation.h */ -#ifndef SIMDJSON_FALLBACK_BITMANIPULATION_H -#define SIMDJSON_FALLBACK_BITMANIPULATION_H - -#include - -namespace simdjson { -namespace fallback { -namespace { - -#if defined(_MSC_VER) && !defined(_M_ARM64) && !defined(_M_X64) -static inline unsigned char _BitScanForward64(unsigned long* ret, uint64_t x) { - unsigned long x0 = (unsigned long)x, top, bottom; - _BitScanForward(&top, (unsigned long)(x >> 32)); - _BitScanForward(&bottom, x0); - *ret = x0 ? bottom : 32 + top; - return x != 0; -} -static unsigned char _BitScanReverse64(unsigned long* ret, uint64_t x) { - unsigned long x1 = (unsigned long)(x >> 32), top, bottom; - _BitScanReverse(&top, x1); - _BitScanReverse(&bottom, (unsigned long)x); - *ret = x1 ? top + 32 : bottom; - return x != 0; -} -#endif - -/* result might be undefined when input_num is zero */ -simdjson_inline int leading_zeroes(uint64_t input_num) { -#ifdef _MSC_VER - unsigned long leading_zero = 0; - // Search the mask data from most significant bit (MSB) - // to least significant bit (LSB) for a set bit (1). - if (_BitScanReverse64(&leading_zero, input_num)) - return (int)(63 - leading_zero); - else - return 64; -#else - return __builtin_clzll(input_num); -#endif// _MSC_VER -} - -} // unnamed namespace -} // namespace fallback -} // namespace simdjson - -#endif // SIMDJSON_FALLBACK_BITMANIPULATION_H -/* end file include/simdjson/fallback/bitmanipulation.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ - -namespace simdjson { -namespace fallback { -namespace { -namespace jsoncharutils { - -// return non-zero if not a structural or whitespace char -// zero otherwise -simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace_negated[c]; -} - -simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; -} - -// returns a value with the high 16 bits set if not valid -// otherwise returns the conversion of the 4 hex digits at src into the bottom -// 16 bits of the 32-bit return register -// -// see -// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ -static inline uint32_t hex_to_u32_nocheck( - const uint8_t *src) { // strictly speaking, static inline is a C-ism - uint32_t v1 = internal::digit_to_val32[630 + src[0]]; - uint32_t v2 = internal::digit_to_val32[420 + src[1]]; - uint32_t v3 = internal::digit_to_val32[210 + src[2]]; - uint32_t v4 = internal::digit_to_val32[0 + src[3]]; - return v1 | v2 | v3 | v4; -} - -// given a code point cp, writes to c -// the utf-8 code, outputting the length in -// bytes, if the length is zero, the code point -// is invalid -// -// This can possibly be made faster using pdep -// and clz and table lookups, but JSON documents -// have few escaped code points, and the following -// function looks cheap. -// -// Note: we assume that surrogates are treated separately -// -simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { - if (cp <= 0x7F) { - c[0] = uint8_t(cp); - return 1; // ascii - } - if (cp <= 0x7FF) { - c[0] = uint8_t((cp >> 6) + 192); - c[1] = uint8_t((cp & 63) + 128); - return 2; // universal plane - // Surrogates are treated elsewhere... - //} //else if (0xd800 <= cp && cp <= 0xdfff) { - // return 0; // surrogates // could put assert here - } else if (cp <= 0xFFFF) { - c[0] = uint8_t((cp >> 12) + 224); - c[1] = uint8_t(((cp >> 6) & 63) + 128); - c[2] = uint8_t((cp & 63) + 128); - return 3; - } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this - // is not needed - c[0] = uint8_t((cp >> 18) + 240); - c[1] = uint8_t(((cp >> 12) & 63) + 128); - c[2] = uint8_t(((cp >> 6) & 63) + 128); - c[3] = uint8_t((cp & 63) + 128); - return 4; - } - // will return 0 when the code point was too large. - return 0; // bad r -} - -#ifdef SIMDJSON_IS_32BITS // _umul128 for x86, arm -// this is a slow emulation routine for 32-bit -// -static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { - return x * (uint64_t)y; -} -static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { - uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); - uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); - uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); - uint64_t adbc_carry = !!(adbc < ad); - uint64_t lo = bd + (adbc << 32); - *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + - (adbc_carry << 32) + !!(lo < bd); - return lo; -} -#endif - -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) -#ifdef _M_ARM64 - // ARM64 has native support for 64-bit multiplications, no need to emultate - answer.high = __umulh(value1, value2); - answer.low = value1 * value2; -#else - answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64 -#endif // _M_ARM64 -#else // defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) - __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; - answer.low = uint64_t(r); - answer.high = uint64_t(r >> 64); -#endif - return answer; -} - -} // namespace jsoncharutils -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace fallback { -namespace { -/// @private -namespace atomparsing { - -// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. -// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot -// be certain that the character pointer will be properly aligned. -// You might think that using memcpy makes this function expensive, but you'd be wrong. -// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); -// to the compile-time constant 1936482662. -simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } - - -// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive. -// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about. -simdjson_warn_unused -simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) { - uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++) - static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes"); - std::memcpy(&srcval, src, sizeof(uint32_t)); - return srcval ^ string_to_uint32(atom); -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src) { - return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_true_atom(src); } - else if (len == 4) { return !str4ncmp(src, "true"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src) { - return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) { - if (len > 5) { return is_valid_false_atom(src); } - else if (len == 5) { return !str4ncmp(src+1, "alse"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src) { - return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_null_atom(src); } - else if (len == 4) { return !str4ncmp(src, "null"); } - else { return false; } -} - -} // namespace atomparsing -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/fallback/stringparsing.h */ -#ifndef SIMDJSON_FALLBACK_STRINGPARSING_H -#define SIMDJSON_FALLBACK_STRINGPARSING_H - - -namespace simdjson { -namespace fallback { -namespace { - -// Holds backslashes and quotes locations. -struct backslash_and_quote { -public: - static constexpr uint32_t BYTES_PROCESSED = 1; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); - - simdjson_inline bool has_quote_first() { return c == '"'; } - simdjson_inline bool has_backslash() { return c == '\\'; } - simdjson_inline int quote_index() { return c == '"' ? 0 : 1; } - simdjson_inline int backslash_index() { return c == '\\' ? 0 : 1; } - - uint8_t c; -}; // struct backslash_and_quote - -simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { - // store to dest unconditionally - we can overwrite the bits we don't like later - dst[0] = src[0]; - return { src[0] }; -} - -} // unnamed namespace -} // namespace fallback -} // namespace simdjson - -#endif // SIMDJSON_FALLBACK_STRINGPARSING_H -/* end file include/simdjson/fallback/stringparsing.h */ -/* begin file include/simdjson/fallback/numberparsing.h */ -#ifndef SIMDJSON_FALLBACK_NUMBERPARSING_H -#define SIMDJSON_FALLBACK_NUMBERPARSING_H - -#ifdef JSON_TEST_NUMBERS // for unit testing -void found_invalid_number(const uint8_t *buf); -void found_integer(int64_t result, const uint8_t *buf); -void found_unsigned_integer(uint64_t result, const uint8_t *buf); -void found_float(double result, const uint8_t *buf); -#endif - -namespace simdjson { -namespace fallback { -namespace { -// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ -static simdjson_inline uint32_t parse_eight_digits_unrolled(const char *chars) { - uint64_t val; - memcpy(&val, chars, sizeof(uint64_t)); - val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); -} -static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { - return parse_eight_digits_unrolled(reinterpret_cast(chars)); -} - -} // unnamed namespace -} // namespace fallback -} // namespace simdjson - -#define SIMDJSON_SWAR_NUMBER_PARSING 1 - -/* begin file include/simdjson/generic/numberparsing.h */ -#include - -namespace simdjson { -namespace fallback { - -namespace ondemand { -/** - * The type of a JSON number - */ -enum class number_type { - floating_point_number=1, /// a binary64 number - signed_integer, /// a signed integer that fits in a 64-bit word using two's complement - unsigned_integer /// a positive integer larger or equal to 1<<63 -}; -} - -namespace { -/// @private -namespace numberparsing { - - - -#ifdef JSON_TEST_NUMBERS -#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) -#else -#define INVALID_NUMBER(SRC) (NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE)) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE)) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE)) -#endif - -namespace { -// Convert a mantissa, an exponent and a sign bit into an ieee64 double. -// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable). -// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed. -simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) { - double d; - mantissa &= ~(1ULL << 52); - mantissa |= real_exponent << 52; - mantissa |= ((static_cast(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; -} -} -// Attempts to compute i * 10^(power) exactly; and if "negative" is -// true, negate the result. -// This function will only work in some cases, when it does not work, success is -// set to false. This should work *most of the time* (like 99% of the time). -// We assume that power is in the [smallest_power, -// largest_power] interval: the caller is responsible for this check. -simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) { - // we start with a fast path - // It was described in - // Clinger WD. How to read floating point numbers accurately. - // ACM SIGPLAN Notices. 1990 -#ifndef FLT_EVAL_METHOD -#error "FLT_EVAL_METHOD should be defined, please include cfloat." -#endif -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - // We cannot be certain that x/y is rounded to nearest. - if (0 <= power && power <= 22 && i <= 9007199254740991) { -#else - if (-22 <= power && power <= 22 && i <= 9007199254740991) { -#endif - // convert the integer into a double. This is lossless since - // 0 <= i <= 2^53 - 1. - d = double(i); - // - // The general idea is as follows. - // If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then - // 1) Both s and p can be represented exactly as 64-bit floating-point - // values - // (binary64). - // 2) Because s and p can be represented exactly as floating-point values, - // then s * p - // and s / p will produce correctly rounded values. - // - if (power < 0) { - d = d / simdjson::internal::power_of_ten[-power]; - } else { - d = d * simdjson::internal::power_of_ten[power]; - } - if (negative) { - d = -d; - } - return true; - } - // When 22 < power && power < 22 + 16, we could - // hope for another, secondary fast path. It was - // described by David M. Gay in "Correctly rounded - // binary-decimal and decimal-binary conversions." (1990) - // If you need to compute i * 10^(22 + x) for x < 16, - // first compute i * 10^x, if you know that result is exact - // (e.g., when i * 10^x < 2^53), - // then you can still proceed and do (i * 10^x) * 10^22. - // Is this worth your time? - // You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53) - // for this second fast path to work. - // If you you have 22 < power *and* power < 22 + 16, and then you - // optimistically compute "i * 10^(x-22)", there is still a chance that you - // have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of - // this optimization maybe less common than we would like. Source: - // http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - // also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html - - // The fast path has now failed, so we are failing back on the slower path. - - // In the slow path, we need to adjust i so that it is > 1<<63 which is always - // possible, except if i == 0, so we handle i == 0 separately. - if(i == 0) { - d = 0.0; - return true; - } - - - // The exponent is 1024 + 63 + power - // + floor(log(5**power)/log(2)). - // The 1024 comes from the ieee64 standard. - // The 63 comes from the fact that we use a 64-bit word. - // - // Computing floor(log(5**power)/log(2)) could be - // slow. Instead we use a fast function. - // - // For power in (-400,350), we have that - // (((152170 + 65536) * power ) >> 16); - // is equal to - // floor(log(5**power)/log(2)) + power when power >= 0 - // and it is equal to - // ceil(log(5**-power)/log(2)) + power when power < 0 - // - // The 65536 is (1<<16) and corresponds to - // (65536 * power) >> 16 ---> power - // - // ((152170 * power ) >> 16) is equal to - // floor(log(5**power)/log(2)) - // - // Note that this is not magic: 152170/(1<<16) is - // approximatively equal to log(5)/log(2). - // The 1<<16 value is a power of two; we could use a - // larger power of 2 if we wanted to. - // - int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63; - - - // We want the most significant bit of i to be 1. Shift if needed. - int lz = leading_zeroes(i); - i <<= lz; - - - // We are going to need to do some 64-bit arithmetic to get a precise product. - // We use a table lookup approach. - // It is safe because - // power >= smallest_power - // and power <= largest_power - // We recover the mantissa of the power, it has a leading 1. It is always - // rounded down. - // - // We want the most significant 64 bits of the product. We know - // this will be non-zero because the most significant bit of i is - // 1. - const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power); - // Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.) - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 firstproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // Both i and power_of_five_128[index] have their most significant bit set to 1 which - // implies that the either the most or the second most significant bit of the product - // is 1. We pack values in this manner for efficiency reasons: it maximizes the use - // we make of the product. It also makes it easy to reason about the product: there - // is 0 or 1 leading zero in the product. - - // Unless the least significant 9 bits of the high (64-bit) part of the full - // product are all 1s, then we know that the most significant 55 bits are - // exact and no further work is needed. Having 55 bits is necessary because - // we need 53 bits for the mantissa but we have to have one rounding bit and - // we can waste a bit if the most significant bit of the product is zero. - if((firstproduct.high & 0x1FF) == 0x1FF) { - // We want to compute i * 5^q, but only care about the top 55 bits at most. - // Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing - // the full computation is wasteful. So we do what is called a "truncated - // multiplication". - // We take the most significant 64-bits, and we put them in - // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q - // to the desired approximation using one multiplication. Sometimes it does not suffice. - // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and - // then we get a better approximation to i * 5^q. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat - // more complicated. - // - // There is an extra layer of complexity in that we need more than 55 bits of - // accuracy in the round-to-even scenario. - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 secondproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - uint64_t lower = firstproduct.low; - uint64_t upper = firstproduct.high; - // The final mantissa should be 53 bits with a leading 1. - // We shift it so that it occupies 54 bits with a leading 1. - /////// - uint64_t upperbit = upper >> 63; - uint64_t mantissa = upper >> (upperbit + 9); - lz += int(1 ^ upperbit); - - // Here we have mantissa < (1<<54). - int64_t real_exponent = exponent - lz; - if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal? - // Here have that real_exponent <= 0 so -real_exponent >= 0 - if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. - d = 0.0; - return true; - } - // next line is safe because -real_exponent + 1 < 0 - mantissa >>= -real_exponent + 1; - // Thankfully, we can't have both "round-to-even" and subnormals because - // "round-to-even" only occurs for powers close to 0. - mantissa += (mantissa & 1); // round up - mantissa >>= 1; - // There is a weird scenario where we don't have a subnormal but just. - // Suppose we start with 2.2250738585072013e-308, we end up - // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal - // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round - // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer - // subnormal, but we can only know this after rounding. - // So we only declare a subnormal if we are smaller than the threshold. - real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1; - d = to_double(mantissa, real_exponent, negative); - return true; - } - // We have to round to even. The "to even" part - // is only a problem when we are right in between two floats - // which we guard against. - // If we have lots of trailing zeros, we may fall right between two - // floating-point values. - // - // The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54] - // times a power of two. That is, it is right between a number with binary significand - // m and another number with binary significand m+1; and it must be the case - // that it cannot be represented by a float itself. - // - // We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p. - // Recall that 10^q = 5^q * 2^q. - // When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that - // 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23. - // When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so - // (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have - // 2^{53} x 5^{-q} < 2^{64}. - // Hence we have 5^{-q} < 2^{11}$ or q>= -4. - // - // We require lower <= 1 and not lower == 0 because we could not prove that - // that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test. - if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) { - if((mantissa << (upperbit + 64 - 53 - 2)) == upper) { - mantissa &= ~1; // flip it so that we do not round up - } - } - - mantissa += mantissa & 1; - mantissa >>= 1; - - // Here we have mantissa < (1<<53), unless there was an overflow - if (mantissa >= (1ULL << 53)) { - ////////// - // This will happen when parsing values such as 7.2057594037927933e+16 - //////// - mantissa = (1ULL << 52); - real_exponent++; - } - mantissa &= ~(1ULL << 52); - // we have to check that real_exponent is in range, otherwise we bail out - if (simdjson_unlikely(real_exponent > 2046)) { - // We have an infinite value!!! We could actually throw an error here if we could. - return false; - } - d = to_double(mantissa, real_exponent, negative); - return true; -} - -// We call a fallback floating-point parser that might be slow. Note -// it will accept JSON numbers, but the JSON spec. is more restrictive so -// before you call parse_float_fallback, you need to have validated the input -// string with the JSON grammar. -// It will return an error (false) if the parsed number is infinite. -// The string parsing itself always succeeds. We know that there is at least -// one digit. -static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} -static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} - -// check quickly whether the next 8 chars are made of digits -// at a glance, it looks better than Mula's -// http://0x80.pl/articles/swar-digits-validate.html -simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) { - uint64_t val; - // this can read up to 7 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7"); - std::memcpy(&val, chars, 8); - // a branchy method might be faster: - // return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030) - // && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) == - // 0x3030303030303030); - return (((val & 0xF0F0F0F0F0F0F0F0) | - (((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) == - 0x3333333333333333); -} - -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; - } - return INVALID_NUMBER(src); -} - -template -SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later -simdjson_inline bool parse_digit(const uint8_t c, I &i) { - const uint8_t digit = static_cast(c - '0'); - if (digit > 9) { - return false; - } - // PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication - i = 10 * i + digit; // might overflow, we will handle the overflow later - return true; -} - -simdjson_inline error_code parse_decimal(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { - // we continue with the fiction that we have an integer. If the - // floating point number is representable as x * 10^z for some integer - // z that fits in 53 bits, then we will be able to convert back the - // the integer into a float in a lossless manner. - const uint8_t *const first_after_period = p; - -#ifdef SIMDJSON_SWAR_NUMBER_PARSING -#if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } -#endif // SIMDJSON_SWAR_NUMBER_PARSING -#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; -} - -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { - // Exp Sign: -123.456e[-]78 - bool neg_exp = ('-' == *p); - if (neg_exp || '+' == *p) { p++; } // Skip + as well - - // Exponent: -123.456e-[78] - auto start_exp = p; - int64_t exp_number = 0; - while (parse_digit(*p, exp_number)) { ++p; } - // It is possible for parse_digit to overflow. - // In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN. - // Thus we *must* check for possible overflow before we negate exp_number. - - // Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into - // a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may - // not oblige and may, in fact, generate two distinct paths in any case. It might be - // possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off - // instructions for a simdjson_likely branch, an unconclusive gain. - - // If there were no digits, it's an error. - if (simdjson_unlikely(p == start_exp)) { - return INVALID_NUMBER(src); - } - // We have a valid positive exponent in exp_number at this point, except that - // it may have overflowed. - - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - while (*start_exp == '0') { start_exp++; } - // 19 digits could overflow int64_t and is kind of absurd anyway. We don't - // support exponents smaller than -999,999,999,999,999,999 and bigger - // than 999,999,999,999,999,999. - // We can truncate. - // Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before - // infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could - // truncate at 324. - // Note that there is no reason to fail per se at this point in time. - // E.g., 0e999999999999999999999 is a fine number. - if (p > start_exp+18) { exp_number = 999999999999999999; } - } - // At this point, we know that exp_number is a sane, positive, signed integer. - // It is <= 999,999,999,999,999,999. As long as 'exponent' is in - // [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent' - // is bounded in magnitude by the size of the JSON input, we are fine in this universe. - // To sum it up: the next line should never overflow. - exponent += (neg_exp ? -exp_number : exp_number); - return SUCCESS; -} - -simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) { - // It is possible that the integer had an overflow. - // We have to handle the case where we have 0.0000somenumber. - const uint8_t *start = start_digits; - while ((*start == '0') || (*start == '.')) { ++start; } - // we over-decrement by one when there is a '.' - return digit_count - size_t(start - start_digits); -} - -template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { - // If we frequently had to deal with long strings of digits, - // we could extend our code by using a 128-bit integer instead - // of a 64-bit integer. However, this is uncommon in practice. - // - // 9999999999999999999 < 2**64 so we can accommodate 19 digits. - // If we have a decimal separator, then digit_count - 1 is the number of digits, but we - // may not have a decimal separator! - if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) { - // Ok, chances are good that we had an overflow! - // this is almost never going to get called!!! - // we start anew, going slowly!!! - // This will happen in the following examples: - // 10000000000000000000000000000000000000000000e+308 - // 3.1415926535897932384626433832795028841971693993751 - // - // NOTE: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. If we passed our writer reference to - // it, it would force it to be stored in memory, preventing the compiler from picking it apart - // and putting into registers. i.e. if we pass it as reference, it gets slow. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - return error; - } - // NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other - // way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331 - // To future reader: we'd love if someone found a better way, or at least could explain this result! - if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) { - // - // Important: smallest_power is such that it leads to a zero value. - // Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero - // so something x 10^-343 goes to zero, but not so with something x 10^-342. - static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough"); - // - if((exponent < simdjson::internal::smallest_power) || (i == 0)) { - WRITE_DOUBLE(0, src, writer); - return SUCCESS; - } else { // (exponent > largest_power) and (i != 0) - // We have, for sure, an infinite value and simdjson refuses to parse infinite values. - return INVALID_NUMBER(src); - } - } - double d; - if (!compute_float_64(exponent, i, negative, d)) { - // we are almost never going to get here. - if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); } - } - WRITE_DOUBLE(d, src, writer); - return SUCCESS; -} - -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING - -template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds -} - -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return ondemand::number_type::signed_integer; } -#else - -// parse the number at src -// define JSON_TEST_NUMBERS for unit testing -// -// It is assumed that the number is followed by a structural ({,},],[) character -// or a white space character. If that is not the case (e.g., when the JSON -// document is made of a single number), then it is necessary to copy the -// content and append a space before calling this function. -// -// Our objective is accurate parsing (ULP of 0) at high speed. -template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); } - - // - // Handle floats if there is a . or e (or both) - // - int64_t exponent = 0; - bool is_float = false; - if ('.' == *p) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_decimal(src, p, i, exponent) ); - digit_count = int(p - start_digits); // used later to guard against overflows - } - if (('e' == *p) || ('E' == *p)) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_exponent(src, p, exponent) ); - } - if (is_float) { - const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p); - SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) ); - if (dirty_end) { return INVALID_NUMBER(src); } - return SUCCESS; - } - - // The longest negative 64-bit number is 19 digits. - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - size_t longest_digit_count = negative ? 19 : 20; - if (digit_count > longest_digit_count) { return INVALID_NUMBER(src); } - if (digit_count == longest_digit_count) { - if (negative) { - // Anything negative above INT64_MAX+1 is invalid - if (i > uint64_t(INT64_MAX)+1) { return INVALID_NUMBER(src); } - WRITE_INTEGER(~i+1, src, writer); - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } - } - - // Write unsigned if it doesn't fit in a signed integer. - if (i > uint64_t(INT64_MAX)) { - WRITE_UNSIGNED(i, src, writer); - } else { - WRITE_INTEGER(negative ? (~i+1) : i, src, writer); - } - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; -} - -// Inlineable functions -namespace { - -// This table can be used to characterize the final character of an integer -// string. For JSON structural character and allowable white space characters, -// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise -// we return NUMBER_ERROR. -// Optimization note: we could easily reduce the size of the table by half (to 128) -// at the cost of an extra branch. -// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits): -static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast"); - -const uint8_t integer_string_finisher[256] = { - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR}; - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - - -// Parse any number from 0 to 18,446,744,073,709,551,615 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { - const uint8_t *p = src + 1; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (*p != '"') { return NUMBER_ERROR; } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - // Note: we use src[1] and not src[0] because src[0] is the quote character in this - // instance. - if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept { - // - // Check for minus sign - // - if(src == src_end) { return NUMBER_ERROR; } - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - const uint8_t *p = src + negative + 1; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(*p != '"') { return NUMBER_ERROR; } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { - return (*src == '-'); -} - -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; } - return false; -} - -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return ondemand::number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) { - return ondemand::number_type::unsigned_integer; - } - } - return ondemand::number_type::signed_integer; - } - // Hopefully, we have 'e' or 'E' or '.'. - return ondemand::number_type::floating_point_number; -} - -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept { - if(src == src_end) { return NUMBER_ERROR; } - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - if(p == src_end) { return NUMBER_ERROR; } - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while ((p != src_end) && parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely((p != src_end) && (*p == '.'))) { - p++; - const uint8_t *start_decimal_digits = p; - if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if ((p != src_end) && (*p == 'e' || *p == 'E')) { - p++; - if(p == src_end) { return NUMBER_ERROR; } - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while ((p != src_end) && parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, src_end, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - src += negative + 1; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (*p != '"') { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} -} //namespace {} -#endif // SIMDJSON_SKIPNUMBERPARSING - -} // namespace numberparsing -} // unnamed namespace -} // namespace fallback -} // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_FALLBACK_NUMBERPARSING_H -/* end file include/simdjson/fallback/numberparsing.h */ -/* begin file include/simdjson/fallback/end.h */ -/* end file include/simdjson/fallback/end.h */ - -#endif // SIMDJSON_IMPLEMENTATION_FALLBACK -#endif // SIMDJSON_FALLBACK_H -/* end file include/simdjson/fallback.h */ -/* begin file include/simdjson/icelake.h */ -#ifndef SIMDJSON_ICELAKE_H -#define SIMDJSON_ICELAKE_H - - -#if SIMDJSON_IMPLEMENTATION_ICELAKE - -#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE -#define SIMDJSON_TARGET_ICELAKE -#define SIMDJSON_UNTARGET_ICELAKE -#else -#define SIMDJSON_TARGET_ICELAKE SIMDJSON_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,pclmul,lzcnt") -#define SIMDJSON_UNTARGET_ICELAKE SIMDJSON_UNTARGET_REGION -#endif - -namespace simdjson { -/** - * Implementation for Icelake (Intel AVX512). - */ -namespace icelake { -} // namespace icelake -} // namespace simdjson - -// -// These two need to be included outside SIMDJSON_TARGET_ICELAKE -// -/* begin file include/simdjson/icelake/implementation.h */ -#ifndef SIMDJSON_ICELAKE_IMPLEMENTATION_H -#define SIMDJSON_ICELAKE_IMPLEMENTATION_H - - -// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_ICELAKE -namespace simdjson { -namespace icelake { - -using namespace simdjson; - -class implementation final : public simdjson::implementation { -public: - simdjson_inline implementation() : simdjson::implementation( - "icelake", - "Intel/AMD AVX512", - internal::instruction_set::AVX2 | internal::instruction_set::PCLMULQDQ | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 | internal::instruction_set::AVX512F | internal::instruction_set::AVX512DQ | internal::instruction_set::AVX512CD | internal::instruction_set::AVX512BW | internal::instruction_set::AVX512VL | internal::instruction_set::AVX512VBMI2 - ) {} - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t capacity, - size_t max_length, - std::unique_ptr& dst - ) const noexcept final; - simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; - simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; -}; - -} // namespace icelake -} // namespace simdjson - -#endif // SIMDJSON_ICELAKE_IMPLEMENTATION_H -/* end file include/simdjson/icelake/implementation.h */ -/* begin file include/simdjson/icelake/intrinsics.h */ -#ifndef SIMDJSON_ICELAKE_INTRINSICS_H -#define SIMDJSON_ICELAKE_INTRINSICS_H - - -#ifdef SIMDJSON_VISUAL_STUDIO -// under clang within visual studio, this will include -#include // visual studio or clang -#else -#include // elsewhere -#endif // SIMDJSON_VISUAL_STUDIO - -#ifdef SIMDJSON_CLANG_VISUAL_STUDIO -/** - * You are not supposed, normally, to include these - * headers directly. Instead you should either include intrin.h - * or x86intrin.h. However, when compiling with clang - * under Windows (i.e., when _MSC_VER is set), these headers - * only get included *if* the corresponding features are detected - * from macros: - * e.g., if __AVX2__ is set... in turn, we normally set these - * macros by compiling against the corresponding architecture - * (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole - * software with these advanced instructions. In simdjson, we - * want to compile the whole program for a generic target, - * and only target our specific kernels. As a workaround, - * we directly include the needed headers. These headers would - * normally guard against such usage, but we carefully included - * (or ) before, so the headers - * are fooled. - */ -#include // for _blsr_u64 -#include // for __lzcnt64 -#include // for most things (AVX2, AVX512, _popcnt64) -#include -#include -#include -#include -#include // for _mm_clmulepi64_si128 -// Important: we need the AVX-512 headers: -#include -#include -#include -#include -#include -#include -#include -// unfortunately, we may not get _blsr_u64, but, thankfully, clang -// has it as a macro. -#ifndef _blsr_u64 -// we roll our own -#define _blsr_u64(n) ((n - 1) & n) -#endif // _blsr_u64 -#endif // SIMDJSON_CLANG_VISUAL_STUDIO - -static_assert(sizeof(__m512i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for icelake"); - -#endif // SIMDJSON_ICELAKE_INTRINSICS_H -/* end file include/simdjson/icelake/intrinsics.h */ - -// -// The rest need to be inside the region -// -/* begin file include/simdjson/icelake/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "icelake" -// #define SIMDJSON_IMPLEMENTATION icelake -SIMDJSON_TARGET_ICELAKE -/* end file include/simdjson/icelake/begin.h */ - -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ - -namespace simdjson { -namespace icelake { - -// expectation: sizeof(open_container) = 64/8. -struct open_container { - uint32_t tape_index; // where, on the tape, does the scope ([,{) begins - uint32_t count; // how many elements in the scope -}; // struct open_container - -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); - -class dom_parser_implementation final : public internal::dom_parser_implementation { -public: - /** Tape location of each open { or [ */ - std::unique_ptr open_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr is_array{}; - /** Buffer passed to stage 1 */ - const uint8_t *buf{}; - /** Length passed to stage 1 */ - size_t len{0}; - /** Document passed to stage 2 */ - dom::document *doc{}; - - inline dom_parser_implementation() noexcept; - inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; - inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; - dom_parser_implementation(const dom_parser_implementation &) = delete; - dom_parser_implementation &operator=(const dom_parser_implementation &) = delete; - - simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final; - simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final; - simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept final; - inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final; - inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final; -private: - simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity); - -}; - -} // namespace icelake -} // namespace simdjson - -namespace simdjson { -namespace icelake { - -inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -// Leaving these here so they can be inlined if so desired -inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { - if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] ); - if (!structural_indexes) { _capacity = 0; return MEMALLOC; } - structural_indexes[0] = 0; - n_structural_indexes = 0; - - _capacity = capacity; - return SUCCESS; -} - -inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // Stage 2 stacks - open_containers.reset(new (std::nothrow) open_container[max_depth]); - is_array.reset(new (std::nothrow) bool[max_depth]); - if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; } - - _max_depth = max_depth; - return SUCCESS; -} - -} // namespace icelake -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/icelake/bitmanipulation.h */ -#ifndef SIMDJSON_ICELAKE_BITMANIPULATION_H -#define SIMDJSON_ICELAKE_BITMANIPULATION_H - -namespace simdjson { -namespace icelake { -namespace { - -// We sometimes call trailing_zero on inputs that are zero, -// but the algorithms do not end up using the returned value. -// Sadly, sanitizers are not smart enough to figure it out. -SIMDJSON_NO_SANITIZE_UNDEFINED -simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - return (int)_tzcnt_u64(input_num); -#else // SIMDJSON_REGULAR_VISUAL_STUDIO - //////// - // You might expect the next line to be equivalent to - // return (int)_tzcnt_u64(input_num); - // but the generated code differs and might be less efficient? - //////// - return __builtin_ctzll(input_num); -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO -} - -/* result might be undefined when input_num is zero */ -simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { - return _blsr_u64(input_num); -} - -/* result might be undefined when input_num is zero */ -simdjson_inline int leading_zeroes(uint64_t input_num) { - return int(_lzcnt_u64(input_num)); -} - -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO -simdjson_inline unsigned __int64 count_ones(uint64_t input_num) { - // note: we do not support legacy 32-bit Windows - return __popcnt64(input_num);// Visual Studio wants two underscores -} -#else -simdjson_inline long long int count_ones(uint64_t input_num) { - return _popcnt64(input_num); -} -#endif - -simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); -#else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); -#endif -} - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson - -#endif // SIMDJSON_ICELAKE_BITMANIPULATION_H -/* end file include/simdjson/icelake/bitmanipulation.h */ -/* begin file include/simdjson/icelake/bitmask.h */ -#ifndef SIMDJSON_ICELAKE_BITMASK_H -#define SIMDJSON_ICELAKE_BITMASK_H - -namespace simdjson { -namespace icelake { -namespace { - -// -// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered. -// -// For example, prefix_xor(00100100) == 00011100 -// -simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) { - // There should be no such thing with a processor supporting avx2 - // but not clmul. - __m128i all_ones = _mm_set1_epi8('\xFF'); - __m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0); - return _mm_cvtsi128_si64(result); -} - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson - -#endif // SIMDJSON_ICELAKE_BITMASK_H -/* end file include/simdjson/icelake/bitmask.h */ -/* begin file include/simdjson/icelake/simd.h */ -#ifndef SIMDJSON_ICELAKE_SIMD_H -#define SIMDJSON_ICELAKE_SIMD_H - - - - -#if defined(__GNUC__) && !defined(__clang__) -#if __GNUC__ == 8 -#define SIMDJSON_GCC8 1 -#endif // __GNUC__ == 8 -#endif // defined(__GNUC__) && !defined(__clang__) - -#if SIMDJSON_GCC8 -/** - * GCC 8 fails to provide _mm512_set_epi8. We roll our own. - */ -inline __m512i _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) { - return _mm512_set_epi64(uint64_t(a7) + (uint64_t(a6) << 8) + (uint64_t(a5) << 16) + (uint64_t(a4) << 24) + (uint64_t(a3) << 32) + (uint64_t(a2) << 40) + (uint64_t(a1) << 48) + (uint64_t(a0) << 56), - uint64_t(a15) + (uint64_t(a14) << 8) + (uint64_t(a13) << 16) + (uint64_t(a12) << 24) + (uint64_t(a11) << 32) + (uint64_t(a10) << 40) + (uint64_t(a9) << 48) + (uint64_t(a8) << 56), - uint64_t(a23) + (uint64_t(a22) << 8) + (uint64_t(a21) << 16) + (uint64_t(a20) << 24) + (uint64_t(a19) << 32) + (uint64_t(a18) << 40) + (uint64_t(a17) << 48) + (uint64_t(a16) << 56), - uint64_t(a31) + (uint64_t(a30) << 8) + (uint64_t(a29) << 16) + (uint64_t(a28) << 24) + (uint64_t(a27) << 32) + (uint64_t(a26) << 40) + (uint64_t(a25) << 48) + (uint64_t(a24) << 56), - uint64_t(a39) + (uint64_t(a38) << 8) + (uint64_t(a37) << 16) + (uint64_t(a36) << 24) + (uint64_t(a35) << 32) + (uint64_t(a34) << 40) + (uint64_t(a33) << 48) + (uint64_t(a32) << 56), - uint64_t(a47) + (uint64_t(a46) << 8) + (uint64_t(a45) << 16) + (uint64_t(a44) << 24) + (uint64_t(a43) << 32) + (uint64_t(a42) << 40) + (uint64_t(a41) << 48) + (uint64_t(a40) << 56), - uint64_t(a55) + (uint64_t(a54) << 8) + (uint64_t(a53) << 16) + (uint64_t(a52) << 24) + (uint64_t(a51) << 32) + (uint64_t(a50) << 40) + (uint64_t(a49) << 48) + (uint64_t(a48) << 56), - uint64_t(a63) + (uint64_t(a62) << 8) + (uint64_t(a61) << 16) + (uint64_t(a60) << 24) + (uint64_t(a59) << 32) + (uint64_t(a58) << 40) + (uint64_t(a57) << 48) + (uint64_t(a56) << 56)); -} -#endif // SIMDJSON_GCC8 - - - -namespace simdjson { -namespace icelake { -namespace { -namespace simd { - - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m512i value; - - // Zero constructor - simdjson_inline base() : value{__m512i()} {} - - // Conversion from SIMD register - simdjson_inline base(const __m512i _value) : value(_value) {} - - // Conversion to SIMD register - simdjson_inline operator const __m512i&() const { return this->value; } - simdjson_inline operator __m512i&() { return this->value; } - - // Bit operations - simdjson_inline Child operator|(const Child other) const { return _mm512_or_si512(*this, other); } - simdjson_inline Child operator&(const Child other) const { return _mm512_and_si512(*this, other); } - simdjson_inline Child operator^(const Child other) const { return _mm512_xor_si512(*this, other); } - simdjson_inline Child bit_andnot(const Child other) const { return _mm512_andnot_si512(other, *this); } - simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; - - // Forward-declared so they can be used by splat and friends. - template - struct simd8; - - template> - struct base8: base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; - - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m512i _value) : base>(_value) {} - - friend simdjson_really_inline uint64_t operator==(const simd8 lhs, const simd8 rhs) { - return _mm512_cmpeq_epi8_mask(lhs, rhs); - } - - static const int SIZE = sizeof(base::value); - - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { -#if SIMDJSON_GCC8 - // workaround for compilers unable to figure out that 16 - N is a constant (GCC 8) - constexpr int shift = 16 - N; - return _mm512_alignr_epi8(*this, _mm512_permutex2var_epi64(prev_chunk, _mm512_set_epi64(13, 12, 11, 10, 9, 8, 7, 6), *this), shift); -#else - return _mm512_alignr_epi8(*this, _mm512_permutex2var_epi64(prev_chunk, _mm512_set_epi64(13, 12, 11, 10, 9, 8, 7, 6), *this), 16 - N); -#endif - } - }; - - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8: base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); } - - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m512i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); } - simdjson_inline simd8 operator~() const { return *this ^ true; } - }; - - template - struct base8_numeric: base8 { - static simdjson_inline simd8 splat(T _value) { return _mm512_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm512_setzero_si512(); } - static simdjson_inline simd8 load(const T values[64]) { - return _mm512_loadu_si512(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 repeat_16( - T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7, - T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m512i _value) : base8(_value) {} - - // Store to array - simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); } - - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm512_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm512_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm512_shuffle_epi8(lookup_table, *this); - } - - // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset). - // Passing a 0 value for mask would be equivalent to writing out every byte to output. - // Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes - // get written. - // Design consideration: it seems like a function with the - // signature simd8 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint64_t mask, L * output) const { - _mm512_mask_compressstoreu_epi8 (output,~mask,*this); - } - - template - simdjson_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; - - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_value) {} - // Splat constructor - simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const int8_t values[64]) : simd8(load(values)) {} - // Member-by-member initialization - simdjson_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15, - int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23, - int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31, - int8_t v32, int8_t v33, int8_t v34, int8_t v35, int8_t v36, int8_t v37, int8_t v38, int8_t v39, - int8_t v40, int8_t v41, int8_t v42, int8_t v43, int8_t v44, int8_t v45, int8_t v46, int8_t v47, - int8_t v48, int8_t v49, int8_t v50, int8_t v51, int8_t v52, int8_t v53, int8_t v54, int8_t v55, - int8_t v56, int8_t v57, int8_t v58, int8_t v59, int8_t v60, int8_t v61, int8_t v62, int8_t v63 - ) : simd8(_mm512_set_epi8( - v63, v62, v61, v60, v59, v58, v57, v56, - v55, v54, v53, v52, v51, v50, v49, v48, - v47, v46, v45, v44, v43, v42, v41, v40, - v39, v38, v37, v36, v35, v34, v33, v32, - v31, v30, v29, v28, v27, v26, v25, v24, - v23, v22, v21, v20, v19, v18, v17, v16, - v15, v14, v13, v12, v11, v10, v9, v8, - v7, v6, v5, v4, v3, v2, v1, v0 - )) {} - - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 repeat_16( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epi8(*this, other); } - - simdjson_inline simd8 operator>(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); } - }; - - // Unsigned bytes - template<> - struct simd8: base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m512i _value) : base8_numeric(_value) {} - // Splat constructor - simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const uint8_t values[64]) : simd8(load(values)) {} - // Member-by-member initialization - simdjson_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15, - uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23, - uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31, - uint8_t v32, uint8_t v33, uint8_t v34, uint8_t v35, uint8_t v36, uint8_t v37, uint8_t v38, uint8_t v39, - uint8_t v40, uint8_t v41, uint8_t v42, uint8_t v43, uint8_t v44, uint8_t v45, uint8_t v46, uint8_t v47, - uint8_t v48, uint8_t v49, uint8_t v50, uint8_t v51, uint8_t v52, uint8_t v53, uint8_t v54, uint8_t v55, - uint8_t v56, uint8_t v57, uint8_t v58, uint8_t v59, uint8_t v60, uint8_t v61, uint8_t v62, uint8_t v63 - ) : simd8(_mm512_set_epi8( - v63, v62, v61, v60, v59, v58, v57, v56, - v55, v54, v53, v52, v51, v50, v49, v48, - v47, v46, v45, v44, v43, v42, v41, v40, - v39, v38, v37, v36, v35, v34, v33, v32, - v31, v30, v29, v28, v27, v26, v25, v24, - v23, v22, v21, v20, v19, v18, v17, v16, - v15, v14, v13, v12, v11, v10, v9, v8, - v7, v6, v5, v4, v3, v2, v1, v0 - )) {} - - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 repeat_16( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm512_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm512_subs_epu8(*this, other); } - - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm512_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm512_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline uint64_t operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline uint64_t operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - - simdjson_inline bool is_ascii() const { return _mm512_movepi8_mask(*this) == 0; } - simdjson_inline bool bits_not_set_anywhere() const { - return !_mm512_test_epi8_mask(*this, *this); - } - simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); } - simdjson_inline bool bits_not_set_anywhere(simd8 bits) const { return !_mm512_test_epi8_mask(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_mm512_slli_epi16(*this, N)) & uint8_t(0xFFu << N); } - // Get one of the bits and make a bitmask out of it. - // e.g. value.get_bit<7>() gets the high bit - template - simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); } - }; - - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} - simdjson_inline simd8x64(const simd8 chunk0) : chunks{chunk0} {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr)} {} - - simdjson_inline uint64_t compress(uint64_t mask, T * output) const { - this->chunks[0].compress(mask, output); - return 64 - count_ones(mask); - } - - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr+sizeof(simd8)*0); - } - - simdjson_inline simd8 reduce_or() const { - return this->chunks[0]; - } - - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask - ); - } - - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] == mask; - } - - simdjson_inline uint64_t eq(const simd8x64 &other) const { - return this->chunks[0] == other.chunks[0]; - } - - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return this->chunks[0] <= mask; - } - }; // struct simd8x64 - -} // namespace simd - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson - -#endif // SIMDJSON_ICELAKE_SIMD_H -/* end file include/simdjson/icelake/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ - -namespace simdjson { -namespace icelake { -namespace { -namespace jsoncharutils { - -// return non-zero if not a structural or whitespace char -// zero otherwise -simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace_negated[c]; -} - -simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; -} - -// returns a value with the high 16 bits set if not valid -// otherwise returns the conversion of the 4 hex digits at src into the bottom -// 16 bits of the 32-bit return register -// -// see -// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ -static inline uint32_t hex_to_u32_nocheck( - const uint8_t *src) { // strictly speaking, static inline is a C-ism - uint32_t v1 = internal::digit_to_val32[630 + src[0]]; - uint32_t v2 = internal::digit_to_val32[420 + src[1]]; - uint32_t v3 = internal::digit_to_val32[210 + src[2]]; - uint32_t v4 = internal::digit_to_val32[0 + src[3]]; - return v1 | v2 | v3 | v4; -} - -// given a code point cp, writes to c -// the utf-8 code, outputting the length in -// bytes, if the length is zero, the code point -// is invalid -// -// This can possibly be made faster using pdep -// and clz and table lookups, but JSON documents -// have few escaped code points, and the following -// function looks cheap. -// -// Note: we assume that surrogates are treated separately -// -simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { - if (cp <= 0x7F) { - c[0] = uint8_t(cp); - return 1; // ascii - } - if (cp <= 0x7FF) { - c[0] = uint8_t((cp >> 6) + 192); - c[1] = uint8_t((cp & 63) + 128); - return 2; // universal plane - // Surrogates are treated elsewhere... - //} //else if (0xd800 <= cp && cp <= 0xdfff) { - // return 0; // surrogates // could put assert here - } else if (cp <= 0xFFFF) { - c[0] = uint8_t((cp >> 12) + 224); - c[1] = uint8_t(((cp >> 6) & 63) + 128); - c[2] = uint8_t((cp & 63) + 128); - return 3; - } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this - // is not needed - c[0] = uint8_t((cp >> 18) + 240); - c[1] = uint8_t(((cp >> 12) & 63) + 128); - c[2] = uint8_t(((cp >> 6) & 63) + 128); - c[3] = uint8_t((cp & 63) + 128); - return 4; - } - // will return 0 when the code point was too large. - return 0; // bad r -} - -#ifdef SIMDJSON_IS_32BITS // _umul128 for x86, arm -// this is a slow emulation routine for 32-bit -// -static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { - return x * (uint64_t)y; -} -static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { - uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); - uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); - uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); - uint64_t adbc_carry = !!(adbc < ad); - uint64_t lo = bd + (adbc << 32); - *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + - (adbc_carry << 32) + !!(lo < bd); - return lo; -} -#endif - -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) -#ifdef _M_ARM64 - // ARM64 has native support for 64-bit multiplications, no need to emultate - answer.high = __umulh(value1, value2); - answer.low = value1 * value2; -#else - answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64 -#endif // _M_ARM64 -#else // defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) - __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; - answer.low = uint64_t(r); - answer.high = uint64_t(r >> 64); -#endif - return answer; -} - -} // namespace jsoncharutils -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace icelake { -namespace { -/// @private -namespace atomparsing { - -// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. -// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot -// be certain that the character pointer will be properly aligned. -// You might think that using memcpy makes this function expensive, but you'd be wrong. -// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); -// to the compile-time constant 1936482662. -simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } - - -// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive. -// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about. -simdjson_warn_unused -simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) { - uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++) - static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes"); - std::memcpy(&srcval, src, sizeof(uint32_t)); - return srcval ^ string_to_uint32(atom); -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src) { - return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_true_atom(src); } - else if (len == 4) { return !str4ncmp(src, "true"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src) { - return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) { - if (len > 5) { return is_valid_false_atom(src); } - else if (len == 5) { return !str4ncmp(src+1, "alse"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src) { - return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_null_atom(src); } - else if (len == 4) { return !str4ncmp(src, "null"); } - else { return false; } -} - -} // namespace atomparsing -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/icelake/stringparsing.h */ -#ifndef SIMDJSON_ICELAKE_STRINGPARSING_H -#define SIMDJSON_ICELAKE_STRINGPARSING_H - - -namespace simdjson { -namespace icelake { -namespace { - -using namespace simd; - -// Holds backslashes and quotes locations. -struct backslash_and_quote { -public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); - - simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } - simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } - simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); } - simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); } - - uint64_t bs_bits; - uint64_t quote_bits; -}; // struct backslash_and_quote - -simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { - // this can read up to 15 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes"); - simd8 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast(v == '\\'), // bs_bits - static_cast(v == '"'), // quote_bits - }; -} - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson - -#endif // SIMDJSON_ICELAKE_STRINGPARSING_H -/* end file include/simdjson/icelake/stringparsing.h */ -/* begin file include/simdjson/icelake/numberparsing.h */ -#ifndef SIMDJSON_ICELAKE_NUMBERPARSING_H -#define SIMDJSON_ICELAKE_NUMBERPARSING_H - -namespace simdjson { -namespace icelake { -namespace { - -static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { - // this actually computes *16* values so we are being wasteful. - const __m128i ascii0 = _mm_set1_epi8('0'); - const __m128i mul_1_10 = - _mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1); - const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1); - const __m128i mul_1_10000 = - _mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1); - const __m128i input = _mm_sub_epi8( - _mm_loadu_si128(reinterpret_cast(chars)), ascii0); - const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10); - const __m128i t2 = _mm_madd_epi16(t1, mul_1_100); - const __m128i t3 = _mm_packus_epi32(t2, t2); - const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000); - return _mm_cvtsi128_si32( - t4); // only captures the sum of the first 8 digits, drop the rest -} - -} // unnamed namespace -} // namespace icelake -} // namespace simdjson - -#define SIMDJSON_SWAR_NUMBER_PARSING 1 - -/* begin file include/simdjson/generic/numberparsing.h */ -#include - -namespace simdjson { -namespace icelake { - -namespace ondemand { -/** - * The type of a JSON number - */ -enum class number_type { - floating_point_number=1, /// a binary64 number - signed_integer, /// a signed integer that fits in a 64-bit word using two's complement - unsigned_integer /// a positive integer larger or equal to 1<<63 -}; -} - -namespace { -/// @private -namespace numberparsing { - - - -#ifdef JSON_TEST_NUMBERS -#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) -#else -#define INVALID_NUMBER(SRC) (NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE)) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE)) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE)) -#endif - -namespace { -// Convert a mantissa, an exponent and a sign bit into an ieee64 double. -// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable). -// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed. -simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) { - double d; - mantissa &= ~(1ULL << 52); - mantissa |= real_exponent << 52; - mantissa |= ((static_cast(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; -} -} -// Attempts to compute i * 10^(power) exactly; and if "negative" is -// true, negate the result. -// This function will only work in some cases, when it does not work, success is -// set to false. This should work *most of the time* (like 99% of the time). -// We assume that power is in the [smallest_power, -// largest_power] interval: the caller is responsible for this check. -simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) { - // we start with a fast path - // It was described in - // Clinger WD. How to read floating point numbers accurately. - // ACM SIGPLAN Notices. 1990 -#ifndef FLT_EVAL_METHOD -#error "FLT_EVAL_METHOD should be defined, please include cfloat." -#endif -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - // We cannot be certain that x/y is rounded to nearest. - if (0 <= power && power <= 22 && i <= 9007199254740991) { -#else - if (-22 <= power && power <= 22 && i <= 9007199254740991) { -#endif - // convert the integer into a double. This is lossless since - // 0 <= i <= 2^53 - 1. - d = double(i); - // - // The general idea is as follows. - // If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then - // 1) Both s and p can be represented exactly as 64-bit floating-point - // values - // (binary64). - // 2) Because s and p can be represented exactly as floating-point values, - // then s * p - // and s / p will produce correctly rounded values. - // - if (power < 0) { - d = d / simdjson::internal::power_of_ten[-power]; - } else { - d = d * simdjson::internal::power_of_ten[power]; - } - if (negative) { - d = -d; - } - return true; - } - // When 22 < power && power < 22 + 16, we could - // hope for another, secondary fast path. It was - // described by David M. Gay in "Correctly rounded - // binary-decimal and decimal-binary conversions." (1990) - // If you need to compute i * 10^(22 + x) for x < 16, - // first compute i * 10^x, if you know that result is exact - // (e.g., when i * 10^x < 2^53), - // then you can still proceed and do (i * 10^x) * 10^22. - // Is this worth your time? - // You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53) - // for this second fast path to work. - // If you you have 22 < power *and* power < 22 + 16, and then you - // optimistically compute "i * 10^(x-22)", there is still a chance that you - // have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of - // this optimization maybe less common than we would like. Source: - // http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - // also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html - - // The fast path has now failed, so we are failing back on the slower path. - - // In the slow path, we need to adjust i so that it is > 1<<63 which is always - // possible, except if i == 0, so we handle i == 0 separately. - if(i == 0) { - d = 0.0; - return true; - } - - - // The exponent is 1024 + 63 + power - // + floor(log(5**power)/log(2)). - // The 1024 comes from the ieee64 standard. - // The 63 comes from the fact that we use a 64-bit word. - // - // Computing floor(log(5**power)/log(2)) could be - // slow. Instead we use a fast function. - // - // For power in (-400,350), we have that - // (((152170 + 65536) * power ) >> 16); - // is equal to - // floor(log(5**power)/log(2)) + power when power >= 0 - // and it is equal to - // ceil(log(5**-power)/log(2)) + power when power < 0 - // - // The 65536 is (1<<16) and corresponds to - // (65536 * power) >> 16 ---> power - // - // ((152170 * power ) >> 16) is equal to - // floor(log(5**power)/log(2)) - // - // Note that this is not magic: 152170/(1<<16) is - // approximatively equal to log(5)/log(2). - // The 1<<16 value is a power of two; we could use a - // larger power of 2 if we wanted to. - // - int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63; - - - // We want the most significant bit of i to be 1. Shift if needed. - int lz = leading_zeroes(i); - i <<= lz; - - - // We are going to need to do some 64-bit arithmetic to get a precise product. - // We use a table lookup approach. - // It is safe because - // power >= smallest_power - // and power <= largest_power - // We recover the mantissa of the power, it has a leading 1. It is always - // rounded down. - // - // We want the most significant 64 bits of the product. We know - // this will be non-zero because the most significant bit of i is - // 1. - const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power); - // Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.) - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 firstproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // Both i and power_of_five_128[index] have their most significant bit set to 1 which - // implies that the either the most or the second most significant bit of the product - // is 1. We pack values in this manner for efficiency reasons: it maximizes the use - // we make of the product. It also makes it easy to reason about the product: there - // is 0 or 1 leading zero in the product. - - // Unless the least significant 9 bits of the high (64-bit) part of the full - // product are all 1s, then we know that the most significant 55 bits are - // exact and no further work is needed. Having 55 bits is necessary because - // we need 53 bits for the mantissa but we have to have one rounding bit and - // we can waste a bit if the most significant bit of the product is zero. - if((firstproduct.high & 0x1FF) == 0x1FF) { - // We want to compute i * 5^q, but only care about the top 55 bits at most. - // Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing - // the full computation is wasteful. So we do what is called a "truncated - // multiplication". - // We take the most significant 64-bits, and we put them in - // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q - // to the desired approximation using one multiplication. Sometimes it does not suffice. - // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and - // then we get a better approximation to i * 5^q. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat - // more complicated. - // - // There is an extra layer of complexity in that we need more than 55 bits of - // accuracy in the round-to-even scenario. - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 secondproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - uint64_t lower = firstproduct.low; - uint64_t upper = firstproduct.high; - // The final mantissa should be 53 bits with a leading 1. - // We shift it so that it occupies 54 bits with a leading 1. - /////// - uint64_t upperbit = upper >> 63; - uint64_t mantissa = upper >> (upperbit + 9); - lz += int(1 ^ upperbit); - - // Here we have mantissa < (1<<54). - int64_t real_exponent = exponent - lz; - if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal? - // Here have that real_exponent <= 0 so -real_exponent >= 0 - if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. - d = 0.0; - return true; - } - // next line is safe because -real_exponent + 1 < 0 - mantissa >>= -real_exponent + 1; - // Thankfully, we can't have both "round-to-even" and subnormals because - // "round-to-even" only occurs for powers close to 0. - mantissa += (mantissa & 1); // round up - mantissa >>= 1; - // There is a weird scenario where we don't have a subnormal but just. - // Suppose we start with 2.2250738585072013e-308, we end up - // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal - // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round - // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer - // subnormal, but we can only know this after rounding. - // So we only declare a subnormal if we are smaller than the threshold. - real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1; - d = to_double(mantissa, real_exponent, negative); - return true; - } - // We have to round to even. The "to even" part - // is only a problem when we are right in between two floats - // which we guard against. - // If we have lots of trailing zeros, we may fall right between two - // floating-point values. - // - // The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54] - // times a power of two. That is, it is right between a number with binary significand - // m and another number with binary significand m+1; and it must be the case - // that it cannot be represented by a float itself. - // - // We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p. - // Recall that 10^q = 5^q * 2^q. - // When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that - // 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23. - // When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so - // (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have - // 2^{53} x 5^{-q} < 2^{64}. - // Hence we have 5^{-q} < 2^{11}$ or q>= -4. - // - // We require lower <= 1 and not lower == 0 because we could not prove that - // that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test. - if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) { - if((mantissa << (upperbit + 64 - 53 - 2)) == upper) { - mantissa &= ~1; // flip it so that we do not round up - } - } - - mantissa += mantissa & 1; - mantissa >>= 1; - - // Here we have mantissa < (1<<53), unless there was an overflow - if (mantissa >= (1ULL << 53)) { - ////////// - // This will happen when parsing values such as 7.2057594037927933e+16 - //////// - mantissa = (1ULL << 52); - real_exponent++; - } - mantissa &= ~(1ULL << 52); - // we have to check that real_exponent is in range, otherwise we bail out - if (simdjson_unlikely(real_exponent > 2046)) { - // We have an infinite value!!! We could actually throw an error here if we could. - return false; - } - d = to_double(mantissa, real_exponent, negative); - return true; -} - -// We call a fallback floating-point parser that might be slow. Note -// it will accept JSON numbers, but the JSON spec. is more restrictive so -// before you call parse_float_fallback, you need to have validated the input -// string with the JSON grammar. -// It will return an error (false) if the parsed number is infinite. -// The string parsing itself always succeeds. We know that there is at least -// one digit. -static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} -static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} - -// check quickly whether the next 8 chars are made of digits -// at a glance, it looks better than Mula's -// http://0x80.pl/articles/swar-digits-validate.html -simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) { - uint64_t val; - // this can read up to 7 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7"); - std::memcpy(&val, chars, 8); - // a branchy method might be faster: - // return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030) - // && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) == - // 0x3030303030303030); - return (((val & 0xF0F0F0F0F0F0F0F0) | - (((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) == - 0x3333333333333333); -} - -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; - } - return INVALID_NUMBER(src); -} - -template -SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later -simdjson_inline bool parse_digit(const uint8_t c, I &i) { - const uint8_t digit = static_cast(c - '0'); - if (digit > 9) { - return false; - } - // PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication - i = 10 * i + digit; // might overflow, we will handle the overflow later - return true; -} - -simdjson_inline error_code parse_decimal(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { - // we continue with the fiction that we have an integer. If the - // floating point number is representable as x * 10^z for some integer - // z that fits in 53 bits, then we will be able to convert back the - // the integer into a float in a lossless manner. - const uint8_t *const first_after_period = p; - -#ifdef SIMDJSON_SWAR_NUMBER_PARSING -#if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } -#endif // SIMDJSON_SWAR_NUMBER_PARSING -#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; -} - -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { - // Exp Sign: -123.456e[-]78 - bool neg_exp = ('-' == *p); - if (neg_exp || '+' == *p) { p++; } // Skip + as well - - // Exponent: -123.456e-[78] - auto start_exp = p; - int64_t exp_number = 0; - while (parse_digit(*p, exp_number)) { ++p; } - // It is possible for parse_digit to overflow. - // In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN. - // Thus we *must* check for possible overflow before we negate exp_number. - - // Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into - // a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may - // not oblige and may, in fact, generate two distinct paths in any case. It might be - // possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off - // instructions for a simdjson_likely branch, an unconclusive gain. - - // If there were no digits, it's an error. - if (simdjson_unlikely(p == start_exp)) { - return INVALID_NUMBER(src); - } - // We have a valid positive exponent in exp_number at this point, except that - // it may have overflowed. - - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - while (*start_exp == '0') { start_exp++; } - // 19 digits could overflow int64_t and is kind of absurd anyway. We don't - // support exponents smaller than -999,999,999,999,999,999 and bigger - // than 999,999,999,999,999,999. - // We can truncate. - // Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before - // infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could - // truncate at 324. - // Note that there is no reason to fail per se at this point in time. - // E.g., 0e999999999999999999999 is a fine number. - if (p > start_exp+18) { exp_number = 999999999999999999; } - } - // At this point, we know that exp_number is a sane, positive, signed integer. - // It is <= 999,999,999,999,999,999. As long as 'exponent' is in - // [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent' - // is bounded in magnitude by the size of the JSON input, we are fine in this universe. - // To sum it up: the next line should never overflow. - exponent += (neg_exp ? -exp_number : exp_number); - return SUCCESS; -} - -simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) { - // It is possible that the integer had an overflow. - // We have to handle the case where we have 0.0000somenumber. - const uint8_t *start = start_digits; - while ((*start == '0') || (*start == '.')) { ++start; } - // we over-decrement by one when there is a '.' - return digit_count - size_t(start - start_digits); -} - -template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { - // If we frequently had to deal with long strings of digits, - // we could extend our code by using a 128-bit integer instead - // of a 64-bit integer. However, this is uncommon in practice. - // - // 9999999999999999999 < 2**64 so we can accommodate 19 digits. - // If we have a decimal separator, then digit_count - 1 is the number of digits, but we - // may not have a decimal separator! - if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) { - // Ok, chances are good that we had an overflow! - // this is almost never going to get called!!! - // we start anew, going slowly!!! - // This will happen in the following examples: - // 10000000000000000000000000000000000000000000e+308 - // 3.1415926535897932384626433832795028841971693993751 - // - // NOTE: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. If we passed our writer reference to - // it, it would force it to be stored in memory, preventing the compiler from picking it apart - // and putting into registers. i.e. if we pass it as reference, it gets slow. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - return error; - } - // NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other - // way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331 - // To future reader: we'd love if someone found a better way, or at least could explain this result! - if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) { - // - // Important: smallest_power is such that it leads to a zero value. - // Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero - // so something x 10^-343 goes to zero, but not so with something x 10^-342. - static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough"); - // - if((exponent < simdjson::internal::smallest_power) || (i == 0)) { - WRITE_DOUBLE(0, src, writer); - return SUCCESS; - } else { // (exponent > largest_power) and (i != 0) - // We have, for sure, an infinite value and simdjson refuses to parse infinite values. - return INVALID_NUMBER(src); - } - } - double d; - if (!compute_float_64(exponent, i, negative, d)) { - // we are almost never going to get here. - if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); } - } - WRITE_DOUBLE(d, src, writer); - return SUCCESS; -} - -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING - -template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds -} - -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return ondemand::number_type::signed_integer; } -#else - -// parse the number at src -// define JSON_TEST_NUMBERS for unit testing -// -// It is assumed that the number is followed by a structural ({,},],[) character -// or a white space character. If that is not the case (e.g., when the JSON -// document is made of a single number), then it is necessary to copy the -// content and append a space before calling this function. -// -// Our objective is accurate parsing (ULP of 0) at high speed. -template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); } - - // - // Handle floats if there is a . or e (or both) - // - int64_t exponent = 0; - bool is_float = false; - if ('.' == *p) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_decimal(src, p, i, exponent) ); - digit_count = int(p - start_digits); // used later to guard against overflows - } - if (('e' == *p) || ('E' == *p)) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_exponent(src, p, exponent) ); - } - if (is_float) { - const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p); - SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) ); - if (dirty_end) { return INVALID_NUMBER(src); } - return SUCCESS; - } - - // The longest negative 64-bit number is 19 digits. - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - size_t longest_digit_count = negative ? 19 : 20; - if (digit_count > longest_digit_count) { return INVALID_NUMBER(src); } - if (digit_count == longest_digit_count) { - if (negative) { - // Anything negative above INT64_MAX+1 is invalid - if (i > uint64_t(INT64_MAX)+1) { return INVALID_NUMBER(src); } - WRITE_INTEGER(~i+1, src, writer); - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } - } - - // Write unsigned if it doesn't fit in a signed integer. - if (i > uint64_t(INT64_MAX)) { - WRITE_UNSIGNED(i, src, writer); - } else { - WRITE_INTEGER(negative ? (~i+1) : i, src, writer); - } - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; -} - -// Inlineable functions -namespace { - -// This table can be used to characterize the final character of an integer -// string. For JSON structural character and allowable white space characters, -// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise -// we return NUMBER_ERROR. -// Optimization note: we could easily reduce the size of the table by half (to 128) -// at the cost of an extra branch. -// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits): -static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast"); - -const uint8_t integer_string_finisher[256] = { - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR}; - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - - -// Parse any number from 0 to 18,446,744,073,709,551,615 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { - const uint8_t *p = src + 1; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (*p != '"') { return NUMBER_ERROR; } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - // Note: we use src[1] and not src[0] because src[0] is the quote character in this - // instance. - if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept { - // - // Check for minus sign - // - if(src == src_end) { return NUMBER_ERROR; } - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - const uint8_t *p = src + negative + 1; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(*p != '"') { return NUMBER_ERROR; } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { - return (*src == '-'); -} - -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; } - return false; -} - -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return ondemand::number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) { - return ondemand::number_type::unsigned_integer; - } - } - return ondemand::number_type::signed_integer; - } - // Hopefully, we have 'e' or 'E' or '.'. - return ondemand::number_type::floating_point_number; -} - -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept { - if(src == src_end) { return NUMBER_ERROR; } - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - if(p == src_end) { return NUMBER_ERROR; } - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while ((p != src_end) && parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely((p != src_end) && (*p == '.'))) { - p++; - const uint8_t *start_decimal_digits = p; - if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if ((p != src_end) && (*p == 'e' || *p == 'E')) { - p++; - if(p == src_end) { return NUMBER_ERROR; } - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while ((p != src_end) && parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, src_end, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - src += negative + 1; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (*p != '"') { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} -} //namespace {} -#endif // SIMDJSON_SKIPNUMBERPARSING - -} // namespace numberparsing -} // unnamed namespace -} // namespace icelake -} // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_ICELAKE_NUMBERPARSING_H -/* end file include/simdjson/icelake/numberparsing.h */ -/* begin file include/simdjson/icelake/end.h */ -SIMDJSON_UNTARGET_ICELAKE -/* end file include/simdjson/icelake/end.h */ - -#endif // SIMDJSON_IMPLEMENTATION_ICELAKE -#endif // SIMDJSON_ICELAKE_H -/* end file include/simdjson/icelake.h */ -/* begin file include/simdjson/haswell.h */ -#ifndef SIMDJSON_HASWELL_H -#define SIMDJSON_HASWELL_H - - -#if SIMDJSON_IMPLEMENTATION_HASWELL - -#if SIMDJSON_CAN_ALWAYS_RUN_HASWELL -#define SIMDJSON_TARGET_HASWELL -#define SIMDJSON_UNTARGET_HASWELL -#else -#define SIMDJSON_TARGET_HASWELL SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt") -#define SIMDJSON_UNTARGET_HASWELL SIMDJSON_UNTARGET_REGION -#endif - -namespace simdjson { -/** - * Implementation for Haswell (Intel AVX2). - */ -namespace haswell { -} // namespace haswell -} // namespace simdjson - -// -// These two need to be included outside SIMDJSON_TARGET_HASWELL -// -/* begin file include/simdjson/haswell/implementation.h */ -#ifndef SIMDJSON_HASWELL_IMPLEMENTATION_H -#define SIMDJSON_HASWELL_IMPLEMENTATION_H - - -// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_HASWELL -namespace simdjson { -namespace haswell { - -using namespace simdjson; - -class implementation final : public simdjson::implementation { -public: - simdjson_inline implementation() : simdjson::implementation( - "haswell", - "Intel/AMD AVX2", - internal::instruction_set::AVX2 | internal::instruction_set::PCLMULQDQ | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 - ) {} - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t capacity, - size_t max_length, - std::unique_ptr& dst - ) const noexcept final; - simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; - simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; -}; - -} // namespace haswell -} // namespace simdjson - -#endif // SIMDJSON_HASWELL_IMPLEMENTATION_H -/* end file include/simdjson/haswell/implementation.h */ -/* begin file include/simdjson/haswell/intrinsics.h */ -#ifndef SIMDJSON_HASWELL_INTRINSICS_H -#define SIMDJSON_HASWELL_INTRINSICS_H - - -#ifdef SIMDJSON_VISUAL_STUDIO -// under clang within visual studio, this will include -#include // visual studio or clang -#else -#include // elsewhere -#endif // SIMDJSON_VISUAL_STUDIO - -#ifdef SIMDJSON_CLANG_VISUAL_STUDIO -/** - * You are not supposed, normally, to include these - * headers directly. Instead you should either include intrin.h - * or x86intrin.h. However, when compiling with clang - * under Windows (i.e., when _MSC_VER is set), these headers - * only get included *if* the corresponding features are detected - * from macros: - * e.g., if __AVX2__ is set... in turn, we normally set these - * macros by compiling against the corresponding architecture - * (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole - * software with these advanced instructions. In simdjson, we - * want to compile the whole program for a generic target, - * and only target our specific kernels. As a workaround, - * we directly include the needed headers. These headers would - * normally guard against such usage, but we carefully included - * (or ) before, so the headers - * are fooled. - */ -#include // for _blsr_u64 -#include // for __lzcnt64 -#include // for most things (AVX2, AVX512, _popcnt64) -#include -#include -#include -#include -#include // for _mm_clmulepi64_si128 -// unfortunately, we may not get _blsr_u64, but, thankfully, clang -// has it as a macro. -#ifndef _blsr_u64 -// we roll our own -#define _blsr_u64(n) ((n - 1) & n) -#endif // _blsr_u64 -#endif // SIMDJSON_CLANG_VISUAL_STUDIO - -static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for haswell kernel."); - -#endif // SIMDJSON_HASWELL_INTRINSICS_H -/* end file include/simdjson/haswell/intrinsics.h */ - -// -// The rest need to be inside the region -// -/* begin file include/simdjson/haswell/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "haswell" -// #define SIMDJSON_IMPLEMENTATION haswell -SIMDJSON_TARGET_HASWELL -/* end file include/simdjson/haswell/begin.h */ - -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ - -namespace simdjson { -namespace haswell { - -// expectation: sizeof(open_container) = 64/8. -struct open_container { - uint32_t tape_index; // where, on the tape, does the scope ([,{) begins - uint32_t count; // how many elements in the scope -}; // struct open_container - -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); - -class dom_parser_implementation final : public internal::dom_parser_implementation { -public: - /** Tape location of each open { or [ */ - std::unique_ptr open_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr is_array{}; - /** Buffer passed to stage 1 */ - const uint8_t *buf{}; - /** Length passed to stage 1 */ - size_t len{0}; - /** Document passed to stage 2 */ - dom::document *doc{}; - - inline dom_parser_implementation() noexcept; - inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; - inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; - dom_parser_implementation(const dom_parser_implementation &) = delete; - dom_parser_implementation &operator=(const dom_parser_implementation &) = delete; - - simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final; - simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final; - simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept final; - inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final; - inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final; -private: - simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity); - -}; - -} // namespace haswell -} // namespace simdjson - -namespace simdjson { -namespace haswell { - -inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -// Leaving these here so they can be inlined if so desired -inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { - if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] ); - if (!structural_indexes) { _capacity = 0; return MEMALLOC; } - structural_indexes[0] = 0; - n_structural_indexes = 0; - - _capacity = capacity; - return SUCCESS; -} - -inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // Stage 2 stacks - open_containers.reset(new (std::nothrow) open_container[max_depth]); - is_array.reset(new (std::nothrow) bool[max_depth]); - if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; } - - _max_depth = max_depth; - return SUCCESS; -} - -} // namespace haswell -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/haswell/bitmanipulation.h */ -#ifndef SIMDJSON_HASWELL_BITMANIPULATION_H -#define SIMDJSON_HASWELL_BITMANIPULATION_H - -namespace simdjson { -namespace haswell { -namespace { - -// We sometimes call trailing_zero on inputs that are zero, -// but the algorithms do not end up using the returned value. -// Sadly, sanitizers are not smart enough to figure it out. -SIMDJSON_NO_SANITIZE_UNDEFINED -simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - return (int)_tzcnt_u64(input_num); -#else // SIMDJSON_REGULAR_VISUAL_STUDIO - //////// - // You might expect the next line to be equivalent to - // return (int)_tzcnt_u64(input_num); - // but the generated code differs and might be less efficient? - //////// - return __builtin_ctzll(input_num); -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO -} - -/* result might be undefined when input_num is zero */ -simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { - return _blsr_u64(input_num); -} - -/* result might be undefined when input_num is zero */ -simdjson_inline int leading_zeroes(uint64_t input_num) { - return int(_lzcnt_u64(input_num)); -} - -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO -simdjson_inline unsigned __int64 count_ones(uint64_t input_num) { - // note: we do not support legacy 32-bit Windows - return __popcnt64(input_num);// Visual Studio wants two underscores -} -#else -simdjson_inline long long int count_ones(uint64_t input_num) { - return _popcnt64(input_num); -} -#endif - -simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); -#else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); -#endif -} - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson - -#endif // SIMDJSON_HASWELL_BITMANIPULATION_H -/* end file include/simdjson/haswell/bitmanipulation.h */ -/* begin file include/simdjson/haswell/bitmask.h */ -#ifndef SIMDJSON_HASWELL_BITMASK_H -#define SIMDJSON_HASWELL_BITMASK_H - -namespace simdjson { -namespace haswell { -namespace { - -// -// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered. -// -// For example, prefix_xor(00100100) == 00011100 -// -simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) { - // There should be no such thing with a processor supporting avx2 - // but not clmul. - __m128i all_ones = _mm_set1_epi8('\xFF'); - __m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0); - return _mm_cvtsi128_si64(result); -} - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson - -#endif // SIMDJSON_HASWELL_BITMASK_H -/* end file include/simdjson/haswell/bitmask.h */ -/* begin file include/simdjson/haswell/simd.h */ -#ifndef SIMDJSON_HASWELL_SIMD_H -#define SIMDJSON_HASWELL_SIMD_H - - -namespace simdjson { -namespace haswell { -namespace { -namespace simd { - - // Forward-declared so they can be used by splat and friends. - template - struct base { - __m256i value; - - // Zero constructor - simdjson_inline base() : value{__m256i()} {} - - // Conversion from SIMD register - simdjson_inline base(const __m256i _value) : value(_value) {} - - // Conversion to SIMD register - simdjson_inline operator const __m256i&() const { return this->value; } - simdjson_inline operator __m256i&() { return this->value; } - - // Bit operations - simdjson_inline Child operator|(const Child other) const { return _mm256_or_si256(*this, other); } - simdjson_inline Child operator&(const Child other) const { return _mm256_and_si256(*this, other); } - simdjson_inline Child operator^(const Child other) const { return _mm256_xor_si256(*this, other); } - simdjson_inline Child bit_andnot(const Child other) const { return _mm256_andnot_si256(other, *this); } - simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; - - // Forward-declared so they can be used by splat and friends. - template - struct simd8; - - template> - struct base8: base> { - typedef uint32_t bitmask_t; - typedef uint64_t bitmask2_t; - - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m256i _value) : base>(_value) {} - - friend simdjson_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm256_cmpeq_epi8(lhs, rhs); } - - static const int SIZE = sizeof(base::value); - - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N); - } - }; - - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8: base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); } - - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m256i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - - simdjson_inline int to_bitmask() const { return _mm256_movemask_epi8(*this); } - simdjson_inline bool any() const { return !_mm256_testz_si256(*this, *this); } - simdjson_inline simd8 operator~() const { return *this ^ true; } - }; - - template - struct base8_numeric: base8 { - static simdjson_inline simd8 splat(T _value) { return _mm256_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm256_setzero_si256(); } - static simdjson_inline simd8 load(const T values[32]) { - return _mm256_loadu_si256(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 repeat_16( - T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7, - T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m256i _value) : base8(_value) {} - - // Store to array - simdjson_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); } - - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm256_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm256_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm256_shuffle_epi8(lookup_table, *this); - } - - // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset). - // Passing a 0 value for mask would be equivalent to writing out every byte to output. - // Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes - // get written. - // Design consideration: it seems like a function with the - // signature simd8 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint32_t mask, L * output) const { - using internal::thintable_epi8; - using internal::BitsSetTable256mul2; - using internal::pshufb_combine_table; - // this particular implementation was inspired by work done by @animetosho - // we do it in four steps, first 8 bytes and then second 8 bytes... - uint8_t mask1 = uint8_t(mask); // least significant 8 bits - uint8_t mask2 = uint8_t(mask >> 8); // second least significant 8 bits - uint8_t mask3 = uint8_t(mask >> 16); // ... - uint8_t mask4 = uint8_t(mask >> 24); // ... - // next line just loads the 64-bit values thintable_epi8[mask1] and - // thintable_epi8[mask2] into a 128-bit register, using only - // two instructions on most compilers. - __m256i shufmask = _mm256_set_epi64x(thintable_epi8[mask4], thintable_epi8[mask3], - thintable_epi8[mask2], thintable_epi8[mask1]); - // we increment by 0x08 the second half of the mask and so forth - shufmask = - _mm256_add_epi8(shufmask, _mm256_set_epi32(0x18181818, 0x18181818, - 0x10101010, 0x10101010, 0x08080808, 0x08080808, 0, 0)); - // this is the version "nearly pruned" - __m256i pruned = _mm256_shuffle_epi8(*this, shufmask); - // we still need to put the pieces back together. - // we compute the popcount of the first words: - int pop1 = BitsSetTable256mul2[mask1]; - int pop3 = BitsSetTable256mul2[mask3]; - - // then load the corresponding mask - // could be done with _mm256_loadu2_m128i but many standard libraries omit this intrinsic. - __m256i v256 = _mm256_castsi128_si256( - _mm_loadu_si128(reinterpret_cast(pshufb_combine_table + pop1 * 8))); - __m256i compactmask = _mm256_insertf128_si256(v256, - _mm_loadu_si128(reinterpret_cast(pshufb_combine_table + pop3 * 8)), 1); - __m256i almostthere = _mm256_shuffle_epi8(pruned, compactmask); - // We just need to write out the result. - // This is the tricky bit that is hard to do - // if we want to return a SIMD register, since there - // is no single-instruction approach to recombine - // the two 128-bit lanes with an offset. - __m128i v128; - v128 = _mm256_castsi256_si128(almostthere); - _mm_storeu_si128( reinterpret_cast<__m128i *>(output), v128); - v128 = _mm256_extractf128_si256(almostthere, 1); - _mm_storeu_si128( reinterpret_cast<__m128i *>(output + 16 - count_ones(mask & 0xFFFF)), v128); - } - - template - simdjson_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; - - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_value) {} - // Splat constructor - simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const int8_t values[32]) : simd8(load(values)) {} - // Member-by-member initialization - simdjson_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15, - int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23, - int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31 - ) : simd8(_mm256_setr_epi8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v16,v17,v18,v19,v20,v21,v22,v23, - v24,v25,v26,v27,v28,v29,v30,v31 - )) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 repeat_16( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm256_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm256_cmpgt_epi8(other, *this); } - }; - - // Unsigned bytes - template<> - struct simd8: base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m256i _value) : base8_numeric(_value) {} - // Splat constructor - simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const uint8_t values[32]) : simd8(load(values)) {} - // Member-by-member initialization - simdjson_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15, - uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23, - uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31 - ) : simd8(_mm256_setr_epi8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v16,v17,v18,v19,v20,v21,v22,v23, - v24,v25,v26,v27,v28,v29,v30,v31 - )) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 repeat_16( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15, - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm256_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm256_subs_epu8(*this, other); } - - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm256_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm256_min_epu8(other, *this); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->lt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - simdjson_inline bool is_ascii() const { return _mm256_movemask_epi8(*this) == 0; } - simdjson_inline bool bits_not_set_anywhere() const { return _mm256_testz_si256(*this, *this); } - simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); } - simdjson_inline bool bits_not_set_anywhere(simd8 bits) const { return _mm256_testz_si256(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_mm256_slli_epi16(*this, N)) & uint8_t(0xFFu << N); } - // Get one of the bits and make a bitmask out of it. - // e.g. value.get_bit<7>() gets the high bit - template - simdjson_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); } - }; - - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1) : chunks{chunk0, chunk1} {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+32)} {} - - simdjson_inline uint64_t compress(uint64_t mask, T * output) const { - uint32_t mask1 = uint32_t(mask); - uint32_t mask2 = uint32_t(mask >> 32); - this->chunks[0].compress(mask1, output); - this->chunks[1].compress(mask2, output + 32 - count_ones(mask1)); - return 64 - count_ones(mask); - } - - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr+sizeof(simd8)*0); - this->chunks[1].store(ptr+sizeof(simd8)*1); - } - - simdjson_inline uint64_t to_bitmask() const { - uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r_hi = this->chunks[1].to_bitmask(); - return r_lo | (r_hi << 32); - } - - simdjson_inline simd8 reduce_or() const { - return this->chunks[0] | this->chunks[1]; - } - - simdjson_inline simd8x64 bit_or(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] | mask, - this->chunks[1] | mask - ); - } - - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask - ).to_bitmask(); - } - - simdjson_inline uint64_t eq(const simd8x64 &other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1] - ).to_bitmask(); - } - - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 - -} // namespace simd - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson - -#endif // SIMDJSON_HASWELL_SIMD_H -/* end file include/simdjson/haswell/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ - -namespace simdjson { -namespace haswell { -namespace { -namespace jsoncharutils { - -// return non-zero if not a structural or whitespace char -// zero otherwise -simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace_negated[c]; -} - -simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; -} - -// returns a value with the high 16 bits set if not valid -// otherwise returns the conversion of the 4 hex digits at src into the bottom -// 16 bits of the 32-bit return register -// -// see -// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ -static inline uint32_t hex_to_u32_nocheck( - const uint8_t *src) { // strictly speaking, static inline is a C-ism - uint32_t v1 = internal::digit_to_val32[630 + src[0]]; - uint32_t v2 = internal::digit_to_val32[420 + src[1]]; - uint32_t v3 = internal::digit_to_val32[210 + src[2]]; - uint32_t v4 = internal::digit_to_val32[0 + src[3]]; - return v1 | v2 | v3 | v4; -} - -// given a code point cp, writes to c -// the utf-8 code, outputting the length in -// bytes, if the length is zero, the code point -// is invalid -// -// This can possibly be made faster using pdep -// and clz and table lookups, but JSON documents -// have few escaped code points, and the following -// function looks cheap. -// -// Note: we assume that surrogates are treated separately -// -simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { - if (cp <= 0x7F) { - c[0] = uint8_t(cp); - return 1; // ascii - } - if (cp <= 0x7FF) { - c[0] = uint8_t((cp >> 6) + 192); - c[1] = uint8_t((cp & 63) + 128); - return 2; // universal plane - // Surrogates are treated elsewhere... - //} //else if (0xd800 <= cp && cp <= 0xdfff) { - // return 0; // surrogates // could put assert here - } else if (cp <= 0xFFFF) { - c[0] = uint8_t((cp >> 12) + 224); - c[1] = uint8_t(((cp >> 6) & 63) + 128); - c[2] = uint8_t((cp & 63) + 128); - return 3; - } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this - // is not needed - c[0] = uint8_t((cp >> 18) + 240); - c[1] = uint8_t(((cp >> 12) & 63) + 128); - c[2] = uint8_t(((cp >> 6) & 63) + 128); - c[3] = uint8_t((cp & 63) + 128); - return 4; - } - // will return 0 when the code point was too large. - return 0; // bad r -} - -#ifdef SIMDJSON_IS_32BITS // _umul128 for x86, arm -// this is a slow emulation routine for 32-bit -// -static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { - return x * (uint64_t)y; -} -static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { - uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); - uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); - uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); - uint64_t adbc_carry = !!(adbc < ad); - uint64_t lo = bd + (adbc << 32); - *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + - (adbc_carry << 32) + !!(lo < bd); - return lo; -} -#endif - -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) -#ifdef _M_ARM64 - // ARM64 has native support for 64-bit multiplications, no need to emultate - answer.high = __umulh(value1, value2); - answer.low = value1 * value2; -#else - answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64 -#endif // _M_ARM64 -#else // defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) - __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; - answer.low = uint64_t(r); - answer.high = uint64_t(r >> 64); -#endif - return answer; -} - -} // namespace jsoncharutils -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace haswell { -namespace { -/// @private -namespace atomparsing { - -// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. -// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot -// be certain that the character pointer will be properly aligned. -// You might think that using memcpy makes this function expensive, but you'd be wrong. -// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); -// to the compile-time constant 1936482662. -simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } - - -// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive. -// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about. -simdjson_warn_unused -simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) { - uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++) - static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes"); - std::memcpy(&srcval, src, sizeof(uint32_t)); - return srcval ^ string_to_uint32(atom); -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src) { - return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_true_atom(src); } - else if (len == 4) { return !str4ncmp(src, "true"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src) { - return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) { - if (len > 5) { return is_valid_false_atom(src); } - else if (len == 5) { return !str4ncmp(src+1, "alse"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src) { - return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_null_atom(src); } - else if (len == 4) { return !str4ncmp(src, "null"); } - else { return false; } -} - -} // namespace atomparsing -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/haswell/stringparsing.h */ -#ifndef SIMDJSON_HASWELL_STRINGPARSING_H -#define SIMDJSON_HASWELL_STRINGPARSING_H - - -namespace simdjson { -namespace haswell { -namespace { - -using namespace simd; - -// Holds backslashes and quotes locations. -struct backslash_and_quote { -public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); - - simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } - simdjson_inline bool has_backslash() { return ((quote_bits - 1) & bs_bits) != 0; } - simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); } - simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); } - - uint32_t bs_bits; - uint32_t quote_bits; -}; // struct backslash_and_quote - -simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { - // this can read up to 15 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes"); - simd8 v(src); - // store to dest unconditionally - we can overwrite the bits we don't like later - v.store(dst); - return { - static_cast((v == '\\').to_bitmask()), // bs_bits - static_cast((v == '"').to_bitmask()), // quote_bits - }; -} - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson - -#endif // SIMDJSON_HASWELL_STRINGPARSING_H -/* end file include/simdjson/haswell/stringparsing.h */ -/* begin file include/simdjson/haswell/numberparsing.h */ -#ifndef SIMDJSON_HASWELL_NUMBERPARSING_H -#define SIMDJSON_HASWELL_NUMBERPARSING_H - -namespace simdjson { -namespace haswell { -namespace { - -static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { - // this actually computes *16* values so we are being wasteful. - const __m128i ascii0 = _mm_set1_epi8('0'); - const __m128i mul_1_10 = - _mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1); - const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1); - const __m128i mul_1_10000 = - _mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1); - const __m128i input = _mm_sub_epi8( - _mm_loadu_si128(reinterpret_cast(chars)), ascii0); - const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10); - const __m128i t2 = _mm_madd_epi16(t1, mul_1_100); - const __m128i t3 = _mm_packus_epi32(t2, t2); - const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000); - return _mm_cvtsi128_si32( - t4); // only captures the sum of the first 8 digits, drop the rest -} - -} // unnamed namespace -} // namespace haswell -} // namespace simdjson - -#define SIMDJSON_SWAR_NUMBER_PARSING 1 - -/* begin file include/simdjson/generic/numberparsing.h */ -#include - -namespace simdjson { -namespace haswell { - -namespace ondemand { -/** - * The type of a JSON number - */ -enum class number_type { - floating_point_number=1, /// a binary64 number - signed_integer, /// a signed integer that fits in a 64-bit word using two's complement - unsigned_integer /// a positive integer larger or equal to 1<<63 -}; -} - -namespace { -/// @private -namespace numberparsing { - - - -#ifdef JSON_TEST_NUMBERS -#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) -#else -#define INVALID_NUMBER(SRC) (NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE)) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE)) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE)) -#endif - -namespace { -// Convert a mantissa, an exponent and a sign bit into an ieee64 double. -// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable). -// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed. -simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) { - double d; - mantissa &= ~(1ULL << 52); - mantissa |= real_exponent << 52; - mantissa |= ((static_cast(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; -} -} -// Attempts to compute i * 10^(power) exactly; and if "negative" is -// true, negate the result. -// This function will only work in some cases, when it does not work, success is -// set to false. This should work *most of the time* (like 99% of the time). -// We assume that power is in the [smallest_power, -// largest_power] interval: the caller is responsible for this check. -simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) { - // we start with a fast path - // It was described in - // Clinger WD. How to read floating point numbers accurately. - // ACM SIGPLAN Notices. 1990 -#ifndef FLT_EVAL_METHOD -#error "FLT_EVAL_METHOD should be defined, please include cfloat." -#endif -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - // We cannot be certain that x/y is rounded to nearest. - if (0 <= power && power <= 22 && i <= 9007199254740991) { -#else - if (-22 <= power && power <= 22 && i <= 9007199254740991) { -#endif - // convert the integer into a double. This is lossless since - // 0 <= i <= 2^53 - 1. - d = double(i); - // - // The general idea is as follows. - // If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then - // 1) Both s and p can be represented exactly as 64-bit floating-point - // values - // (binary64). - // 2) Because s and p can be represented exactly as floating-point values, - // then s * p - // and s / p will produce correctly rounded values. - // - if (power < 0) { - d = d / simdjson::internal::power_of_ten[-power]; - } else { - d = d * simdjson::internal::power_of_ten[power]; - } - if (negative) { - d = -d; - } - return true; - } - // When 22 < power && power < 22 + 16, we could - // hope for another, secondary fast path. It was - // described by David M. Gay in "Correctly rounded - // binary-decimal and decimal-binary conversions." (1990) - // If you need to compute i * 10^(22 + x) for x < 16, - // first compute i * 10^x, if you know that result is exact - // (e.g., when i * 10^x < 2^53), - // then you can still proceed and do (i * 10^x) * 10^22. - // Is this worth your time? - // You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53) - // for this second fast path to work. - // If you you have 22 < power *and* power < 22 + 16, and then you - // optimistically compute "i * 10^(x-22)", there is still a chance that you - // have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of - // this optimization maybe less common than we would like. Source: - // http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - // also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html - - // The fast path has now failed, so we are failing back on the slower path. - - // In the slow path, we need to adjust i so that it is > 1<<63 which is always - // possible, except if i == 0, so we handle i == 0 separately. - if(i == 0) { - d = 0.0; - return true; - } - - - // The exponent is 1024 + 63 + power - // + floor(log(5**power)/log(2)). - // The 1024 comes from the ieee64 standard. - // The 63 comes from the fact that we use a 64-bit word. - // - // Computing floor(log(5**power)/log(2)) could be - // slow. Instead we use a fast function. - // - // For power in (-400,350), we have that - // (((152170 + 65536) * power ) >> 16); - // is equal to - // floor(log(5**power)/log(2)) + power when power >= 0 - // and it is equal to - // ceil(log(5**-power)/log(2)) + power when power < 0 - // - // The 65536 is (1<<16) and corresponds to - // (65536 * power) >> 16 ---> power - // - // ((152170 * power ) >> 16) is equal to - // floor(log(5**power)/log(2)) - // - // Note that this is not magic: 152170/(1<<16) is - // approximatively equal to log(5)/log(2). - // The 1<<16 value is a power of two; we could use a - // larger power of 2 if we wanted to. - // - int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63; - - - // We want the most significant bit of i to be 1. Shift if needed. - int lz = leading_zeroes(i); - i <<= lz; - - - // We are going to need to do some 64-bit arithmetic to get a precise product. - // We use a table lookup approach. - // It is safe because - // power >= smallest_power - // and power <= largest_power - // We recover the mantissa of the power, it has a leading 1. It is always - // rounded down. - // - // We want the most significant 64 bits of the product. We know - // this will be non-zero because the most significant bit of i is - // 1. - const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power); - // Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.) - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 firstproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // Both i and power_of_five_128[index] have their most significant bit set to 1 which - // implies that the either the most or the second most significant bit of the product - // is 1. We pack values in this manner for efficiency reasons: it maximizes the use - // we make of the product. It also makes it easy to reason about the product: there - // is 0 or 1 leading zero in the product. - - // Unless the least significant 9 bits of the high (64-bit) part of the full - // product are all 1s, then we know that the most significant 55 bits are - // exact and no further work is needed. Having 55 bits is necessary because - // we need 53 bits for the mantissa but we have to have one rounding bit and - // we can waste a bit if the most significant bit of the product is zero. - if((firstproduct.high & 0x1FF) == 0x1FF) { - // We want to compute i * 5^q, but only care about the top 55 bits at most. - // Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing - // the full computation is wasteful. So we do what is called a "truncated - // multiplication". - // We take the most significant 64-bits, and we put them in - // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q - // to the desired approximation using one multiplication. Sometimes it does not suffice. - // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and - // then we get a better approximation to i * 5^q. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat - // more complicated. - // - // There is an extra layer of complexity in that we need more than 55 bits of - // accuracy in the round-to-even scenario. - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 secondproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - uint64_t lower = firstproduct.low; - uint64_t upper = firstproduct.high; - // The final mantissa should be 53 bits with a leading 1. - // We shift it so that it occupies 54 bits with a leading 1. - /////// - uint64_t upperbit = upper >> 63; - uint64_t mantissa = upper >> (upperbit + 9); - lz += int(1 ^ upperbit); - - // Here we have mantissa < (1<<54). - int64_t real_exponent = exponent - lz; - if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal? - // Here have that real_exponent <= 0 so -real_exponent >= 0 - if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. - d = 0.0; - return true; - } - // next line is safe because -real_exponent + 1 < 0 - mantissa >>= -real_exponent + 1; - // Thankfully, we can't have both "round-to-even" and subnormals because - // "round-to-even" only occurs for powers close to 0. - mantissa += (mantissa & 1); // round up - mantissa >>= 1; - // There is a weird scenario where we don't have a subnormal but just. - // Suppose we start with 2.2250738585072013e-308, we end up - // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal - // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round - // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer - // subnormal, but we can only know this after rounding. - // So we only declare a subnormal if we are smaller than the threshold. - real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1; - d = to_double(mantissa, real_exponent, negative); - return true; - } - // We have to round to even. The "to even" part - // is only a problem when we are right in between two floats - // which we guard against. - // If we have lots of trailing zeros, we may fall right between two - // floating-point values. - // - // The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54] - // times a power of two. That is, it is right between a number with binary significand - // m and another number with binary significand m+1; and it must be the case - // that it cannot be represented by a float itself. - // - // We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p. - // Recall that 10^q = 5^q * 2^q. - // When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that - // 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23. - // When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so - // (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have - // 2^{53} x 5^{-q} < 2^{64}. - // Hence we have 5^{-q} < 2^{11}$ or q>= -4. - // - // We require lower <= 1 and not lower == 0 because we could not prove that - // that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test. - if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) { - if((mantissa << (upperbit + 64 - 53 - 2)) == upper) { - mantissa &= ~1; // flip it so that we do not round up - } - } - - mantissa += mantissa & 1; - mantissa >>= 1; - - // Here we have mantissa < (1<<53), unless there was an overflow - if (mantissa >= (1ULL << 53)) { - ////////// - // This will happen when parsing values such as 7.2057594037927933e+16 - //////// - mantissa = (1ULL << 52); - real_exponent++; - } - mantissa &= ~(1ULL << 52); - // we have to check that real_exponent is in range, otherwise we bail out - if (simdjson_unlikely(real_exponent > 2046)) { - // We have an infinite value!!! We could actually throw an error here if we could. - return false; - } - d = to_double(mantissa, real_exponent, negative); - return true; -} - -// We call a fallback floating-point parser that might be slow. Note -// it will accept JSON numbers, but the JSON spec. is more restrictive so -// before you call parse_float_fallback, you need to have validated the input -// string with the JSON grammar. -// It will return an error (false) if the parsed number is infinite. -// The string parsing itself always succeeds. We know that there is at least -// one digit. -static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} -static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} - -// check quickly whether the next 8 chars are made of digits -// at a glance, it looks better than Mula's -// http://0x80.pl/articles/swar-digits-validate.html -simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) { - uint64_t val; - // this can read up to 7 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7"); - std::memcpy(&val, chars, 8); - // a branchy method might be faster: - // return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030) - // && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) == - // 0x3030303030303030); - return (((val & 0xF0F0F0F0F0F0F0F0) | - (((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) == - 0x3333333333333333); -} - -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; - } - return INVALID_NUMBER(src); -} - -template -SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later -simdjson_inline bool parse_digit(const uint8_t c, I &i) { - const uint8_t digit = static_cast(c - '0'); - if (digit > 9) { - return false; - } - // PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication - i = 10 * i + digit; // might overflow, we will handle the overflow later - return true; -} - -simdjson_inline error_code parse_decimal(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { - // we continue with the fiction that we have an integer. If the - // floating point number is representable as x * 10^z for some integer - // z that fits in 53 bits, then we will be able to convert back the - // the integer into a float in a lossless manner. - const uint8_t *const first_after_period = p; - -#ifdef SIMDJSON_SWAR_NUMBER_PARSING -#if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } -#endif // SIMDJSON_SWAR_NUMBER_PARSING -#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; -} - -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { - // Exp Sign: -123.456e[-]78 - bool neg_exp = ('-' == *p); - if (neg_exp || '+' == *p) { p++; } // Skip + as well - - // Exponent: -123.456e-[78] - auto start_exp = p; - int64_t exp_number = 0; - while (parse_digit(*p, exp_number)) { ++p; } - // It is possible for parse_digit to overflow. - // In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN. - // Thus we *must* check for possible overflow before we negate exp_number. - - // Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into - // a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may - // not oblige and may, in fact, generate two distinct paths in any case. It might be - // possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off - // instructions for a simdjson_likely branch, an unconclusive gain. - - // If there were no digits, it's an error. - if (simdjson_unlikely(p == start_exp)) { - return INVALID_NUMBER(src); - } - // We have a valid positive exponent in exp_number at this point, except that - // it may have overflowed. - - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - while (*start_exp == '0') { start_exp++; } - // 19 digits could overflow int64_t and is kind of absurd anyway. We don't - // support exponents smaller than -999,999,999,999,999,999 and bigger - // than 999,999,999,999,999,999. - // We can truncate. - // Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before - // infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could - // truncate at 324. - // Note that there is no reason to fail per se at this point in time. - // E.g., 0e999999999999999999999 is a fine number. - if (p > start_exp+18) { exp_number = 999999999999999999; } - } - // At this point, we know that exp_number is a sane, positive, signed integer. - // It is <= 999,999,999,999,999,999. As long as 'exponent' is in - // [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent' - // is bounded in magnitude by the size of the JSON input, we are fine in this universe. - // To sum it up: the next line should never overflow. - exponent += (neg_exp ? -exp_number : exp_number); - return SUCCESS; -} - -simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) { - // It is possible that the integer had an overflow. - // We have to handle the case where we have 0.0000somenumber. - const uint8_t *start = start_digits; - while ((*start == '0') || (*start == '.')) { ++start; } - // we over-decrement by one when there is a '.' - return digit_count - size_t(start - start_digits); -} - -template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { - // If we frequently had to deal with long strings of digits, - // we could extend our code by using a 128-bit integer instead - // of a 64-bit integer. However, this is uncommon in practice. - // - // 9999999999999999999 < 2**64 so we can accommodate 19 digits. - // If we have a decimal separator, then digit_count - 1 is the number of digits, but we - // may not have a decimal separator! - if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) { - // Ok, chances are good that we had an overflow! - // this is almost never going to get called!!! - // we start anew, going slowly!!! - // This will happen in the following examples: - // 10000000000000000000000000000000000000000000e+308 - // 3.1415926535897932384626433832795028841971693993751 - // - // NOTE: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. If we passed our writer reference to - // it, it would force it to be stored in memory, preventing the compiler from picking it apart - // and putting into registers. i.e. if we pass it as reference, it gets slow. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - return error; - } - // NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other - // way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331 - // To future reader: we'd love if someone found a better way, or at least could explain this result! - if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) { - // - // Important: smallest_power is such that it leads to a zero value. - // Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero - // so something x 10^-343 goes to zero, but not so with something x 10^-342. - static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough"); - // - if((exponent < simdjson::internal::smallest_power) || (i == 0)) { - WRITE_DOUBLE(0, src, writer); - return SUCCESS; - } else { // (exponent > largest_power) and (i != 0) - // We have, for sure, an infinite value and simdjson refuses to parse infinite values. - return INVALID_NUMBER(src); - } - } - double d; - if (!compute_float_64(exponent, i, negative, d)) { - // we are almost never going to get here. - if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); } - } - WRITE_DOUBLE(d, src, writer); - return SUCCESS; -} - -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING - -template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds -} - -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return ondemand::number_type::signed_integer; } -#else - -// parse the number at src -// define JSON_TEST_NUMBERS for unit testing -// -// It is assumed that the number is followed by a structural ({,},],[) character -// or a white space character. If that is not the case (e.g., when the JSON -// document is made of a single number), then it is necessary to copy the -// content and append a space before calling this function. -// -// Our objective is accurate parsing (ULP of 0) at high speed. -template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); } - - // - // Handle floats if there is a . or e (or both) - // - int64_t exponent = 0; - bool is_float = false; - if ('.' == *p) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_decimal(src, p, i, exponent) ); - digit_count = int(p - start_digits); // used later to guard against overflows - } - if (('e' == *p) || ('E' == *p)) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_exponent(src, p, exponent) ); - } - if (is_float) { - const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p); - SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) ); - if (dirty_end) { return INVALID_NUMBER(src); } - return SUCCESS; - } - - // The longest negative 64-bit number is 19 digits. - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - size_t longest_digit_count = negative ? 19 : 20; - if (digit_count > longest_digit_count) { return INVALID_NUMBER(src); } - if (digit_count == longest_digit_count) { - if (negative) { - // Anything negative above INT64_MAX+1 is invalid - if (i > uint64_t(INT64_MAX)+1) { return INVALID_NUMBER(src); } - WRITE_INTEGER(~i+1, src, writer); - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } - } - - // Write unsigned if it doesn't fit in a signed integer. - if (i > uint64_t(INT64_MAX)) { - WRITE_UNSIGNED(i, src, writer); - } else { - WRITE_INTEGER(negative ? (~i+1) : i, src, writer); - } - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; -} - -// Inlineable functions -namespace { - -// This table can be used to characterize the final character of an integer -// string. For JSON structural character and allowable white space characters, -// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise -// we return NUMBER_ERROR. -// Optimization note: we could easily reduce the size of the table by half (to 128) -// at the cost of an extra branch. -// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits): -static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast"); - -const uint8_t integer_string_finisher[256] = { - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR}; - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - - -// Parse any number from 0 to 18,446,744,073,709,551,615 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { - const uint8_t *p = src + 1; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (*p != '"') { return NUMBER_ERROR; } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - // Note: we use src[1] and not src[0] because src[0] is the quote character in this - // instance. - if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept { - // - // Check for minus sign - // - if(src == src_end) { return NUMBER_ERROR; } - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - const uint8_t *p = src + negative + 1; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(*p != '"') { return NUMBER_ERROR; } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { - return (*src == '-'); -} - -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; } - return false; -} - -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return ondemand::number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) { - return ondemand::number_type::unsigned_integer; - } - } - return ondemand::number_type::signed_integer; - } - // Hopefully, we have 'e' or 'E' or '.'. - return ondemand::number_type::floating_point_number; -} - -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept { - if(src == src_end) { return NUMBER_ERROR; } - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - if(p == src_end) { return NUMBER_ERROR; } - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while ((p != src_end) && parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely((p != src_end) && (*p == '.'))) { - p++; - const uint8_t *start_decimal_digits = p; - if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if ((p != src_end) && (*p == 'e' || *p == 'E')) { - p++; - if(p == src_end) { return NUMBER_ERROR; } - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while ((p != src_end) && parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, src_end, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - src += negative + 1; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (*p != '"') { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} -} //namespace {} -#endif // SIMDJSON_SKIPNUMBERPARSING - -} // namespace numberparsing -} // unnamed namespace -} // namespace haswell -} // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_HASWELL_NUMBERPARSING_H -/* end file include/simdjson/haswell/numberparsing.h */ -/* begin file include/simdjson/haswell/end.h */ -SIMDJSON_UNTARGET_HASWELL -/* end file include/simdjson/haswell/end.h */ - -#endif // SIMDJSON_IMPLEMENTATION_HASWELL -#endif // SIMDJSON_HASWELL_COMMON_H -/* end file include/simdjson/haswell.h */ -/* begin file include/simdjson/ppc64.h */ -#ifndef SIMDJSON_PPC64_H -#define SIMDJSON_PPC64_H - - -#if SIMDJSON_IMPLEMENTATION_PPC64 - -namespace simdjson { -/** - * Implementation for ALTIVEC (PPC64). - */ -namespace ppc64 { -} // namespace ppc64 -} // namespace simdjson - -/* begin file include/simdjson/ppc64/implementation.h */ -#ifndef SIMDJSON_PPC64_IMPLEMENTATION_H -#define SIMDJSON_PPC64_IMPLEMENTATION_H - - -namespace simdjson { -namespace ppc64 { - -namespace { -using namespace simdjson; -using namespace simdjson::dom; -} // namespace - -class implementation final : public simdjson::implementation { -public: - simdjson_inline implementation() - : simdjson::implementation("ppc64", "PPC64 ALTIVEC", - internal::instruction_set::ALTIVEC) {} - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t capacity, size_t max_length, - std::unique_ptr &dst) - const noexcept final; - simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, - uint8_t *dst, - size_t &dst_len) const noexcept final; - simdjson_warn_unused bool validate_utf8(const char *buf, - size_t len) const noexcept final; -}; - -} // namespace ppc64 -} // namespace simdjson - -#endif // SIMDJSON_PPC64_IMPLEMENTATION_H -/* end file include/simdjson/ppc64/implementation.h */ - -/* begin file include/simdjson/ppc64/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "ppc64" -// #define SIMDJSON_IMPLEMENTATION ppc64 -/* end file include/simdjson/ppc64/begin.h */ - -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ - -namespace simdjson { -namespace ppc64 { - -// expectation: sizeof(open_container) = 64/8. -struct open_container { - uint32_t tape_index; // where, on the tape, does the scope ([,{) begins - uint32_t count; // how many elements in the scope -}; // struct open_container - -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); - -class dom_parser_implementation final : public internal::dom_parser_implementation { -public: - /** Tape location of each open { or [ */ - std::unique_ptr open_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr is_array{}; - /** Buffer passed to stage 1 */ - const uint8_t *buf{}; - /** Length passed to stage 1 */ - size_t len{0}; - /** Document passed to stage 2 */ - dom::document *doc{}; - - inline dom_parser_implementation() noexcept; - inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; - inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; - dom_parser_implementation(const dom_parser_implementation &) = delete; - dom_parser_implementation &operator=(const dom_parser_implementation &) = delete; - - simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final; - simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final; - simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept final; - inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final; - inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final; -private: - simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity); - -}; - -} // namespace ppc64 -} // namespace simdjson - -namespace simdjson { -namespace ppc64 { - -inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -// Leaving these here so they can be inlined if so desired -inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { - if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] ); - if (!structural_indexes) { _capacity = 0; return MEMALLOC; } - structural_indexes[0] = 0; - n_structural_indexes = 0; - - _capacity = capacity; - return SUCCESS; -} - -inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // Stage 2 stacks - open_containers.reset(new (std::nothrow) open_container[max_depth]); - is_array.reset(new (std::nothrow) bool[max_depth]); - if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; } - - _max_depth = max_depth; - return SUCCESS; -} - -} // namespace ppc64 -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/ppc64/intrinsics.h */ -#ifndef SIMDJSON_PPC64_INTRINSICS_H -#define SIMDJSON_PPC64_INTRINSICS_H - - -// This should be the correct header whether -// you use visual studio or other compilers. -#include - -// These are defined by altivec.h in GCC toolchain, it is safe to undef them. -#ifdef bool -#undef bool -#endif - -#ifdef vector -#undef vector -#endif - -static_assert(sizeof(__vector unsigned char) <= simdjson::SIMDJSON_PADDING, "insufficient padding for ppc64"); - -#endif // SIMDJSON_PPC64_INTRINSICS_H -/* end file include/simdjson/ppc64/intrinsics.h */ -/* begin file include/simdjson/ppc64/bitmanipulation.h */ -#ifndef SIMDJSON_PPC64_BITMANIPULATION_H -#define SIMDJSON_PPC64_BITMANIPULATION_H - -namespace simdjson { -namespace ppc64 { -namespace { - -// We sometimes call trailing_zero on inputs that are zero, -// but the algorithms do not end up using the returned value. -// Sadly, sanitizers are not smart enough to figure it out. -SIMDJSON_NO_SANITIZE_UNDEFINED -simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - unsigned long ret; - // Search the mask data from least significant bit (LSB) - // to the most significant bit (MSB) for a set bit (1). - _BitScanForward64(&ret, input_num); - return (int)ret; -#else // SIMDJSON_REGULAR_VISUAL_STUDIO - return __builtin_ctzll(input_num); -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO -} - -/* result might be undefined when input_num is zero */ -simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { - return input_num & (input_num - 1); -} - -/* result might be undefined when input_num is zero */ -simdjson_inline int leading_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - unsigned long leading_zero = 0; - // Search the mask data from most significant bit (MSB) - // to least significant bit (LSB) for a set bit (1). - if (_BitScanReverse64(&leading_zero, input_num)) - return (int)(63 - leading_zero); - else - return 64; -#else - return __builtin_clzll(input_num); -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO -} - -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO -simdjson_inline int count_ones(uint64_t input_num) { - // note: we do not support legacy 32-bit Windows - return __popcnt64(input_num); // Visual Studio wants two underscores -} -#else -simdjson_inline int count_ones(uint64_t input_num) { - return __builtin_popcountll(input_num); -} -#endif - -simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - *result = value1 + value2; - return *result < value1; -#else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); -#endif -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson - -#endif // SIMDJSON_PPC64_BITMANIPULATION_H -/* end file include/simdjson/ppc64/bitmanipulation.h */ -/* begin file include/simdjson/ppc64/bitmask.h */ -#ifndef SIMDJSON_PPC64_BITMASK_H -#define SIMDJSON_PPC64_BITMASK_H - -namespace simdjson { -namespace ppc64 { -namespace { - -// -// Perform a "cumulative bitwise xor," flipping bits each time a 1 is -// encountered. -// -// For example, prefix_xor(00100100) == 00011100 -// -simdjson_inline uint64_t prefix_xor(uint64_t bitmask) { - // You can use the version below, however gcc sometimes miscompiles - // vec_pmsum_be, it happens somewhere around between 8 and 9th version. - // The performance boost was not noticeable, falling back to a usual - // implementation. - // __vector unsigned long long all_ones = {~0ull, ~0ull}; - // __vector unsigned long long mask = {bitmask, 0}; - // // Clang and GCC return different values for pmsum for ull so cast it to one. - // // Generally it is not specified by ALTIVEC ISA what is returned by - // // vec_pmsum_be. - // #if defined(__LITTLE_ENDIAN__) - // return (uint64_t)(((__vector unsigned long long)vec_pmsum_be(all_ones, mask))[0]); - // #else - // return (uint64_t)(((__vector unsigned long long)vec_pmsum_be(all_ones, mask))[1]); - // #endif - bitmask ^= bitmask << 1; - bitmask ^= bitmask << 2; - bitmask ^= bitmask << 4; - bitmask ^= bitmask << 8; - bitmask ^= bitmask << 16; - bitmask ^= bitmask << 32; - return bitmask; -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson - -#endif -/* end file include/simdjson/ppc64/bitmask.h */ -/* begin file include/simdjson/ppc64/simd.h */ -#ifndef SIMDJSON_PPC64_SIMD_H -#define SIMDJSON_PPC64_SIMD_H - -#include - -namespace simdjson { -namespace ppc64 { -namespace { -namespace simd { - -using __m128i = __vector unsigned char; - -template struct base { - __m128i value; - - // Zero constructor - simdjson_inline base() : value{__m128i()} {} - - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} - - // Conversion to SIMD register - simdjson_inline operator const __m128i &() const { - return this->value; - } - simdjson_inline operator __m128i &() { return this->value; } - - // Bit operations - simdjson_inline Child operator|(const Child other) const { - return vec_or(this->value, (__m128i)other); - } - simdjson_inline Child operator&(const Child other) const { - return vec_and(this->value, (__m128i)other); - } - simdjson_inline Child operator^(const Child other) const { - return vec_xor(this->value, (__m128i)other); - } - simdjson_inline Child bit_andnot(const Child other) const { - return vec_andc(this->value, (__m128i)other); - } - simdjson_inline Child &operator|=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast | other; - return *this_cast; - } - simdjson_inline Child &operator&=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast & other; - return *this_cast; - } - simdjson_inline Child &operator^=(const Child other) { - auto this_cast = static_cast(this); - *this_cast = *this_cast ^ other; - return *this_cast; - } -}; - -// Forward-declared so they can be used by splat and friends. -template struct simd8; - -template > -struct base8 : base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; - - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} - - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs); - } - - static const int SIZE = sizeof(base>::value); - - template - simdjson_inline simd8 prev(simd8 prev_chunk) const { - __m128i chunk = this->value; -#ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve(this->value); - prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk); -#endif - chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N); -#ifdef __LITTLE_ENDIAN__ - chunk = (__m128i)vec_reve((__m128i)chunk); -#endif - return chunk; - } -}; - -// SIMD byte mask type (returned by things like eq and gt) -template <> struct simd8 : base8 { - static simdjson_inline simd8 splat(bool _value) { - return (__m128i)vec_splats((unsigned char)(-(!!_value))); - } - - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) - : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) - : base8(splat(_value)) {} - - simdjson_inline int to_bitmask() const { - __vector unsigned long long result; - const __m128i perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, - 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x00}; - - result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value, - (__m128i)perm_mask)); -#ifdef __LITTLE_ENDIAN__ - return static_cast(result[1]); -#else - return static_cast(result[0]); -#endif - } - simdjson_inline bool any() const { - return !vec_all_eq(this->value, (__m128i)vec_splats(0)); - } - simdjson_inline simd8 operator~() const { - return this->value ^ (__m128i)splat(true); - } -}; - -template struct base8_numeric : base8 { - static simdjson_inline simd8 splat(T value) { - (void)value; - return (__m128i)vec_splats(value); - } - static simdjson_inline simd8 zero() { return splat(0); } - static simdjson_inline simd8 load(const T values[16]) { - return (__m128i)(vec_vsx_ld(0, reinterpret_cast(values))); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 repeat_16(T v0, T v1, T v2, T v3, T v4, - T v5, T v6, T v7, T v8, T v9, - T v10, T v11, T v12, T v13, - T v14, T v15) { - return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15); - } - - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) - : base8(_value) {} - - // Store to array - simdjson_inline void store(T dst[16]) const { - vec_vsx_st(this->value, 0, reinterpret_cast<__m128i *>(dst)); - } - - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { - return (__m128i)((__m128i)this->value + (__m128i)other); - } - simdjson_inline simd8 operator-(const simd8 other) const { - return (__m128i)((__m128i)this->value - (__m128i)other); - } - simdjson_inline simd8 &operator+=(const simd8 other) { - *this = *this + other; - return *static_cast *>(this); - } - simdjson_inline simd8 &operator-=(const simd8 other) { - *this = *this - other; - return *static_cast *>(this); - } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior - // for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value); - } - - // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted - // as a bitset). Passing a 0 value for mask would be equivalent to writing out - // every byte to output. Only the first 16 - count_ones(mask) bytes of the - // result are significant but 16 bytes get written. Design consideration: it - // seems like a function with the signature simd8 compress(uint32_t mask) - // would be sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint16_t mask, L *output) const { - using internal::BitsSetTable256mul2; - using internal::pshufb_combine_table; - using internal::thintable_epi8; - // this particular implementation was inspired by work done by @animetosho - // we do it in two steps, first 8 bytes and then second 8 bytes - uint8_t mask1 = uint8_t(mask); // least significant 8 bits - uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits - // next line just loads the 64-bit values thintable_epi8[mask1] and - // thintable_epi8[mask2] into a 128-bit register, using only - // two instructions on most compilers. -#ifdef __LITTLE_ENDIAN__ - __m128i shufmask = (__m128i)(__vector unsigned long long){ - thintable_epi8[mask1], thintable_epi8[mask2]}; -#else - __m128i shufmask = (__m128i)(__vector unsigned long long){ - thintable_epi8[mask2], thintable_epi8[mask1]}; - shufmask = (__m128i)vec_reve((__m128i)shufmask); -#endif - // we increment by 0x08 the second half of the mask - shufmask = ((__m128i)shufmask) + - ((__m128i)(__vector int){0, 0, 0x08080808, 0x08080808}); - - // this is the version "nearly pruned" - __m128i pruned = vec_perm(this->value, this->value, shufmask); - // we still need to put the two halves together. - // we compute the popcount of the first half: - int pop1 = BitsSetTable256mul2[mask1]; - // then load the corresponding mask, what it does is to write - // only the first pop1 bytes from the first 8 bytes, and then - // it fills in with the bytes from the second 8 bytes + some filling - // at the end. - __m128i compactmask = - vec_vsx_ld(0, reinterpret_cast(pshufb_combine_table + pop1 * 8)); - __m128i answer = vec_perm(pruned, (__m128i)vec_splats(0), compactmask); - vec_vsx_st(answer, 0, reinterpret_cast<__m128i *>(output)); - } - - template - simdjson_inline simd8 - lookup_16(L replace0, L replace1, L replace2, L replace3, L replace4, - L replace5, L replace6, L replace7, L replace8, L replace9, - L replace10, L replace11, L replace12, L replace13, L replace14, - L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, replace4, replace5, replace6, - replace7, replace8, replace9, replace10, replace11, replace12, - replace13, replace14, replace15)); - } -}; - -// Signed bytes -template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_value) {} - // Splat constructor - simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const int8_t *values) : simd8(load(values)) {} - // Member-by-member initialization - simdjson_inline simd8(int8_t v0, int8_t v1, int8_t v2, int8_t v3, - int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, - int8_t v12, int8_t v13, int8_t v14, int8_t v15) - : simd8((__m128i)(__vector signed char){v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, - v15}) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 - repeat_16(int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, - int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11, - int8_t v12, int8_t v13, int8_t v14, int8_t v15) { - return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } - - // Order-sensitive comparisons - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return (__m128i)vec_cmpgt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return (__m128i)vec_cmplt((__vector signed char)this->value, - (__vector signed char)(__m128i)other); - } -}; - -// Unsigned bytes -template <> struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) - : base8_numeric(_value) {} - // Splat constructor - simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const uint8_t *values) : simd8(load(values)) {} - // Member-by-member initialization - simdjson_inline - simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, - uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, - uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15) - : simd8((__m128i){v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15}) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 - repeat_16(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, - uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, - uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, - uint8_t v15) { - return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); - } - - // Saturated math - simdjson_inline simd8 - saturating_add(const simd8 other) const { - return (__m128i)vec_adds(this->value, (__m128i)other); - } - simdjson_inline simd8 - saturating_sub(const simd8 other) const { - return (__m128i)vec_subs(this->value, (__m128i)other); - } - - // Order-specific operations - simdjson_inline simd8 - max_val(const simd8 other) const { - return (__m128i)vec_max(this->value, (__m128i)other); - } - simdjson_inline simd8 - min_val(const simd8 other) const { - return (__m128i)vec_min(this->value, (__m128i)other); - } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - gt_bits(const simd8 other) const { - return this->saturating_sub(other); - } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 - lt_bits(const simd8 other) const { - return other.saturating_sub(*this); - } - simdjson_inline simd8 - operator<=(const simd8 other) const { - return other.max_val(*this) == other; - } - simdjson_inline simd8 - operator>=(const simd8 other) const { - return other.min_val(*this) == other; - } - simdjson_inline simd8 - operator>(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } - simdjson_inline simd8 - operator<(const simd8 other) const { - return this->gt_bits(other).any_bits_set(); - } - - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { - return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0))); - } - simdjson_inline simd8 bits_not_set(simd8 bits) const { - return (*this & bits).bits_not_set(); - } - simdjson_inline simd8 any_bits_set() const { - return ~this->bits_not_set(); - } - simdjson_inline simd8 any_bits_set(simd8 bits) const { - return ~this->bits_not_set(bits); - } - simdjson_inline bool bits_not_set_anywhere() const { - return vec_all_eq(this->value, (__m128i)vec_splats(0)); - } - simdjson_inline bool any_bits_set_anywhere() const { - return !bits_not_set_anywhere(); - } - simdjson_inline bool bits_not_set_anywhere(simd8 bits) const { - return vec_all_eq(vec_and(this->value, (__m128i)bits), - (__m128i)vec_splats(0)); - } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { - return !bits_not_set_anywhere(bits); - } - template simdjson_inline simd8 shr() const { - return simd8( - (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N))); - } - template simdjson_inline simd8 shl() const { - return simd8( - (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N))); - } -}; - -template struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, - "PPC64 kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64 &o) = delete; // no copy allowed - simd8x64 & - operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, - const simd8 chunk2, const simd8 chunk3) - : chunks{chunk0, chunk1, chunk2, chunk3} {} - simdjson_inline simd8x64(const T ptr[64]) - : chunks{simd8::load(ptr), simd8::load(ptr + 16), - simd8::load(ptr + 32), simd8::load(ptr + 48)} {} - - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr + sizeof(simd8) * 0); - this->chunks[1].store(ptr + sizeof(simd8) * 1); - this->chunks[2].store(ptr + sizeof(simd8) * 2); - this->chunks[3].store(ptr + sizeof(simd8) * 3); - } - - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | - (this->chunks[2] | this->chunks[3]); - } - - simdjson_inline uint64_t compress(uint64_t mask, T *output) const { - this->chunks[0].compress(uint16_t(mask), output); - this->chunks[1].compress(uint16_t(mask >> 16), - output + 16 - count_ones(mask & 0xFFFF)); - this->chunks[2].compress(uint16_t(mask >> 32), - output + 32 - count_ones(mask & 0xFFFFFFFF)); - this->chunks[3].compress(uint16_t(mask >> 48), - output + 48 - count_ones(mask & 0xFFFFFFFFFFFF)); - return 64 - count_ones(mask); - } - - simdjson_inline uint64_t to_bitmask() const { - uint64_t r0 = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r1 = this->chunks[1].to_bitmask(); - uint64_t r2 = this->chunks[2].to_bitmask(); - uint64_t r3 = this->chunks[3].to_bitmask(); - return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48); - } - - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] == mask, this->chunks[1] == mask, - this->chunks[2] == mask, this->chunks[3] == mask) - .to_bitmask(); - } - - simdjson_inline uint64_t eq(const simd8x64 &other) const { - return simd8x64(this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1], - this->chunks[2] == other.chunks[2], - this->chunks[3] == other.chunks[3]) - .to_bitmask(); - } - - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] <= mask, this->chunks[1] <= mask, - this->chunks[2] <= mask, this->chunks[3] <= mask) - .to_bitmask(); - } -}; // struct simd8x64 - -} // namespace simd -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson - -#endif // SIMDJSON_PPC64_SIMD_INPUT_H -/* end file include/simdjson/ppc64/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ - -namespace simdjson { -namespace ppc64 { -namespace { -namespace jsoncharutils { - -// return non-zero if not a structural or whitespace char -// zero otherwise -simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace_negated[c]; -} - -simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; -} - -// returns a value with the high 16 bits set if not valid -// otherwise returns the conversion of the 4 hex digits at src into the bottom -// 16 bits of the 32-bit return register -// -// see -// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ -static inline uint32_t hex_to_u32_nocheck( - const uint8_t *src) { // strictly speaking, static inline is a C-ism - uint32_t v1 = internal::digit_to_val32[630 + src[0]]; - uint32_t v2 = internal::digit_to_val32[420 + src[1]]; - uint32_t v3 = internal::digit_to_val32[210 + src[2]]; - uint32_t v4 = internal::digit_to_val32[0 + src[3]]; - return v1 | v2 | v3 | v4; -} - -// given a code point cp, writes to c -// the utf-8 code, outputting the length in -// bytes, if the length is zero, the code point -// is invalid -// -// This can possibly be made faster using pdep -// and clz and table lookups, but JSON documents -// have few escaped code points, and the following -// function looks cheap. -// -// Note: we assume that surrogates are treated separately -// -simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { - if (cp <= 0x7F) { - c[0] = uint8_t(cp); - return 1; // ascii - } - if (cp <= 0x7FF) { - c[0] = uint8_t((cp >> 6) + 192); - c[1] = uint8_t((cp & 63) + 128); - return 2; // universal plane - // Surrogates are treated elsewhere... - //} //else if (0xd800 <= cp && cp <= 0xdfff) { - // return 0; // surrogates // could put assert here - } else if (cp <= 0xFFFF) { - c[0] = uint8_t((cp >> 12) + 224); - c[1] = uint8_t(((cp >> 6) & 63) + 128); - c[2] = uint8_t((cp & 63) + 128); - return 3; - } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this - // is not needed - c[0] = uint8_t((cp >> 18) + 240); - c[1] = uint8_t(((cp >> 12) & 63) + 128); - c[2] = uint8_t(((cp >> 6) & 63) + 128); - c[3] = uint8_t((cp & 63) + 128); - return 4; - } - // will return 0 when the code point was too large. - return 0; // bad r -} - -#ifdef SIMDJSON_IS_32BITS // _umul128 for x86, arm -// this is a slow emulation routine for 32-bit -// -static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { - return x * (uint64_t)y; -} -static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { - uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); - uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); - uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); - uint64_t adbc_carry = !!(adbc < ad); - uint64_t lo = bd + (adbc << 32); - *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + - (adbc_carry << 32) + !!(lo < bd); - return lo; -} -#endif - -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) -#ifdef _M_ARM64 - // ARM64 has native support for 64-bit multiplications, no need to emultate - answer.high = __umulh(value1, value2); - answer.low = value1 * value2; -#else - answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64 -#endif // _M_ARM64 -#else // defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) - __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; - answer.low = uint64_t(r); - answer.high = uint64_t(r >> 64); -#endif - return answer; -} - -} // namespace jsoncharutils -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace ppc64 { -namespace { -/// @private -namespace atomparsing { - -// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. -// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot -// be certain that the character pointer will be properly aligned. -// You might think that using memcpy makes this function expensive, but you'd be wrong. -// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); -// to the compile-time constant 1936482662. -simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } - - -// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive. -// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about. -simdjson_warn_unused -simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) { - uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++) - static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes"); - std::memcpy(&srcval, src, sizeof(uint32_t)); - return srcval ^ string_to_uint32(atom); -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src) { - return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_true_atom(src); } - else if (len == 4) { return !str4ncmp(src, "true"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src) { - return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) { - if (len > 5) { return is_valid_false_atom(src); } - else if (len == 5) { return !str4ncmp(src+1, "alse"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src) { - return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_null_atom(src); } - else if (len == 4) { return !str4ncmp(src, "null"); } - else { return false; } -} - -} // namespace atomparsing -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/ppc64/stringparsing.h */ -#ifndef SIMDJSON_PPC64_STRINGPARSING_H -#define SIMDJSON_PPC64_STRINGPARSING_H - - -namespace simdjson { -namespace ppc64 { -namespace { - -using namespace simd; - -// Holds backslashes and quotes locations. -struct backslash_and_quote { -public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote - copy_and_find(const uint8_t *src, uint8_t *dst); - - simdjson_inline bool has_quote_first() { - return ((bs_bits - 1) & quote_bits) != 0; - } - simdjson_inline bool has_backslash() { return bs_bits != 0; } - simdjson_inline int quote_index() { - return trailing_zeroes(quote_bits); - } - simdjson_inline int backslash_index() { - return trailing_zeroes(bs_bits); - } - - uint32_t bs_bits; - uint32_t quote_bits; -}; // struct backslash_and_quote - -simdjson_inline backslash_and_quote -backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { - // this can read up to 31 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), - "backslash and quote finder must process fewer than " - "SIMDJSON_PADDING bytes"); - simd8 v0(src); - simd8 v1(src + sizeof(v0)); - v0.store(dst); - v1.store(dst + sizeof(v0)); - - // Getting a 64-bit bitmask is much cheaper than multiple 16-bit bitmasks on - // PPC; therefore, we smash them together into a 64-byte mask and get the - // bitmask from there. - uint64_t bs_and_quote = - simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson - -#endif // SIMDJSON_PPC64_STRINGPARSING_H -/* end file include/simdjson/ppc64/stringparsing.h */ -/* begin file include/simdjson/ppc64/numberparsing.h */ -#ifndef SIMDJSON_PPC64_NUMBERPARSING_H -#define SIMDJSON_PPC64_NUMBERPARSING_H - -#if defined(__linux__) -#include -#elif defined(__FreeBSD__) -#include -#endif - -namespace simdjson { -namespace ppc64 { -namespace { - -// we don't have appropriate instructions, so let us use a scalar function -// credit: https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ -static simdjson_inline uint32_t -parse_eight_digits_unrolled(const uint8_t *chars) { - uint64_t val; - std::memcpy(&val, chars, sizeof(uint64_t)); -#ifdef __BIG_ENDIAN__ -#if defined(__linux__) - val = bswap_64(val); -#elif defined(__FreeBSD__) - val = bswap64(val); -#endif -#endif - val = (val & 0x0F0F0F0F0F0F0F0F) * 2561 >> 8; - val = (val & 0x00FF00FF00FF00FF) * 6553601 >> 16; - return uint32_t((val & 0x0000FFFF0000FFFF) * 42949672960001 >> 32); -} - -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson - -#define SIMDJSON_SWAR_NUMBER_PARSING 1 - -/* begin file include/simdjson/generic/numberparsing.h */ -#include - -namespace simdjson { -namespace ppc64 { - -namespace ondemand { -/** - * The type of a JSON number - */ -enum class number_type { - floating_point_number=1, /// a binary64 number - signed_integer, /// a signed integer that fits in a 64-bit word using two's complement - unsigned_integer /// a positive integer larger or equal to 1<<63 -}; -} - -namespace { -/// @private -namespace numberparsing { - - - -#ifdef JSON_TEST_NUMBERS -#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) -#else -#define INVALID_NUMBER(SRC) (NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE)) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE)) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE)) -#endif - -namespace { -// Convert a mantissa, an exponent and a sign bit into an ieee64 double. -// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable). -// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed. -simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) { - double d; - mantissa &= ~(1ULL << 52); - mantissa |= real_exponent << 52; - mantissa |= ((static_cast(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; -} -} -// Attempts to compute i * 10^(power) exactly; and if "negative" is -// true, negate the result. -// This function will only work in some cases, when it does not work, success is -// set to false. This should work *most of the time* (like 99% of the time). -// We assume that power is in the [smallest_power, -// largest_power] interval: the caller is responsible for this check. -simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) { - // we start with a fast path - // It was described in - // Clinger WD. How to read floating point numbers accurately. - // ACM SIGPLAN Notices. 1990 -#ifndef FLT_EVAL_METHOD -#error "FLT_EVAL_METHOD should be defined, please include cfloat." -#endif -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - // We cannot be certain that x/y is rounded to nearest. - if (0 <= power && power <= 22 && i <= 9007199254740991) { -#else - if (-22 <= power && power <= 22 && i <= 9007199254740991) { -#endif - // convert the integer into a double. This is lossless since - // 0 <= i <= 2^53 - 1. - d = double(i); - // - // The general idea is as follows. - // If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then - // 1) Both s and p can be represented exactly as 64-bit floating-point - // values - // (binary64). - // 2) Because s and p can be represented exactly as floating-point values, - // then s * p - // and s / p will produce correctly rounded values. - // - if (power < 0) { - d = d / simdjson::internal::power_of_ten[-power]; - } else { - d = d * simdjson::internal::power_of_ten[power]; - } - if (negative) { - d = -d; - } - return true; - } - // When 22 < power && power < 22 + 16, we could - // hope for another, secondary fast path. It was - // described by David M. Gay in "Correctly rounded - // binary-decimal and decimal-binary conversions." (1990) - // If you need to compute i * 10^(22 + x) for x < 16, - // first compute i * 10^x, if you know that result is exact - // (e.g., when i * 10^x < 2^53), - // then you can still proceed and do (i * 10^x) * 10^22. - // Is this worth your time? - // You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53) - // for this second fast path to work. - // If you you have 22 < power *and* power < 22 + 16, and then you - // optimistically compute "i * 10^(x-22)", there is still a chance that you - // have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of - // this optimization maybe less common than we would like. Source: - // http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - // also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html - - // The fast path has now failed, so we are failing back on the slower path. - - // In the slow path, we need to adjust i so that it is > 1<<63 which is always - // possible, except if i == 0, so we handle i == 0 separately. - if(i == 0) { - d = 0.0; - return true; - } - - - // The exponent is 1024 + 63 + power - // + floor(log(5**power)/log(2)). - // The 1024 comes from the ieee64 standard. - // The 63 comes from the fact that we use a 64-bit word. - // - // Computing floor(log(5**power)/log(2)) could be - // slow. Instead we use a fast function. - // - // For power in (-400,350), we have that - // (((152170 + 65536) * power ) >> 16); - // is equal to - // floor(log(5**power)/log(2)) + power when power >= 0 - // and it is equal to - // ceil(log(5**-power)/log(2)) + power when power < 0 - // - // The 65536 is (1<<16) and corresponds to - // (65536 * power) >> 16 ---> power - // - // ((152170 * power ) >> 16) is equal to - // floor(log(5**power)/log(2)) - // - // Note that this is not magic: 152170/(1<<16) is - // approximatively equal to log(5)/log(2). - // The 1<<16 value is a power of two; we could use a - // larger power of 2 if we wanted to. - // - int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63; - - - // We want the most significant bit of i to be 1. Shift if needed. - int lz = leading_zeroes(i); - i <<= lz; - - - // We are going to need to do some 64-bit arithmetic to get a precise product. - // We use a table lookup approach. - // It is safe because - // power >= smallest_power - // and power <= largest_power - // We recover the mantissa of the power, it has a leading 1. It is always - // rounded down. - // - // We want the most significant 64 bits of the product. We know - // this will be non-zero because the most significant bit of i is - // 1. - const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power); - // Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.) - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 firstproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // Both i and power_of_five_128[index] have their most significant bit set to 1 which - // implies that the either the most or the second most significant bit of the product - // is 1. We pack values in this manner for efficiency reasons: it maximizes the use - // we make of the product. It also makes it easy to reason about the product: there - // is 0 or 1 leading zero in the product. - - // Unless the least significant 9 bits of the high (64-bit) part of the full - // product are all 1s, then we know that the most significant 55 bits are - // exact and no further work is needed. Having 55 bits is necessary because - // we need 53 bits for the mantissa but we have to have one rounding bit and - // we can waste a bit if the most significant bit of the product is zero. - if((firstproduct.high & 0x1FF) == 0x1FF) { - // We want to compute i * 5^q, but only care about the top 55 bits at most. - // Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing - // the full computation is wasteful. So we do what is called a "truncated - // multiplication". - // We take the most significant 64-bits, and we put them in - // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q - // to the desired approximation using one multiplication. Sometimes it does not suffice. - // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and - // then we get a better approximation to i * 5^q. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat - // more complicated. - // - // There is an extra layer of complexity in that we need more than 55 bits of - // accuracy in the round-to-even scenario. - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 secondproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - uint64_t lower = firstproduct.low; - uint64_t upper = firstproduct.high; - // The final mantissa should be 53 bits with a leading 1. - // We shift it so that it occupies 54 bits with a leading 1. - /////// - uint64_t upperbit = upper >> 63; - uint64_t mantissa = upper >> (upperbit + 9); - lz += int(1 ^ upperbit); - - // Here we have mantissa < (1<<54). - int64_t real_exponent = exponent - lz; - if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal? - // Here have that real_exponent <= 0 so -real_exponent >= 0 - if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. - d = 0.0; - return true; - } - // next line is safe because -real_exponent + 1 < 0 - mantissa >>= -real_exponent + 1; - // Thankfully, we can't have both "round-to-even" and subnormals because - // "round-to-even" only occurs for powers close to 0. - mantissa += (mantissa & 1); // round up - mantissa >>= 1; - // There is a weird scenario where we don't have a subnormal but just. - // Suppose we start with 2.2250738585072013e-308, we end up - // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal - // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round - // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer - // subnormal, but we can only know this after rounding. - // So we only declare a subnormal if we are smaller than the threshold. - real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1; - d = to_double(mantissa, real_exponent, negative); - return true; - } - // We have to round to even. The "to even" part - // is only a problem when we are right in between two floats - // which we guard against. - // If we have lots of trailing zeros, we may fall right between two - // floating-point values. - // - // The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54] - // times a power of two. That is, it is right between a number with binary significand - // m and another number with binary significand m+1; and it must be the case - // that it cannot be represented by a float itself. - // - // We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p. - // Recall that 10^q = 5^q * 2^q. - // When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that - // 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23. - // When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so - // (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have - // 2^{53} x 5^{-q} < 2^{64}. - // Hence we have 5^{-q} < 2^{11}$ or q>= -4. - // - // We require lower <= 1 and not lower == 0 because we could not prove that - // that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test. - if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) { - if((mantissa << (upperbit + 64 - 53 - 2)) == upper) { - mantissa &= ~1; // flip it so that we do not round up - } - } - - mantissa += mantissa & 1; - mantissa >>= 1; - - // Here we have mantissa < (1<<53), unless there was an overflow - if (mantissa >= (1ULL << 53)) { - ////////// - // This will happen when parsing values such as 7.2057594037927933e+16 - //////// - mantissa = (1ULL << 52); - real_exponent++; - } - mantissa &= ~(1ULL << 52); - // we have to check that real_exponent is in range, otherwise we bail out - if (simdjson_unlikely(real_exponent > 2046)) { - // We have an infinite value!!! We could actually throw an error here if we could. - return false; - } - d = to_double(mantissa, real_exponent, negative); - return true; -} - -// We call a fallback floating-point parser that might be slow. Note -// it will accept JSON numbers, but the JSON spec. is more restrictive so -// before you call parse_float_fallback, you need to have validated the input -// string with the JSON grammar. -// It will return an error (false) if the parsed number is infinite. -// The string parsing itself always succeeds. We know that there is at least -// one digit. -static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} -static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} - -// check quickly whether the next 8 chars are made of digits -// at a glance, it looks better than Mula's -// http://0x80.pl/articles/swar-digits-validate.html -simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) { - uint64_t val; - // this can read up to 7 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7"); - std::memcpy(&val, chars, 8); - // a branchy method might be faster: - // return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030) - // && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) == - // 0x3030303030303030); - return (((val & 0xF0F0F0F0F0F0F0F0) | - (((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) == - 0x3333333333333333); -} - -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; - } - return INVALID_NUMBER(src); -} - -template -SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later -simdjson_inline bool parse_digit(const uint8_t c, I &i) { - const uint8_t digit = static_cast(c - '0'); - if (digit > 9) { - return false; - } - // PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication - i = 10 * i + digit; // might overflow, we will handle the overflow later - return true; -} - -simdjson_inline error_code parse_decimal(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { - // we continue with the fiction that we have an integer. If the - // floating point number is representable as x * 10^z for some integer - // z that fits in 53 bits, then we will be able to convert back the - // the integer into a float in a lossless manner. - const uint8_t *const first_after_period = p; - -#ifdef SIMDJSON_SWAR_NUMBER_PARSING -#if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } -#endif // SIMDJSON_SWAR_NUMBER_PARSING -#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; -} - -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { - // Exp Sign: -123.456e[-]78 - bool neg_exp = ('-' == *p); - if (neg_exp || '+' == *p) { p++; } // Skip + as well - - // Exponent: -123.456e-[78] - auto start_exp = p; - int64_t exp_number = 0; - while (parse_digit(*p, exp_number)) { ++p; } - // It is possible for parse_digit to overflow. - // In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN. - // Thus we *must* check for possible overflow before we negate exp_number. - - // Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into - // a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may - // not oblige and may, in fact, generate two distinct paths in any case. It might be - // possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off - // instructions for a simdjson_likely branch, an unconclusive gain. - - // If there were no digits, it's an error. - if (simdjson_unlikely(p == start_exp)) { - return INVALID_NUMBER(src); - } - // We have a valid positive exponent in exp_number at this point, except that - // it may have overflowed. - - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - while (*start_exp == '0') { start_exp++; } - // 19 digits could overflow int64_t and is kind of absurd anyway. We don't - // support exponents smaller than -999,999,999,999,999,999 and bigger - // than 999,999,999,999,999,999. - // We can truncate. - // Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before - // infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could - // truncate at 324. - // Note that there is no reason to fail per se at this point in time. - // E.g., 0e999999999999999999999 is a fine number. - if (p > start_exp+18) { exp_number = 999999999999999999; } - } - // At this point, we know that exp_number is a sane, positive, signed integer. - // It is <= 999,999,999,999,999,999. As long as 'exponent' is in - // [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent' - // is bounded in magnitude by the size of the JSON input, we are fine in this universe. - // To sum it up: the next line should never overflow. - exponent += (neg_exp ? -exp_number : exp_number); - return SUCCESS; -} - -simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) { - // It is possible that the integer had an overflow. - // We have to handle the case where we have 0.0000somenumber. - const uint8_t *start = start_digits; - while ((*start == '0') || (*start == '.')) { ++start; } - // we over-decrement by one when there is a '.' - return digit_count - size_t(start - start_digits); -} - -template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { - // If we frequently had to deal with long strings of digits, - // we could extend our code by using a 128-bit integer instead - // of a 64-bit integer. However, this is uncommon in practice. - // - // 9999999999999999999 < 2**64 so we can accommodate 19 digits. - // If we have a decimal separator, then digit_count - 1 is the number of digits, but we - // may not have a decimal separator! - if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) { - // Ok, chances are good that we had an overflow! - // this is almost never going to get called!!! - // we start anew, going slowly!!! - // This will happen in the following examples: - // 10000000000000000000000000000000000000000000e+308 - // 3.1415926535897932384626433832795028841971693993751 - // - // NOTE: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. If we passed our writer reference to - // it, it would force it to be stored in memory, preventing the compiler from picking it apart - // and putting into registers. i.e. if we pass it as reference, it gets slow. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - return error; - } - // NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other - // way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331 - // To future reader: we'd love if someone found a better way, or at least could explain this result! - if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) { - // - // Important: smallest_power is such that it leads to a zero value. - // Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero - // so something x 10^-343 goes to zero, but not so with something x 10^-342. - static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough"); - // - if((exponent < simdjson::internal::smallest_power) || (i == 0)) { - WRITE_DOUBLE(0, src, writer); - return SUCCESS; - } else { // (exponent > largest_power) and (i != 0) - // We have, for sure, an infinite value and simdjson refuses to parse infinite values. - return INVALID_NUMBER(src); - } - } - double d; - if (!compute_float_64(exponent, i, negative, d)) { - // we are almost never going to get here. - if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); } - } - WRITE_DOUBLE(d, src, writer); - return SUCCESS; -} - -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING - -template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds -} - -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return ondemand::number_type::signed_integer; } -#else - -// parse the number at src -// define JSON_TEST_NUMBERS for unit testing -// -// It is assumed that the number is followed by a structural ({,},],[) character -// or a white space character. If that is not the case (e.g., when the JSON -// document is made of a single number), then it is necessary to copy the -// content and append a space before calling this function. -// -// Our objective is accurate parsing (ULP of 0) at high speed. -template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); } - - // - // Handle floats if there is a . or e (or both) - // - int64_t exponent = 0; - bool is_float = false; - if ('.' == *p) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_decimal(src, p, i, exponent) ); - digit_count = int(p - start_digits); // used later to guard against overflows - } - if (('e' == *p) || ('E' == *p)) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_exponent(src, p, exponent) ); - } - if (is_float) { - const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p); - SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) ); - if (dirty_end) { return INVALID_NUMBER(src); } - return SUCCESS; - } - - // The longest negative 64-bit number is 19 digits. - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - size_t longest_digit_count = negative ? 19 : 20; - if (digit_count > longest_digit_count) { return INVALID_NUMBER(src); } - if (digit_count == longest_digit_count) { - if (negative) { - // Anything negative above INT64_MAX+1 is invalid - if (i > uint64_t(INT64_MAX)+1) { return INVALID_NUMBER(src); } - WRITE_INTEGER(~i+1, src, writer); - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } - } - - // Write unsigned if it doesn't fit in a signed integer. - if (i > uint64_t(INT64_MAX)) { - WRITE_UNSIGNED(i, src, writer); - } else { - WRITE_INTEGER(negative ? (~i+1) : i, src, writer); - } - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; -} - -// Inlineable functions -namespace { - -// This table can be used to characterize the final character of an integer -// string. For JSON structural character and allowable white space characters, -// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise -// we return NUMBER_ERROR. -// Optimization note: we could easily reduce the size of the table by half (to 128) -// at the cost of an extra branch. -// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits): -static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast"); - -const uint8_t integer_string_finisher[256] = { - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR}; - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - - -// Parse any number from 0 to 18,446,744,073,709,551,615 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { - const uint8_t *p = src + 1; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (*p != '"') { return NUMBER_ERROR; } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - // Note: we use src[1] and not src[0] because src[0] is the quote character in this - // instance. - if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept { - // - // Check for minus sign - // - if(src == src_end) { return NUMBER_ERROR; } - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - const uint8_t *p = src + negative + 1; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(*p != '"') { return NUMBER_ERROR; } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { - return (*src == '-'); -} - -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; } - return false; -} - -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return ondemand::number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) { - return ondemand::number_type::unsigned_integer; - } - } - return ondemand::number_type::signed_integer; - } - // Hopefully, we have 'e' or 'E' or '.'. - return ondemand::number_type::floating_point_number; -} - -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept { - if(src == src_end) { return NUMBER_ERROR; } - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - if(p == src_end) { return NUMBER_ERROR; } - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while ((p != src_end) && parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely((p != src_end) && (*p == '.'))) { - p++; - const uint8_t *start_decimal_digits = p; - if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if ((p != src_end) && (*p == 'e' || *p == 'E')) { - p++; - if(p == src_end) { return NUMBER_ERROR; } - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while ((p != src_end) && parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, src_end, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - src += negative + 1; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (*p != '"') { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} -} //namespace {} -#endif // SIMDJSON_SKIPNUMBERPARSING - -} // namespace numberparsing -} // unnamed namespace -} // namespace ppc64 -} // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_PPC64_NUMBERPARSING_H -/* end file include/simdjson/ppc64/numberparsing.h */ -/* begin file include/simdjson/ppc64/end.h */ -/* end file include/simdjson/ppc64/end.h */ - -#endif // SIMDJSON_IMPLEMENTATION_PPC64 - -#endif // SIMDJSON_PPC64_H -/* end file include/simdjson/ppc64.h */ -/* begin file include/simdjson/westmere.h */ -#ifndef SIMDJSON_WESTMERE_H -#define SIMDJSON_WESTMERE_H - - -#if SIMDJSON_IMPLEMENTATION_WESTMERE - -#if SIMDJSON_CAN_ALWAYS_RUN_WESTMERE -#define SIMDJSON_TARGET_WESTMERE -#define SIMDJSON_UNTARGET_WESTMERE -#else -#define SIMDJSON_TARGET_WESTMERE SIMDJSON_TARGET_REGION("sse4.2,pclmul") -#define SIMDJSON_UNTARGET_WESTMERE SIMDJSON_UNTARGET_REGION -#endif - -namespace simdjson { -/** - * Implementation for Westmere (Intel SSE4.2). - */ -namespace westmere { -} // namespace westmere -} // namespace simdjson - -// -// These two need to be included outside SIMDJSON_TARGET_WESTMERE -// -/* begin file include/simdjson/westmere/implementation.h */ -#ifndef SIMDJSON_WESTMERE_IMPLEMENTATION_H -#define SIMDJSON_WESTMERE_IMPLEMENTATION_H - - -// The constructor may be executed on any host, so we take care not to use SIMDJSON_TARGET_WESTMERE -namespace simdjson { -namespace westmere { - -namespace { -using namespace simdjson; -using namespace simdjson::dom; -} - -class implementation final : public simdjson::implementation { -public: - simdjson_inline implementation() : simdjson::implementation("westmere", "Intel/AMD SSE4.2", internal::instruction_set::SSE42 | internal::instruction_set::PCLMULQDQ) {} - simdjson_warn_unused error_code create_dom_parser_implementation( - size_t capacity, - size_t max_length, - std::unique_ptr& dst - ) const noexcept final; - simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final; - simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final; -}; - -} // namespace westmere -} // namespace simdjson - -#endif // SIMDJSON_WESTMERE_IMPLEMENTATION_H -/* end file include/simdjson/westmere/implementation.h */ -/* begin file include/simdjson/westmere/intrinsics.h */ -#ifndef SIMDJSON_WESTMERE_INTRINSICS_H -#define SIMDJSON_WESTMERE_INTRINSICS_H - -#ifdef SIMDJSON_VISUAL_STUDIO -// under clang within visual studio, this will include -#include // visual studio or clang -#else -#include // elsewhere -#endif // SIMDJSON_VISUAL_STUDIO - - -#ifdef SIMDJSON_CLANG_VISUAL_STUDIO -/** - * You are not supposed, normally, to include these - * headers directly. Instead you should either include intrin.h - * or x86intrin.h. However, when compiling with clang - * under Windows (i.e., when _MSC_VER is set), these headers - * only get included *if* the corresponding features are detected - * from macros: - */ -#include // for _mm_alignr_epi8 -#include // for _mm_clmulepi64_si128 -#endif - -static_assert(sizeof(__m128i) <= simdjson::SIMDJSON_PADDING, "insufficient padding for westmere"); - -#endif // SIMDJSON_WESTMERE_INTRINSICS_H -/* end file include/simdjson/westmere/intrinsics.h */ - -// -// The rest need to be inside the region -// -/* begin file include/simdjson/westmere/begin.h */ -// redefining SIMDJSON_IMPLEMENTATION to "westmere" -// #define SIMDJSON_IMPLEMENTATION westmere -SIMDJSON_TARGET_WESTMERE -/* end file include/simdjson/westmere/begin.h */ - -// Declarations -/* begin file include/simdjson/generic/dom_parser_implementation.h */ - -namespace simdjson { -namespace westmere { - -// expectation: sizeof(open_container) = 64/8. -struct open_container { - uint32_t tape_index; // where, on the tape, does the scope ([,{) begins - uint32_t count; // how many elements in the scope -}; // struct open_container - -static_assert(sizeof(open_container) == 64/8, "Open container must be 64 bits"); - -class dom_parser_implementation final : public internal::dom_parser_implementation { -public: - /** Tape location of each open { or [ */ - std::unique_ptr open_containers{}; - /** Whether each open container is a [ or { */ - std::unique_ptr is_array{}; - /** Buffer passed to stage 1 */ - const uint8_t *buf{}; - /** Length passed to stage 1 */ - size_t len{0}; - /** Document passed to stage 2 */ - dom::document *doc{}; - - inline dom_parser_implementation() noexcept; - inline dom_parser_implementation(dom_parser_implementation &&other) noexcept; - inline dom_parser_implementation &operator=(dom_parser_implementation &&other) noexcept; - dom_parser_implementation(const dom_parser_implementation &) = delete; - dom_parser_implementation &operator=(const dom_parser_implementation &) = delete; - - simdjson_warn_unused error_code parse(const uint8_t *buf, size_t len, dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage1(const uint8_t *buf, size_t len, stage1_mode partial) noexcept final; - simdjson_warn_unused error_code stage2(dom::document &doc) noexcept final; - simdjson_warn_unused error_code stage2_next(dom::document &doc) noexcept final; - simdjson_warn_unused uint8_t *parse_string(const uint8_t *src, uint8_t *dst) const noexcept final; - inline simdjson_warn_unused error_code set_capacity(size_t capacity) noexcept final; - inline simdjson_warn_unused error_code set_max_depth(size_t max_depth) noexcept final; -private: - simdjson_inline simdjson_warn_unused error_code set_capacity_stage1(size_t capacity); - -}; - -} // namespace westmere -} // namespace simdjson - -namespace simdjson { -namespace westmere { - -inline dom_parser_implementation::dom_parser_implementation() noexcept = default; -inline dom_parser_implementation::dom_parser_implementation(dom_parser_implementation &&other) noexcept = default; -inline dom_parser_implementation &dom_parser_implementation::operator=(dom_parser_implementation &&other) noexcept = default; - -// Leaving these here so they can be inlined if so desired -inline simdjson_warn_unused error_code dom_parser_implementation::set_capacity(size_t capacity) noexcept { - if(capacity > SIMDJSON_MAXSIZE_BYTES) { return CAPACITY; } - // Stage 1 index output - size_t max_structures = SIMDJSON_ROUNDUP_N(capacity, 64) + 2 + 7; - structural_indexes.reset( new (std::nothrow) uint32_t[max_structures] ); - if (!structural_indexes) { _capacity = 0; return MEMALLOC; } - structural_indexes[0] = 0; - n_structural_indexes = 0; - - _capacity = capacity; - return SUCCESS; -} - -inline simdjson_warn_unused error_code dom_parser_implementation::set_max_depth(size_t max_depth) noexcept { - // Stage 2 stacks - open_containers.reset(new (std::nothrow) open_container[max_depth]); - is_array.reset(new (std::nothrow) bool[max_depth]); - if (!is_array || !open_containers) { _max_depth = 0; return MEMALLOC; } - - _max_depth = max_depth; - return SUCCESS; -} - -} // namespace westmere -} // namespace simdjson -/* end file include/simdjson/generic/dom_parser_implementation.h */ -/* begin file include/simdjson/westmere/bitmanipulation.h */ -#ifndef SIMDJSON_WESTMERE_BITMANIPULATION_H -#define SIMDJSON_WESTMERE_BITMANIPULATION_H - -namespace simdjson { -namespace westmere { -namespace { - -// We sometimes call trailing_zero on inputs that are zero, -// but the algorithms do not end up using the returned value. -// Sadly, sanitizers are not smart enough to figure it out. -SIMDJSON_NO_SANITIZE_UNDEFINED -simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - unsigned long ret; - // Search the mask data from least significant bit (LSB) - // to the most significant bit (MSB) for a set bit (1). - _BitScanForward64(&ret, input_num); - return (int)ret; -#else // SIMDJSON_REGULAR_VISUAL_STUDIO - return __builtin_ctzll(input_num); -#endif // SIMDJSON_REGULAR_VISUAL_STUDIO -} - -/* result might be undefined when input_num is zero */ -simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { - return input_num & (input_num-1); -} - -/* result might be undefined when input_num is zero */ -simdjson_inline int leading_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - unsigned long leading_zero = 0; - // Search the mask data from most significant bit (MSB) - // to least significant bit (LSB) for a set bit (1). - if (_BitScanReverse64(&leading_zero, input_num)) - return (int)(63 - leading_zero); - else - return 64; -#else - return __builtin_clzll(input_num); -#endif// SIMDJSON_REGULAR_VISUAL_STUDIO -} - -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO -simdjson_inline unsigned __int64 count_ones(uint64_t input_num) { - // note: we do not support legacy 32-bit Windows - return __popcnt64(input_num);// Visual Studio wants two underscores -} -#else -simdjson_inline long long int count_ones(uint64_t input_num) { - return _popcnt64(input_num); -} -#endif - -simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, - uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO - return _addcarry_u64(0, value1, value2, - reinterpret_cast(result)); -#else - return __builtin_uaddll_overflow(value1, value2, - reinterpret_cast(result)); -#endif -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson - -#endif // SIMDJSON_WESTMERE_BITMANIPULATION_H -/* end file include/simdjson/westmere/bitmanipulation.h */ -/* begin file include/simdjson/westmere/bitmask.h */ -#ifndef SIMDJSON_WESTMERE_BITMASK_H -#define SIMDJSON_WESTMERE_BITMASK_H - -namespace simdjson { -namespace westmere { -namespace { - -// -// Perform a "cumulative bitwise xor," flipping bits each time a 1 is encountered. -// -// For example, prefix_xor(00100100) == 00011100 -// -simdjson_inline uint64_t prefix_xor(const uint64_t bitmask) { - // There should be no such thing with a processing supporting avx2 - // but not clmul. - __m128i all_ones = _mm_set1_epi8('\xFF'); - __m128i result = _mm_clmulepi64_si128(_mm_set_epi64x(0ULL, bitmask), all_ones, 0); - return _mm_cvtsi128_si64(result); -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson - -#endif // SIMDJSON_WESTMERE_BITMASK_H -/* end file include/simdjson/westmere/bitmask.h */ -/* begin file include/simdjson/westmere/simd.h */ -#ifndef SIMDJSON_WESTMERE_SIMD_H -#define SIMDJSON_WESTMERE_SIMD_H - - -namespace simdjson { -namespace westmere { -namespace { -namespace simd { - - template - struct base { - __m128i value; - - // Zero constructor - simdjson_inline base() : value{__m128i()} {} - - // Conversion from SIMD register - simdjson_inline base(const __m128i _value) : value(_value) {} - - // Conversion to SIMD register - simdjson_inline operator const __m128i&() const { return this->value; } - simdjson_inline operator __m128i&() { return this->value; } - - // Bit operations - simdjson_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); } - simdjson_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); } - simdjson_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); } - simdjson_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); } - simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast | other; return *this_cast; } - simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast & other; return *this_cast; } - simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast(this); *this_cast = *this_cast ^ other; return *this_cast; } - }; - - // Forward-declared so they can be used by splat and friends. - template - struct simd8; - - template> - struct base8: base> { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; - - simdjson_inline base8() : base>() {} - simdjson_inline base8(const __m128i _value) : base>(_value) {} - - friend simdjson_inline Mask operator==(const simd8 lhs, const simd8 rhs) { return _mm_cmpeq_epi8(lhs, rhs); } - - static const int SIZE = sizeof(base>::value); - - template - simdjson_inline simd8 prev(const simd8 prev_chunk) const { - return _mm_alignr_epi8(*this, prev_chunk, 16 - N); - } - }; - - // SIMD byte mask type (returned by things like eq and gt) - template<> - struct simd8: base8 { - static simdjson_inline simd8 splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); } - - simdjson_inline simd8() : base8() {} - simdjson_inline simd8(const __m128i _value) : base8(_value) {} - // Splat constructor - simdjson_inline simd8(bool _value) : base8(splat(_value)) {} - - simdjson_inline int to_bitmask() const { return _mm_movemask_epi8(*this); } - simdjson_inline bool any() const { return !_mm_testz_si128(*this, *this); } - simdjson_inline simd8 operator~() const { return *this ^ true; } - }; - - template - struct base8_numeric: base8 { - static simdjson_inline simd8 splat(T _value) { return _mm_set1_epi8(_value); } - static simdjson_inline simd8 zero() { return _mm_setzero_si128(); } - static simdjson_inline simd8 load(const T values[16]) { - return _mm_loadu_si128(reinterpret_cast(values)); - } - // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdjson_inline simd8 repeat_16( - T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7, - T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - simdjson_inline base8_numeric() : base8() {} - simdjson_inline base8_numeric(const __m128i _value) : base8(_value) {} - - // Store to array - simdjson_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); } - - // Override to distinguish from bool version - simdjson_inline simd8 operator~() const { return *this ^ 0xFFu; } - - // Addition/subtraction are the same for signed and unsigned - simdjson_inline simd8 operator+(const simd8 other) const { return _mm_add_epi8(*this, other); } - simdjson_inline simd8 operator-(const simd8 other) const { return _mm_sub_epi8(*this, other); } - simdjson_inline simd8& operator+=(const simd8 other) { *this = *this + other; return *static_cast*>(this); } - simdjson_inline simd8& operator-=(const simd8 other) { *this = *this - other; return *static_cast*>(this); } - - // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values) - template - simdjson_inline simd8 lookup_16(simd8 lookup_table) const { - return _mm_shuffle_epi8(lookup_table, *this); - } - - // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset). - // Passing a 0 value for mask would be equivalent to writing out every byte to output. - // Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes - // get written. - // Design consideration: it seems like a function with the - // signature simd8 compress(uint32_t mask) would be - // sensible, but the AVX ISA makes this kind of approach difficult. - template - simdjson_inline void compress(uint16_t mask, L * output) const { - using internal::thintable_epi8; - using internal::BitsSetTable256mul2; - using internal::pshufb_combine_table; - // this particular implementation was inspired by work done by @animetosho - // we do it in two steps, first 8 bytes and then second 8 bytes - uint8_t mask1 = uint8_t(mask); // least significant 8 bits - uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits - // next line just loads the 64-bit values thintable_epi8[mask1] and - // thintable_epi8[mask2] into a 128-bit register, using only - // two instructions on most compilers. - __m128i shufmask = _mm_set_epi64x(thintable_epi8[mask2], thintable_epi8[mask1]); - // we increment by 0x08 the second half of the mask - shufmask = - _mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0)); - // this is the version "nearly pruned" - __m128i pruned = _mm_shuffle_epi8(*this, shufmask); - // we still need to put the two halves together. - // we compute the popcount of the first half: - int pop1 = BitsSetTable256mul2[mask1]; - // then load the corresponding mask, what it does is to write - // only the first pop1 bytes from the first 8 bytes, and then - // it fills in with the bytes from the second 8 bytes + some filling - // at the end. - __m128i compactmask = - _mm_loadu_si128(reinterpret_cast(pshufb_combine_table + pop1 * 8)); - __m128i answer = _mm_shuffle_epi8(pruned, compactmask); - _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); - } - - template - simdjson_inline simd8 lookup_16( - L replace0, L replace1, L replace2, L replace3, - L replace4, L replace5, L replace6, L replace7, - L replace8, L replace9, L replace10, L replace11, - L replace12, L replace13, L replace14, L replace15) const { - return lookup_16(simd8::repeat_16( - replace0, replace1, replace2, replace3, - replace4, replace5, replace6, replace7, - replace8, replace9, replace10, replace11, - replace12, replace13, replace14, replace15 - )); - } - }; - - // Signed bytes - template<> - struct simd8 : base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_value) {} - // Splat constructor - simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {} - // Member-by-member initialization - simdjson_inline simd8( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) : simd8(_mm_setr_epi8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - )) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 repeat_16( - int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, - int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Order-sensitive comparisons - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epi8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epi8(*this, other); } - simdjson_inline simd8 operator>(const simd8 other) const { return _mm_cmpgt_epi8(*this, other); } - simdjson_inline simd8 operator<(const simd8 other) const { return _mm_cmpgt_epi8(other, *this); } - }; - - // Unsigned bytes - template<> - struct simd8: base8_numeric { - simdjson_inline simd8() : base8_numeric() {} - simdjson_inline simd8(const __m128i _value) : base8_numeric(_value) {} - // Splat constructor - simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdjson_inline simd8(const uint8_t* values) : simd8(load(values)) {} - // Member-by-member initialization - simdjson_inline simd8( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) : simd8(_mm_setr_epi8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - )) {} - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdjson_inline static simd8 repeat_16( - uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, - uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 - ) { - return simd8( - v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10,v11,v12,v13,v14,v15 - ); - } - - // Saturated math - simdjson_inline simd8 saturating_add(const simd8 other) const { return _mm_adds_epu8(*this, other); } - simdjson_inline simd8 saturating_sub(const simd8 other) const { return _mm_subs_epu8(*this, other); } - - // Order-specific operations - simdjson_inline simd8 max_val(const simd8 other) const { return _mm_max_epu8(*this, other); } - simdjson_inline simd8 min_val(const simd8 other) const { return _mm_min_epu8(*this, other); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 gt_bits(const simd8 other) const { return this->saturating_sub(other); } - // Same as <, but only guarantees true is nonzero (< guarantees true = -1) - simdjson_inline simd8 lt_bits(const simd8 other) const { return other.saturating_sub(*this); } - simdjson_inline simd8 operator<=(const simd8 other) const { return other.max_val(*this) == other; } - simdjson_inline simd8 operator>=(const simd8 other) const { return other.min_val(*this) == other; } - simdjson_inline simd8 operator>(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - simdjson_inline simd8 operator<(const simd8 other) const { return this->gt_bits(other).any_bits_set(); } - - // Bit-specific operations - simdjson_inline simd8 bits_not_set() const { return *this == uint8_t(0); } - simdjson_inline simd8 bits_not_set(simd8 bits) const { return (*this & bits).bits_not_set(); } - simdjson_inline simd8 any_bits_set() const { return ~this->bits_not_set(); } - simdjson_inline simd8 any_bits_set(simd8 bits) const { return ~this->bits_not_set(bits); } - simdjson_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; } - simdjson_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); } - simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); } - simdjson_inline bool bits_not_set_anywhere(simd8 bits) const { return _mm_testz_si128(*this, bits); } - simdjson_inline bool any_bits_set_anywhere(simd8 bits) const { return !bits_not_set_anywhere(bits); } - template - simdjson_inline simd8 shr() const { return simd8(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); } - template - simdjson_inline simd8 shl() const { return simd8(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); } - // Get one of the bits and make a bitmask out of it. - // e.g. value.get_bit<7>() gets the high bit - template - simdjson_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); } - }; - - template - struct simd8x64 { - static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block."); - const simd8 chunks[NUM_CHUNKS]; - - simd8x64(const simd8x64& o) = delete; // no copy allowed - simd8x64& operator=(const simd8& other) = delete; // no assignment allowed - simd8x64() = delete; // no default constructor allowed - - simdjson_inline simd8x64(const simd8 chunk0, const simd8 chunk1, const simd8 chunk2, const simd8 chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {} - simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8::load(ptr), simd8::load(ptr+16), simd8::load(ptr+32), simd8::load(ptr+48)} {} - - simdjson_inline void store(T ptr[64]) const { - this->chunks[0].store(ptr+sizeof(simd8)*0); - this->chunks[1].store(ptr+sizeof(simd8)*1); - this->chunks[2].store(ptr+sizeof(simd8)*2); - this->chunks[3].store(ptr+sizeof(simd8)*3); - } - - simdjson_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]); - } - - simdjson_inline uint64_t compress(uint64_t mask, T * output) const { - this->chunks[0].compress(uint16_t(mask), output); - this->chunks[1].compress(uint16_t(mask >> 16), output + 16 - count_ones(mask & 0xFFFF)); - this->chunks[2].compress(uint16_t(mask >> 32), output + 32 - count_ones(mask & 0xFFFFFFFF)); - this->chunks[3].compress(uint16_t(mask >> 48), output + 48 - count_ones(mask & 0xFFFFFFFFFFFF)); - return 64 - count_ones(mask); - } - - simdjson_inline uint64_t to_bitmask() const { - uint64_t r0 = uint32_t(this->chunks[0].to_bitmask() ); - uint64_t r1 = this->chunks[1].to_bitmask() ; - uint64_t r2 = this->chunks[2].to_bitmask() ; - uint64_t r3 = this->chunks[3].to_bitmask() ; - return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48); - } - - simdjson_inline uint64_t eq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] == mask, - this->chunks[1] == mask, - this->chunks[2] == mask, - this->chunks[3] == mask - ).to_bitmask(); - } - - simdjson_inline uint64_t eq(const simd8x64 &other) const { - return simd8x64( - this->chunks[0] == other.chunks[0], - this->chunks[1] == other.chunks[1], - this->chunks[2] == other.chunks[2], - this->chunks[3] == other.chunks[3] - ).to_bitmask(); - } - - simdjson_inline uint64_t lteq(const T m) const { - const simd8 mask = simd8::splat(m); - return simd8x64( - this->chunks[0] <= mask, - this->chunks[1] <= mask, - this->chunks[2] <= mask, - this->chunks[3] <= mask - ).to_bitmask(); - } - }; // struct simd8x64 - -} // namespace simd -} // unnamed namespace -} // namespace westmere -} // namespace simdjson - -#endif // SIMDJSON_WESTMERE_SIMD_INPUT_H -/* end file include/simdjson/westmere/simd.h */ -/* begin file include/simdjson/generic/jsoncharutils.h */ - -namespace simdjson { -namespace westmere { -namespace { -namespace jsoncharutils { - -// return non-zero if not a structural or whitespace char -// zero otherwise -simdjson_inline uint32_t is_not_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace_negated[c]; -} - -simdjson_inline uint32_t is_structural_or_whitespace(uint8_t c) { - return internal::structural_or_whitespace[c]; -} - -// returns a value with the high 16 bits set if not valid -// otherwise returns the conversion of the 4 hex digits at src into the bottom -// 16 bits of the 32-bit return register -// -// see -// https://lemire.me/blog/2019/04/17/parsing-short-hexadecimal-strings-efficiently/ -static inline uint32_t hex_to_u32_nocheck( - const uint8_t *src) { // strictly speaking, static inline is a C-ism - uint32_t v1 = internal::digit_to_val32[630 + src[0]]; - uint32_t v2 = internal::digit_to_val32[420 + src[1]]; - uint32_t v3 = internal::digit_to_val32[210 + src[2]]; - uint32_t v4 = internal::digit_to_val32[0 + src[3]]; - return v1 | v2 | v3 | v4; -} - -// given a code point cp, writes to c -// the utf-8 code, outputting the length in -// bytes, if the length is zero, the code point -// is invalid -// -// This can possibly be made faster using pdep -// and clz and table lookups, but JSON documents -// have few escaped code points, and the following -// function looks cheap. -// -// Note: we assume that surrogates are treated separately -// -simdjson_inline size_t codepoint_to_utf8(uint32_t cp, uint8_t *c) { - if (cp <= 0x7F) { - c[0] = uint8_t(cp); - return 1; // ascii - } - if (cp <= 0x7FF) { - c[0] = uint8_t((cp >> 6) + 192); - c[1] = uint8_t((cp & 63) + 128); - return 2; // universal plane - // Surrogates are treated elsewhere... - //} //else if (0xd800 <= cp && cp <= 0xdfff) { - // return 0; // surrogates // could put assert here - } else if (cp <= 0xFFFF) { - c[0] = uint8_t((cp >> 12) + 224); - c[1] = uint8_t(((cp >> 6) & 63) + 128); - c[2] = uint8_t((cp & 63) + 128); - return 3; - } else if (cp <= 0x10FFFF) { // if you know you have a valid code point, this - // is not needed - c[0] = uint8_t((cp >> 18) + 240); - c[1] = uint8_t(((cp >> 12) & 63) + 128); - c[2] = uint8_t(((cp >> 6) & 63) + 128); - c[3] = uint8_t((cp & 63) + 128); - return 4; - } - // will return 0 when the code point was too large. - return 0; // bad r -} - -#ifdef SIMDJSON_IS_32BITS // _umul128 for x86, arm -// this is a slow emulation routine for 32-bit -// -static simdjson_inline uint64_t __emulu(uint32_t x, uint32_t y) { - return x * (uint64_t)y; -} -static simdjson_inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { - uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); - uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); - uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); - uint64_t adbc_carry = !!(adbc < ad); - uint64_t lo = bd + (adbc << 32); - *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + - (adbc_carry << 32) + !!(lo < bd); - return lo; -} -#endif - -using internal::value128; - -simdjson_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { - value128 answer; -#if defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) -#ifdef _M_ARM64 - // ARM64 has native support for 64-bit multiplications, no need to emultate - answer.high = __umulh(value1, value2); - answer.low = value1 * value2; -#else - answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64 -#endif // _M_ARM64 -#else // defined(SIMDJSON_REGULAR_VISUAL_STUDIO) || defined(SIMDJSON_IS_32BITS) - __uint128_t r = (static_cast<__uint128_t>(value1)) * value2; - answer.low = uint64_t(r); - answer.high = uint64_t(r >> 64); -#endif - return answer; -} - -} // namespace jsoncharutils -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file include/simdjson/generic/jsoncharutils.h */ -/* begin file include/simdjson/generic/atomparsing.h */ -namespace simdjson { -namespace westmere { -namespace { -/// @private -namespace atomparsing { - -// The string_to_uint32 is exclusively used to map literal strings to 32-bit values. -// We use memcpy instead of a pointer cast to avoid undefined behaviors since we cannot -// be certain that the character pointer will be properly aligned. -// You might think that using memcpy makes this function expensive, but you'd be wrong. -// All decent optimizing compilers (GCC, clang, Visual Studio) will compile string_to_uint32("false"); -// to the compile-time constant 1936482662. -simdjson_inline uint32_t string_to_uint32(const char* str) { uint32_t val; std::memcpy(&val, str, sizeof(uint32_t)); return val; } - - -// Again in str4ncmp we use a memcpy to avoid undefined behavior. The memcpy may appear expensive. -// Yet all decent optimizing compilers will compile memcpy to a single instruction, just about. -simdjson_warn_unused -simdjson_inline uint32_t str4ncmp(const uint8_t *src, const char* atom) { - uint32_t srcval; // we want to avoid unaligned 32-bit loads (undefined in C/C++) - static_assert(sizeof(uint32_t) <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be larger than 4 bytes"); - std::memcpy(&srcval, src, sizeof(uint32_t)); - return srcval ^ string_to_uint32(atom); -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src) { - return (str4ncmp(src, "true") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_true_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_true_atom(src); } - else if (len == 4) { return !str4ncmp(src, "true"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src) { - return (str4ncmp(src+1, "alse") | jsoncharutils::is_not_structural_or_whitespace(src[5])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_false_atom(const uint8_t *src, size_t len) { - if (len > 5) { return is_valid_false_atom(src); } - else if (len == 5) { return !str4ncmp(src+1, "alse"); } - else { return false; } -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src) { - return (str4ncmp(src, "null") | jsoncharutils::is_not_structural_or_whitespace(src[4])) == 0; -} - -simdjson_warn_unused -simdjson_inline bool is_valid_null_atom(const uint8_t *src, size_t len) { - if (len > 4) { return is_valid_null_atom(src); } - else if (len == 4) { return !str4ncmp(src, "null"); } - else { return false; } -} - -} // namespace atomparsing -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file include/simdjson/generic/atomparsing.h */ -/* begin file include/simdjson/westmere/stringparsing.h */ -#ifndef SIMDJSON_WESTMERE_STRINGPARSING_H -#define SIMDJSON_WESTMERE_STRINGPARSING_H - -namespace simdjson { -namespace westmere { -namespace { - -using namespace simd; - -// Holds backslashes and quotes locations. -struct backslash_and_quote { -public: - static constexpr uint32_t BYTES_PROCESSED = 32; - simdjson_inline static backslash_and_quote copy_and_find(const uint8_t *src, uint8_t *dst); - - simdjson_inline bool has_quote_first() { return ((bs_bits - 1) & quote_bits) != 0; } - simdjson_inline bool has_backslash() { return bs_bits != 0; } - simdjson_inline int quote_index() { return trailing_zeroes(quote_bits); } - simdjson_inline int backslash_index() { return trailing_zeroes(bs_bits); } - - uint32_t bs_bits; - uint32_t quote_bits; -}; // struct backslash_and_quote - -simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) { - // this can read up to 31 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "backslash and quote finder must process fewer than SIMDJSON_PADDING bytes"); - simd8 v0(src); - simd8 v1(src + 16); - v0.store(dst); - v1.store(dst + 16); - uint64_t bs_and_quote = simd8x64(v0 == '\\', v1 == '\\', v0 == '"', v1 == '"').to_bitmask(); - return { - uint32_t(bs_and_quote), // bs_bits - uint32_t(bs_and_quote >> 32) // quote_bits - }; -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson - -#endif // SIMDJSON_WESTMERE_STRINGPARSING_H -/* end file include/simdjson/westmere/stringparsing.h */ -/* begin file include/simdjson/westmere/numberparsing.h */ -#ifndef SIMDJSON_WESTMERE_NUMBERPARSING_H -#define SIMDJSON_WESTMERE_NUMBERPARSING_H - -namespace simdjson { -namespace westmere { -namespace { - -static simdjson_inline uint32_t parse_eight_digits_unrolled(const uint8_t *chars) { - // this actually computes *16* values so we are being wasteful. - const __m128i ascii0 = _mm_set1_epi8('0'); - const __m128i mul_1_10 = - _mm_setr_epi8(10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1); - const __m128i mul_1_100 = _mm_setr_epi16(100, 1, 100, 1, 100, 1, 100, 1); - const __m128i mul_1_10000 = - _mm_setr_epi16(10000, 1, 10000, 1, 10000, 1, 10000, 1); - const __m128i input = _mm_sub_epi8( - _mm_loadu_si128(reinterpret_cast(chars)), ascii0); - const __m128i t1 = _mm_maddubs_epi16(input, mul_1_10); - const __m128i t2 = _mm_madd_epi16(t1, mul_1_100); - const __m128i t3 = _mm_packus_epi32(t2, t2); - const __m128i t4 = _mm_madd_epi16(t3, mul_1_10000); - return _mm_cvtsi128_si32( - t4); // only captures the sum of the first 8 digits, drop the rest -} - -} // unnamed namespace -} // namespace westmere -} // namespace simdjson - -#define SIMDJSON_SWAR_NUMBER_PARSING 1 - -/* begin file include/simdjson/generic/numberparsing.h */ -#include - -namespace simdjson { -namespace westmere { - -namespace ondemand { -/** - * The type of a JSON number - */ -enum class number_type { - floating_point_number=1, /// a binary64 number - signed_integer, /// a signed integer that fits in a 64-bit word using two's complement - unsigned_integer /// a positive integer larger or equal to 1<<63 -}; -} - -namespace { -/// @private -namespace numberparsing { - - - -#ifdef JSON_TEST_NUMBERS -#define INVALID_NUMBER(SRC) (found_invalid_number((SRC)), NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (found_integer((VALUE), (SRC)), (WRITER).append_s64((VALUE))) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (found_unsigned_integer((VALUE), (SRC)), (WRITER).append_u64((VALUE))) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (found_float((VALUE), (SRC)), (WRITER).append_double((VALUE))) -#else -#define INVALID_NUMBER(SRC) (NUMBER_ERROR) -#define WRITE_INTEGER(VALUE, SRC, WRITER) (WRITER).append_s64((VALUE)) -#define WRITE_UNSIGNED(VALUE, SRC, WRITER) (WRITER).append_u64((VALUE)) -#define WRITE_DOUBLE(VALUE, SRC, WRITER) (WRITER).append_double((VALUE)) -#endif - -namespace { -// Convert a mantissa, an exponent and a sign bit into an ieee64 double. -// The real_exponent needs to be in [0, 2046] (technically real_exponent = 2047 would be acceptable). -// The mantissa should be in [0,1<<53). The bit at index (1ULL << 52) while be zeroed. -simdjson_inline double to_double(uint64_t mantissa, uint64_t real_exponent, bool negative) { - double d; - mantissa &= ~(1ULL << 52); - mantissa |= real_exponent << 52; - mantissa |= ((static_cast(negative)) << 63); - std::memcpy(&d, &mantissa, sizeof(d)); - return d; -} -} -// Attempts to compute i * 10^(power) exactly; and if "negative" is -// true, negate the result. -// This function will only work in some cases, when it does not work, success is -// set to false. This should work *most of the time* (like 99% of the time). -// We assume that power is in the [smallest_power, -// largest_power] interval: the caller is responsible for this check. -simdjson_inline bool compute_float_64(int64_t power, uint64_t i, bool negative, double &d) { - // we start with a fast path - // It was described in - // Clinger WD. How to read floating point numbers accurately. - // ACM SIGPLAN Notices. 1990 -#ifndef FLT_EVAL_METHOD -#error "FLT_EVAL_METHOD should be defined, please include cfloat." -#endif -#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) - // We cannot be certain that x/y is rounded to nearest. - if (0 <= power && power <= 22 && i <= 9007199254740991) { -#else - if (-22 <= power && power <= 22 && i <= 9007199254740991) { -#endif - // convert the integer into a double. This is lossless since - // 0 <= i <= 2^53 - 1. - d = double(i); - // - // The general idea is as follows. - // If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then - // 1) Both s and p can be represented exactly as 64-bit floating-point - // values - // (binary64). - // 2) Because s and p can be represented exactly as floating-point values, - // then s * p - // and s / p will produce correctly rounded values. - // - if (power < 0) { - d = d / simdjson::internal::power_of_ten[-power]; - } else { - d = d * simdjson::internal::power_of_ten[power]; - } - if (negative) { - d = -d; - } - return true; - } - // When 22 < power && power < 22 + 16, we could - // hope for another, secondary fast path. It was - // described by David M. Gay in "Correctly rounded - // binary-decimal and decimal-binary conversions." (1990) - // If you need to compute i * 10^(22 + x) for x < 16, - // first compute i * 10^x, if you know that result is exact - // (e.g., when i * 10^x < 2^53), - // then you can still proceed and do (i * 10^x) * 10^22. - // Is this worth your time? - // You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53) - // for this second fast path to work. - // If you you have 22 < power *and* power < 22 + 16, and then you - // optimistically compute "i * 10^(x-22)", there is still a chance that you - // have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of - // this optimization maybe less common than we would like. Source: - // http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ - // also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html - - // The fast path has now failed, so we are failing back on the slower path. - - // In the slow path, we need to adjust i so that it is > 1<<63 which is always - // possible, except if i == 0, so we handle i == 0 separately. - if(i == 0) { - d = 0.0; - return true; - } - - - // The exponent is 1024 + 63 + power - // + floor(log(5**power)/log(2)). - // The 1024 comes from the ieee64 standard. - // The 63 comes from the fact that we use a 64-bit word. - // - // Computing floor(log(5**power)/log(2)) could be - // slow. Instead we use a fast function. - // - // For power in (-400,350), we have that - // (((152170 + 65536) * power ) >> 16); - // is equal to - // floor(log(5**power)/log(2)) + power when power >= 0 - // and it is equal to - // ceil(log(5**-power)/log(2)) + power when power < 0 - // - // The 65536 is (1<<16) and corresponds to - // (65536 * power) >> 16 ---> power - // - // ((152170 * power ) >> 16) is equal to - // floor(log(5**power)/log(2)) - // - // Note that this is not magic: 152170/(1<<16) is - // approximatively equal to log(5)/log(2). - // The 1<<16 value is a power of two; we could use a - // larger power of 2 if we wanted to. - // - int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63; - - - // We want the most significant bit of i to be 1. Shift if needed. - int lz = leading_zeroes(i); - i <<= lz; - - - // We are going to need to do some 64-bit arithmetic to get a precise product. - // We use a table lookup approach. - // It is safe because - // power >= smallest_power - // and power <= largest_power - // We recover the mantissa of the power, it has a leading 1. It is always - // rounded down. - // - // We want the most significant 64 bits of the product. We know - // this will be non-zero because the most significant bit of i is - // 1. - const uint32_t index = 2 * uint32_t(power - simdjson::internal::smallest_power); - // Optimization: It may be that materializing the index as a variable might confuse some compilers and prevent effective complex-addressing loads. (Done for code clarity.) - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 firstproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index]); - // Both i and power_of_five_128[index] have their most significant bit set to 1 which - // implies that the either the most or the second most significant bit of the product - // is 1. We pack values in this manner for efficiency reasons: it maximizes the use - // we make of the product. It also makes it easy to reason about the product: there - // is 0 or 1 leading zero in the product. - - // Unless the least significant 9 bits of the high (64-bit) part of the full - // product are all 1s, then we know that the most significant 55 bits are - // exact and no further work is needed. Having 55 bits is necessary because - // we need 53 bits for the mantissa but we have to have one rounding bit and - // we can waste a bit if the most significant bit of the product is zero. - if((firstproduct.high & 0x1FF) == 0x1FF) { - // We want to compute i * 5^q, but only care about the top 55 bits at most. - // Consider the scenario where q>=0. Then 5^q may not fit in 64-bits. Doing - // the full computation is wasteful. So we do what is called a "truncated - // multiplication". - // We take the most significant 64-bits, and we put them in - // power_of_five_128[index]. Usually, that's good enough to approximate i * 5^q - // to the desired approximation using one multiplication. Sometimes it does not suffice. - // Then we store the next most significant 64 bits in power_of_five_128[index + 1], and - // then we get a better approximation to i * 5^q. In very rare cases, even that - // will not suffice, though it is seemingly very hard to find such a scenario. - // - // That's for when q>=0. The logic for q<0 is somewhat similar but it is somewhat - // more complicated. - // - // There is an extra layer of complexity in that we need more than 55 bits of - // accuracy in the round-to-even scenario. - // - // The full_multiplication function computes the 128-bit product of two 64-bit words - // with a returned value of type value128 with a "low component" corresponding to the - // 64-bit least significant bits of the product and with a "high component" corresponding - // to the 64-bit most significant bits of the product. - simdjson::internal::value128 secondproduct = jsoncharutils::full_multiplication(i, simdjson::internal::power_of_five_128[index + 1]); - firstproduct.low += secondproduct.high; - if(secondproduct.high > firstproduct.low) { firstproduct.high++; } - // At this point, we might need to add at most one to firstproduct, but this - // can only change the value of firstproduct.high if firstproduct.low is maximal. - if(simdjson_unlikely(firstproduct.low == 0xFFFFFFFFFFFFFFFF)) { - // This is very unlikely, but if so, we need to do much more work! - return false; - } - } - uint64_t lower = firstproduct.low; - uint64_t upper = firstproduct.high; - // The final mantissa should be 53 bits with a leading 1. - // We shift it so that it occupies 54 bits with a leading 1. - /////// - uint64_t upperbit = upper >> 63; - uint64_t mantissa = upper >> (upperbit + 9); - lz += int(1 ^ upperbit); - - // Here we have mantissa < (1<<54). - int64_t real_exponent = exponent - lz; - if (simdjson_unlikely(real_exponent <= 0)) { // we have a subnormal? - // Here have that real_exponent <= 0 so -real_exponent >= 0 - if(-real_exponent + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. - d = 0.0; - return true; - } - // next line is safe because -real_exponent + 1 < 0 - mantissa >>= -real_exponent + 1; - // Thankfully, we can't have both "round-to-even" and subnormals because - // "round-to-even" only occurs for powers close to 0. - mantissa += (mantissa & 1); // round up - mantissa >>= 1; - // There is a weird scenario where we don't have a subnormal but just. - // Suppose we start with 2.2250738585072013e-308, we end up - // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal - // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round - // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer - // subnormal, but we can only know this after rounding. - // So we only declare a subnormal if we are smaller than the threshold. - real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1; - d = to_double(mantissa, real_exponent, negative); - return true; - } - // We have to round to even. The "to even" part - // is only a problem when we are right in between two floats - // which we guard against. - // If we have lots of trailing zeros, we may fall right between two - // floating-point values. - // - // The round-to-even cases take the form of a number 2m+1 which is in (2^53,2^54] - // times a power of two. That is, it is right between a number with binary significand - // m and another number with binary significand m+1; and it must be the case - // that it cannot be represented by a float itself. - // - // We must have that w * 10 ^q == (2m+1) * 2^p for some power of two 2^p. - // Recall that 10^q = 5^q * 2^q. - // When q >= 0, we must have that (2m+1) is divible by 5^q, so 5^q <= 2^54. We have that - // 5^23 <= 2^54 and it is the last power of five to qualify, so q <= 23. - // When q<0, we have w >= (2m+1) x 5^{-q}. We must have that w<2^{64} so - // (2m+1) x 5^{-q} < 2^{64}. We have that 2m+1>2^{53}. Hence, we must have - // 2^{53} x 5^{-q} < 2^{64}. - // Hence we have 5^{-q} < 2^{11}$ or q>= -4. - // - // We require lower <= 1 and not lower == 0 because we could not prove that - // that lower == 0 is implied; but we could prove that lower <= 1 is a necessary and sufficient test. - if (simdjson_unlikely((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1))) { - if((mantissa << (upperbit + 64 - 53 - 2)) == upper) { - mantissa &= ~1; // flip it so that we do not round up - } - } - - mantissa += mantissa & 1; - mantissa >>= 1; - - // Here we have mantissa < (1<<53), unless there was an overflow - if (mantissa >= (1ULL << 53)) { - ////////// - // This will happen when parsing values such as 7.2057594037927933e+16 - //////// - mantissa = (1ULL << 52); - real_exponent++; - } - mantissa &= ~(1ULL << 52); - // we have to check that real_exponent is in range, otherwise we bail out - if (simdjson_unlikely(real_exponent > 2046)) { - // We have an infinite value!!! We could actually throw an error here if we could. - return false; - } - d = to_double(mantissa, real_exponent, negative); - return true; -} - -// We call a fallback floating-point parser that might be slow. Note -// it will accept JSON numbers, but the JSON spec. is more restrictive so -// before you call parse_float_fallback, you need to have validated the input -// string with the JSON grammar. -// It will return an error (false) if the parsed number is infinite. -// The string parsing itself always succeeds. We know that there is at least -// one digit. -static bool parse_float_fallback(const uint8_t *ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} -static bool parse_float_fallback(const uint8_t *ptr, const uint8_t *end_ptr, double *outDouble) { - *outDouble = simdjson::internal::from_chars(reinterpret_cast(ptr), reinterpret_cast(end_ptr)); - // We do not accept infinite values. - - // Detecting finite values in a portable manner is ridiculously hard, ideally - // we would want to do: - // return !std::isfinite(*outDouble); - // but that mysteriously fails under legacy/old libc++ libraries, see - // https://github.com/simdjson/simdjson/issues/1286 - // - // Therefore, fall back to this solution (the extra parens are there - // to handle that max may be a macro on windows). - return !(*outDouble > (std::numeric_limits::max)() || *outDouble < std::numeric_limits::lowest()); -} - -// check quickly whether the next 8 chars are made of digits -// at a glance, it looks better than Mula's -// http://0x80.pl/articles/swar-digits-validate.html -simdjson_inline bool is_made_of_eight_digits_fast(const uint8_t *chars) { - uint64_t val; - // this can read up to 7 bytes beyond the buffer size, but we require - // SIMDJSON_PADDING of padding - static_assert(7 <= SIMDJSON_PADDING, "SIMDJSON_PADDING must be bigger than 7"); - std::memcpy(&val, chars, 8); - // a branchy method might be faster: - // return (( val & 0xF0F0F0F0F0F0F0F0 ) == 0x3030303030303030) - // && (( (val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0 ) == - // 0x3030303030303030); - return (((val & 0xF0F0F0F0F0F0F0F0) | - (((val + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4)) == - 0x3333333333333333); -} - -template -error_code slow_float_parsing(simdjson_unused const uint8_t * src, W writer) { - double d; - if (parse_float_fallback(src, &d)) { - writer.append_double(d); - return SUCCESS; - } - return INVALID_NUMBER(src); -} - -template -SIMDJSON_NO_SANITIZE_UNDEFINED // We deliberately allow overflow here and check later -simdjson_inline bool parse_digit(const uint8_t c, I &i) { - const uint8_t digit = static_cast(c - '0'); - if (digit > 9) { - return false; - } - // PERF NOTE: multiplication by 10 is cheaper than arbitrary integer multiplication - i = 10 * i + digit; // might overflow, we will handle the overflow later - return true; -} - -simdjson_inline error_code parse_decimal(simdjson_unused const uint8_t *const src, const uint8_t *&p, uint64_t &i, int64_t &exponent) { - // we continue with the fiction that we have an integer. If the - // floating point number is representable as x * 10^z for some integer - // z that fits in 53 bits, then we will be able to convert back the - // the integer into a float in a lossless manner. - const uint8_t *const first_after_period = p; - -#ifdef SIMDJSON_SWAR_NUMBER_PARSING -#if SIMDJSON_SWAR_NUMBER_PARSING - // this helps if we have lots of decimals! - // this turns out to be frequent enough. - if (is_made_of_eight_digits_fast(p)) { - i = i * 100000000 + parse_eight_digits_unrolled(p); - p += 8; - } -#endif // SIMDJSON_SWAR_NUMBER_PARSING -#endif // #ifdef SIMDJSON_SWAR_NUMBER_PARSING - // Unrolling the first digit makes a small difference on some implementations (e.g. westmere) - if (parse_digit(*p, i)) { ++p; } - while (parse_digit(*p, i)) { p++; } - exponent = first_after_period - p; - // Decimal without digits (123.) is illegal - if (exponent == 0) { - return INVALID_NUMBER(src); - } - return SUCCESS; -} - -simdjson_inline error_code parse_exponent(simdjson_unused const uint8_t *const src, const uint8_t *&p, int64_t &exponent) { - // Exp Sign: -123.456e[-]78 - bool neg_exp = ('-' == *p); - if (neg_exp || '+' == *p) { p++; } // Skip + as well - - // Exponent: -123.456e-[78] - auto start_exp = p; - int64_t exp_number = 0; - while (parse_digit(*p, exp_number)) { ++p; } - // It is possible for parse_digit to overflow. - // In particular, it could overflow to INT64_MIN, and we cannot do - INT64_MIN. - // Thus we *must* check for possible overflow before we negate exp_number. - - // Performance notes: it may seem like combining the two "simdjson_unlikely checks" below into - // a single simdjson_unlikely path would be faster. The reasoning is sound, but the compiler may - // not oblige and may, in fact, generate two distinct paths in any case. It might be - // possible to do uint64_t(p - start_exp - 1) >= 18 but it could end up trading off - // instructions for a simdjson_likely branch, an unconclusive gain. - - // If there were no digits, it's an error. - if (simdjson_unlikely(p == start_exp)) { - return INVALID_NUMBER(src); - } - // We have a valid positive exponent in exp_number at this point, except that - // it may have overflowed. - - // If there were more than 18 digits, we may have overflowed the integer. We have to do - // something!!!! - if (simdjson_unlikely(p > start_exp+18)) { - // Skip leading zeroes: 1e000000000000000000001 is technically valid and doesn't overflow - while (*start_exp == '0') { start_exp++; } - // 19 digits could overflow int64_t and is kind of absurd anyway. We don't - // support exponents smaller than -999,999,999,999,999,999 and bigger - // than 999,999,999,999,999,999. - // We can truncate. - // Note that 999999999999999999 is assuredly too large. The maximal ieee64 value before - // infinity is ~1.8e308. The smallest subnormal is ~5e-324. So, actually, we could - // truncate at 324. - // Note that there is no reason to fail per se at this point in time. - // E.g., 0e999999999999999999999 is a fine number. - if (p > start_exp+18) { exp_number = 999999999999999999; } - } - // At this point, we know that exp_number is a sane, positive, signed integer. - // It is <= 999,999,999,999,999,999. As long as 'exponent' is in - // [-8223372036854775808, 8223372036854775808], we won't overflow. Because 'exponent' - // is bounded in magnitude by the size of the JSON input, we are fine in this universe. - // To sum it up: the next line should never overflow. - exponent += (neg_exp ? -exp_number : exp_number); - return SUCCESS; -} - -simdjson_inline size_t significant_digits(const uint8_t * start_digits, size_t digit_count) { - // It is possible that the integer had an overflow. - // We have to handle the case where we have 0.0000somenumber. - const uint8_t *start = start_digits; - while ((*start == '0') || (*start == '.')) { ++start; } - // we over-decrement by one when there is a '.' - return digit_count - size_t(start - start_digits); -} - -template -simdjson_inline error_code write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer) { - // If we frequently had to deal with long strings of digits, - // we could extend our code by using a 128-bit integer instead - // of a 64-bit integer. However, this is uncommon in practice. - // - // 9999999999999999999 < 2**64 so we can accommodate 19 digits. - // If we have a decimal separator, then digit_count - 1 is the number of digits, but we - // may not have a decimal separator! - if (simdjson_unlikely(digit_count > 19 && significant_digits(start_digits, digit_count) > 19)) { - // Ok, chances are good that we had an overflow! - // this is almost never going to get called!!! - // we start anew, going slowly!!! - // This will happen in the following examples: - // 10000000000000000000000000000000000000000000e+308 - // 3.1415926535897932384626433832795028841971693993751 - // - // NOTE: This makes a *copy* of the writer and passes it to slow_float_parsing. This happens - // because slow_float_parsing is a non-inlined function. If we passed our writer reference to - // it, it would force it to be stored in memory, preventing the compiler from picking it apart - // and putting into registers. i.e. if we pass it as reference, it gets slow. - // This is what forces the skip_double, as well. - error_code error = slow_float_parsing(src, writer); - writer.skip_double(); - return error; - } - // NOTE: it's weird that the simdjson_unlikely() only wraps half the if, but it seems to get slower any other - // way we've tried: https://github.com/simdjson/simdjson/pull/990#discussion_r448497331 - // To future reader: we'd love if someone found a better way, or at least could explain this result! - if (simdjson_unlikely(exponent < simdjson::internal::smallest_power) || (exponent > simdjson::internal::largest_power)) { - // - // Important: smallest_power is such that it leads to a zero value. - // Observe that 18446744073709551615e-343 == 0, i.e. (2**64 - 1) e -343 is zero - // so something x 10^-343 goes to zero, but not so with something x 10^-342. - static_assert(simdjson::internal::smallest_power <= -342, "smallest_power is not small enough"); - // - if((exponent < simdjson::internal::smallest_power) || (i == 0)) { - WRITE_DOUBLE(0, src, writer); - return SUCCESS; - } else { // (exponent > largest_power) and (i != 0) - // We have, for sure, an infinite value and simdjson refuses to parse infinite values. - return INVALID_NUMBER(src); - } - } - double d; - if (!compute_float_64(exponent, i, negative, d)) { - // we are almost never going to get here. - if (!parse_float_fallback(src, &d)) { return INVALID_NUMBER(src); } - } - WRITE_DOUBLE(d, src, writer); - return SUCCESS; -} - -// for performance analysis, it is sometimes useful to skip parsing -#ifdef SIMDJSON_SKIPNUMBERPARSING - -template -simdjson_inline error_code parse_number(const uint8_t *const, W &writer) { - writer.append_s64(0); // always write zero - return SUCCESS; // always succeeds -} - -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * const src) noexcept { return 0; } -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { return false; } -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { return ondemand::number_type::signed_integer; } -#else - -// parse the number at src -// define JSON_TEST_NUMBERS for unit testing -// -// It is assumed that the number is followed by a structural ({,},],[) character -// or a white space character. If that is not the case (e.g., when the JSON -// document is made of a single number), then it is necessary to copy the -// content and append a space before calling this function. -// -// Our objective is accurate parsing (ULP of 0) at high speed. -template -simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - if (digit_count == 0 || ('0' == *start_digits && digit_count > 1)) { return INVALID_NUMBER(src); } - - // - // Handle floats if there is a . or e (or both) - // - int64_t exponent = 0; - bool is_float = false; - if ('.' == *p) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_decimal(src, p, i, exponent) ); - digit_count = int(p - start_digits); // used later to guard against overflows - } - if (('e' == *p) || ('E' == *p)) { - is_float = true; - ++p; - SIMDJSON_TRY( parse_exponent(src, p, exponent) ); - } - if (is_float) { - const bool dirty_end = jsoncharutils::is_not_structural_or_whitespace(*p); - SIMDJSON_TRY( write_float(src, negative, i, start_digits, digit_count, exponent, writer) ); - if (dirty_end) { return INVALID_NUMBER(src); } - return SUCCESS; - } - - // The longest negative 64-bit number is 19 digits. - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - size_t longest_digit_count = negative ? 19 : 20; - if (digit_count > longest_digit_count) { return INVALID_NUMBER(src); } - if (digit_count == longest_digit_count) { - if (negative) { - // Anything negative above INT64_MAX+1 is invalid - if (i > uint64_t(INT64_MAX)+1) { return INVALID_NUMBER(src); } - WRITE_INTEGER(~i+1, src, writer); - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - } else if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INVALID_NUMBER(src); } - } - - // Write unsigned if it doesn't fit in a signed integer. - if (i > uint64_t(INT64_MAX)) { - WRITE_UNSIGNED(i, src, writer); - } else { - WRITE_INTEGER(negative ? (~i+1) : i, src, writer); - } - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return INVALID_NUMBER(src); } - return SUCCESS; -} - -// Inlineable functions -namespace { - -// This table can be used to characterize the final character of an integer -// string. For JSON structural character and allowable white space characters, -// we return SUCCESS. For 'e', '.' and 'E', we return INCORRECT_TYPE. Otherwise -// we return NUMBER_ERROR. -// Optimization note: we could easily reduce the size of the table by half (to 128) -// at the cost of an extra branch. -// Optimization note: we want the values to use at most 8 bits (not, e.g., 32 bits): -static_assert(error_code(uint8_t(NUMBER_ERROR))== NUMBER_ERROR, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(SUCCESS))== SUCCESS, "bad NUMBER_ERROR cast"); -static_assert(error_code(uint8_t(INCORRECT_TYPE))== INCORRECT_TYPE, "bad NUMBER_ERROR cast"); - -const uint8_t integer_string_finisher[256] = { - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, INCORRECT_TYPE, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, SUCCESS, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, INCORRECT_TYPE, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, SUCCESS, NUMBER_ERROR, - SUCCESS, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, NUMBER_ERROR, - NUMBER_ERROR}; - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - - -// Parse any number from 0 to 18,446,744,073,709,551,615 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_unsigned(const uint8_t * const src, const uint8_t * const src_end) noexcept { - const uint8_t *p = src; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if ((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - if (src[0] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from 0 to 18,446,744,073,709,551,615 -simdjson_unused simdjson_inline simdjson_result parse_unsigned_in_string(const uint8_t * const src) noexcept { - const uint8_t *p = src + 1; - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // The longest positive 64-bit number is 20 digits. - // We do it this way so we don't trigger this branch unless we must. - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > 20)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > 20)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if (*p != '"') { return NUMBER_ERROR; } - - if (digit_count == 20) { - // Positive overflow check: - // - A 20 digit number starting with 2-9 is overflow, because 18,446,744,073,709,551,615 is the - // biggest uint64_t. - // - A 20 digit number starting with 1 is overflow if it is less than INT64_MAX. - // If we got here, it's a 20 digit number starting with the digit "1". - // - If a 20 digit number starting with 1 overflowed (i*10+digit), the result will be smaller - // than 1,553,255,926,290,448,384. - // - That is smaller than the smallest possible 20-digit number the user could write: - // 10,000,000,000,000,000,000. - // - Therefore, if the number is positive and lower than that, it's overflow. - // - The value we are looking at is less than or equal to INT64_MAX. - // - // Note: we use src[1] and not src[0] because src[0] is the quote character in this - // instance. - if (src[1] != uint8_t('1') || i <= uint64_t(INT64_MAX)) { return INCORRECT_TYPE; } - } - - return i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_integer(const uint8_t * const src, const uint8_t * const src_end) noexcept { - // - // Check for minus sign - // - if(src == src_end) { return NUMBER_ERROR; } - bool negative = (*src == '-'); - const uint8_t *p = src + negative; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if((p != src_end) && integer_string_finisher[*p] != SUCCESS) { return error_code(integer_string_finisher[*p]); } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -// Parse any number from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 -simdjson_unused simdjson_inline simdjson_result parse_integer_in_string(const uint8_t *src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - const uint8_t *p = src + negative + 1; - - // - // Parse the integer part. - // - // PERF NOTE: we don't use is_made_of_eight_digits_fast because large integers like 123456789 are rare - const uint8_t *const start_digits = p; - uint64_t i = 0; - while (parse_digit(*p, i)) { p++; } - - // If there were no digits, or if the integer starts with 0 and has more than one digit, it's an error. - // Optimization note: size_t is expected to be unsigned. - size_t digit_count = size_t(p - start_digits); - // We go from - // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 - // so we can never represent numbers that have more than 19 digits. - size_t longest_digit_count = 19; - // Optimization note: the compiler can probably merge - // ((digit_count == 0) || (digit_count > longest_digit_count)) - // into a single branch since digit_count is unsigned. - if ((digit_count == 0) || (digit_count > longest_digit_count)) { return INCORRECT_TYPE; } - // Here digit_count > 0. - if (('0' == *start_digits) && (digit_count > 1)) { return NUMBER_ERROR; } - // We can do the following... - // if (!jsoncharutils::is_structural_or_whitespace(*p)) { - // return (*p == '.' || *p == 'e' || *p == 'E') ? INCORRECT_TYPE : NUMBER_ERROR; - // } - // as a single table lookup: - if(*p != '"') { return NUMBER_ERROR; } - // Negative numbers have can go down to - INT64_MAX - 1 whereas positive numbers are limited to INT64_MAX. - // Performance note: This check is only needed when digit_count == longest_digit_count but it is - // so cheap that we might as well always make it. - if(i > uint64_t(INT64_MAX) + uint64_t(negative)) { return INCORRECT_TYPE; } - return negative ? (~i+1) : i; -} - -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline bool is_negative(const uint8_t * src) noexcept { - return (*src == '-'); -} - -simdjson_unused simdjson_inline simdjson_result is_integer(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { return true; } - return false; -} - -simdjson_unused simdjson_inline simdjson_result get_number_type(const uint8_t * src) noexcept { - bool negative = (*src == '-'); - src += negative; - const uint8_t *p = src; - while(static_cast(*p - '0') <= 9) { p++; } - if ( p == src ) { return NUMBER_ERROR; } - if (jsoncharutils::is_structural_or_whitespace(*p)) { - // We have an integer. - // If the number is negative and valid, it must be a signed integer. - if(negative) { return ondemand::number_type::signed_integer; } - // We want values larger or equal to 9223372036854775808 to be unsigned - // integers, and the other values to be signed integers. - int digit_count = int(p - src); - if(digit_count >= 19) { - const uint8_t * smaller_big_integer = reinterpret_cast("9223372036854775808"); - if((digit_count >= 20) || (memcmp(src, smaller_big_integer, 19) >= 0)) { - return ondemand::number_type::unsigned_integer; - } - } - return ondemand::number_type::signed_integer; - } - // Hopefully, we have 'e' or 'E' or '.'. - return ondemand::number_type::floating_point_number; -} - -// Never read at src_end or beyond -simdjson_unused simdjson_inline simdjson_result parse_double(const uint8_t * src, const uint8_t * const src_end) noexcept { - if(src == src_end) { return NUMBER_ERROR; } - // - // Check for minus sign - // - bool negative = (*src == '-'); - src += negative; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - if(p == src_end) { return NUMBER_ERROR; } - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while ((p != src_end) && parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely((p != src_end) && (*p == '.'))) { - p++; - const uint8_t *start_decimal_digits = p; - if ((p == src_end) || !parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while ((p != src_end) && parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if ((p != src_end) && (*p == 'e' || *p == 'E')) { - p++; - if(p == src_end) { return NUMBER_ERROR; } - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while ((p != src_end) && parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if ((p != src_end) && jsoncharutils::is_not_structural_or_whitespace(*p)) { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, src_end, &d)) { - return NUMBER_ERROR; - } - return d; -} - -simdjson_unused simdjson_inline simdjson_result parse_double_in_string(const uint8_t * src) noexcept { - // - // Check for minus sign - // - bool negative = (*(src + 1) == '-'); - src += negative + 1; - - // - // Parse the integer part. - // - uint64_t i = 0; - const uint8_t *p = src; - p += parse_digit(*p, i); - bool leading_zero = (i == 0); - while (parse_digit(*p, i)) { p++; } - // no integer digits, or 0123 (zero must be solo) - if ( p == src ) { return INCORRECT_TYPE; } - if ( (leading_zero && p != src+1)) { return NUMBER_ERROR; } - - // - // Parse the decimal part. - // - int64_t exponent = 0; - bool overflow; - if (simdjson_likely(*p == '.')) { - p++; - const uint8_t *start_decimal_digits = p; - if (!parse_digit(*p, i)) { return NUMBER_ERROR; } // no decimal digits - p++; - while (parse_digit(*p, i)) { p++; } - exponent = -(p - start_decimal_digits); - - // Overflow check. More than 19 digits (minus the decimal) may be overflow. - overflow = p-src-1 > 19; - if (simdjson_unlikely(overflow && leading_zero)) { - // Skip leading 0.00000 and see if it still overflows - const uint8_t *start_digits = src + 2; - while (*start_digits == '0') { start_digits++; } - overflow = start_digits-src > 19; - } - } else { - overflow = p-src > 19; - } - - // - // Parse the exponent - // - if (*p == 'e' || *p == 'E') { - p++; - bool exp_neg = *p == '-'; - p += exp_neg || *p == '+'; - - uint64_t exp = 0; - const uint8_t *start_exp_digits = p; - while (parse_digit(*p, exp)) { p++; } - // no exp digits, or 20+ exp digits - if (p-start_exp_digits == 0 || p-start_exp_digits > 19) { return NUMBER_ERROR; } - - exponent += exp_neg ? 0-exp : exp; - } - - if (*p != '"') { return NUMBER_ERROR; } - - overflow = overflow || exponent < simdjson::internal::smallest_power || exponent > simdjson::internal::largest_power; - - // - // Assemble (or slow-parse) the float - // - double d; - if (simdjson_likely(!overflow)) { - if (compute_float_64(exponent, i, negative, d)) { return d; } - } - if (!parse_float_fallback(src-negative, &d)) { - return NUMBER_ERROR; - } - return d; -} -} //namespace {} -#endif // SIMDJSON_SKIPNUMBERPARSING - -} // namespace numberparsing -} // unnamed namespace -} // namespace westmere -} // namespace simdjson -/* end file include/simdjson/generic/numberparsing.h */ - -#endif // SIMDJSON_WESTMERE_NUMBERPARSING_H -/* end file include/simdjson/westmere/numberparsing.h */ -/* begin file include/simdjson/westmere/end.h */ -SIMDJSON_UNTARGET_WESTMERE -/* end file include/simdjson/westmere/end.h */ - -#endif // SIMDJSON_IMPLEMENTATION_WESTMERE -#endif // SIMDJSON_WESTMERE_COMMON_H -/* end file include/simdjson/westmere.h */ - -// Builtin implementation - -SIMDJSON_POP_DISABLE_WARNINGS - -#endif // SIMDJSON_IMPLEMENTATIONS_H -/* end file include/simdjson/implementations.h */ - -// Determine the best builtin implementation -#ifndef SIMDJSON_BUILTIN_IMPLEMENTATION -#if SIMDJSON_CAN_ALWAYS_RUN_ICELAKE -#define SIMDJSON_BUILTIN_IMPLEMENTATION icelake -#elif SIMDJSON_CAN_ALWAYS_RUN_HASWELL -#define SIMDJSON_BUILTIN_IMPLEMENTATION haswell -#elif SIMDJSON_CAN_ALWAYS_RUN_WESTMERE -#define SIMDJSON_BUILTIN_IMPLEMENTATION westmere -#elif SIMDJSON_CAN_ALWAYS_RUN_ARM64 -#define SIMDJSON_BUILTIN_IMPLEMENTATION arm64 -#elif SIMDJSON_CAN_ALWAYS_RUN_PPC64 -#define SIMDJSON_BUILTIN_IMPLEMENTATION ppc64 -#elif SIMDJSON_CAN_ALWAYS_RUN_FALLBACK -#define SIMDJSON_BUILTIN_IMPLEMENTATION fallback -#else -#error "All possible implementations (including fallback) have been disabled! simdjson will not run." -#endif -#endif // SIMDJSON_BUILTIN_IMPLEMENTATION - -// redefining SIMDJSON_IMPLEMENTATION to "SIMDJSON_BUILTIN_IMPLEMENTATION" -// #define SIMDJSON_IMPLEMENTATION SIMDJSON_BUILTIN_IMPLEMENTATION - -// ondemand is only compiled as part of the builtin implementation at present - -// Interface declarations -/* begin file include/simdjson/generic/implementation_simdjson_result_base.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { - -// This is a near copy of include/error.h's implementation_simdjson_result_base, except it doesn't use std::pair -// so we can avoid inlining errors -// TODO reconcile these! -/** - * The result of a simdjson operation that could fail. - * - * Gives the option of reading error codes, or throwing an exception by casting to the desired result. - * - * This is a base class for implementations that want to add functions to the result type for - * chaining. - * - * Override like: - * - * struct simdjson_result : public internal::implementation_simdjson_result_base { - * simdjson_result() noexcept : internal::implementation_simdjson_result_base() {} - * simdjson_result(error_code error) noexcept : internal::implementation_simdjson_result_base(error) {} - * simdjson_result(T &&value) noexcept : internal::implementation_simdjson_result_base(std::forward(value)) {} - * simdjson_result(T &&value, error_code error) noexcept : internal::implementation_simdjson_result_base(value, error) {} - * // Your extra methods here - * } - * - * Then any method returning simdjson_result will be chainable with your methods. - */ -template -struct implementation_simdjson_result_base { - - /** - * Create a new empty result with error = UNINITIALIZED. - */ - simdjson_inline implementation_simdjson_result_base() noexcept = default; - - /** - * Create a new error result. - */ - simdjson_inline implementation_simdjson_result_base(error_code error) noexcept; - - /** - * Create a new successful result. - */ - simdjson_inline implementation_simdjson_result_base(T &&value) noexcept; - - /** - * Create a new result with both things (use if you don't want to branch when creating the result). - */ - simdjson_inline implementation_simdjson_result_base(T &&value, error_code error) noexcept; - - /** - * Move the value and the error to the provided variables. - * - * @param value The variable to assign the value to. May not be set if there is an error. - * @param error The variable to assign the error to. Set to SUCCESS if there is no error. - */ - simdjson_inline void tie(T &value, error_code &error) && noexcept; - - /** - * Move the value to the provided variable. - * - * @param value The variable to assign the value to. May not be set if there is an error. - */ - simdjson_inline error_code get(T &value) && noexcept; - - /** - * The error. - */ - simdjson_inline error_code error() const noexcept; - -#if SIMDJSON_EXCEPTIONS - - /** - * Get the result value. - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T& value() & noexcept(false); - - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& value() && noexcept(false); - - /** - * Take the result value (move it). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline T&& take_value() && noexcept(false); - - /** - * Cast to the value (will throw on error). - * - * @throw simdjson_error if there was an error. - */ - simdjson_inline operator T&&() && noexcept(false); - - -#endif // SIMDJSON_EXCEPTIONS - - /** - * Get the result value. This function is safe if and only - * the error() method returns a value that evaluates to false. - */ - simdjson_inline const T& value_unsafe() const& noexcept; - /** - * Get the result value. This function is safe if and only - * the error() method returns a value that evaluates to false. - */ - simdjson_inline T& value_unsafe() & noexcept; - /** - * Take the result value (move it). This function is safe if and only - * the error() method returns a value that evaluates to false. - */ - simdjson_inline T&& value_unsafe() && noexcept; -protected: - /** users should never directly access first and second. **/ - T first{}; /** Users should never directly access 'first'. **/ - error_code second{UNINITIALIZED}; /** Users should never directly access 'second'. **/ -}; // struct implementation_simdjson_result_base - -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson -/* end file include/simdjson/generic/implementation_simdjson_result_base.h */ -/* begin file include/simdjson/generic/ondemand.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -/** - * A fast, simple, DOM-like interface that parses JSON as you use it. - * - * Designed for maximum speed and a lower memory profile. - */ -namespace ondemand { - -/** Represents the depth of a JSON value (number of nested arrays/objects). */ -using depth_t = int32_t; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -/* begin file include/simdjson/generic/ondemand/json_type.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { -/** - * The type of a JSON value. - */ -enum class json_type { - // Start at 1 to catch uninitialized / default values more easily - array=1, ///< A JSON array ( [ 1, 2, 3 ... ] ) - object, ///< A JSON object ( { "a": 1, "b" 2, ... } ) - number, ///< A JSON number ( 1 or -2.3 or 4.5e6 ...) - string, ///< A JSON string ( "a" or "hello world\n" ...) - boolean, ///< A JSON boolean (true or false) - null ///< A JSON null (null) -}; - -class value_iterator; - -/** - * A type representing a JSON number. - * The design of the struct is deliberately straight-forward. All - * functions return standard values with no error check. - */ -struct number { - - /** - * return the automatically determined type of - * the number: number_type::floating_point_number, - * number_type::signed_integer or number_type::unsigned_integer. - * - * enum class number_type { - * floating_point_number=1, /// a binary64 number - * signed_integer, /// a signed integer that fits in a 64-bit word using two's complement - * unsigned_integer /// a positive integer larger or equal to 1<<63 - * }; - */ - simdjson_inline number_type get_number_type() const noexcept; - /** - * return true if the automatically determined type of - * the number is number_type::unsigned_integer. - */ - simdjson_inline bool is_uint64() const noexcept; - /** - * return the value as a uint64_t, only valid if is_uint64() is true. - */ - simdjson_inline uint64_t get_uint64() const noexcept; - simdjson_inline operator uint64_t() const noexcept; - - /** - * return true if the automatically determined type of - * the number is number_type::signed_integer. - */ - simdjson_inline bool is_int64() const noexcept; - /** - * return the value as a int64_t, only valid if is_int64() is true. - */ - simdjson_inline int64_t get_int64() const noexcept; - simdjson_inline operator int64_t() const noexcept; - - - /** - * return true if the automatically determined type of - * the number is number_type::floating_point_number. - */ - simdjson_inline bool is_double() const noexcept; - /** - * return the value as a double, only valid if is_double() is true. - */ - simdjson_inline double get_double() const noexcept; - simdjson_inline operator double() const noexcept; - - /** - * Convert the number to a double. Though it always succeed, the conversion - * may be lossy if the number cannot be represented exactly. - */ - simdjson_inline double as_double() const noexcept; - - -protected: - /** - * The next block of declaration is designed so that we can call the number parsing - * functions on a number type. They are protected and should never be used outside - * of the core simdjson library. - */ - friend class value_iterator; - template - friend error_code numberparsing::write_float(const uint8_t *const src, bool negative, uint64_t i, const uint8_t * start_digits, size_t digit_count, int64_t exponent, W &writer); - template - friend error_code numberparsing::parse_number(const uint8_t *const src, W &writer); - template - friend error_code numberparsing::slow_float_parsing(simdjson_unused const uint8_t * src, W writer); - /** Store a signed 64-bit value to the number. */ - simdjson_inline void append_s64(int64_t value) noexcept; - /** Store an unsigned 64-bit value to the number. */ - simdjson_inline void append_u64(uint64_t value) noexcept; - /** Store a double value to the number. */ - simdjson_inline void append_double(double value) noexcept; - /** Specifies that the value is a double, but leave it undefined. */ - simdjson_inline void skip_double() noexcept; - /** - * End of friend declarations. - */ - - /** - * Our attributes are a union type (size = 64 bits) - * followed by a type indicator. - */ - union { - double floating_point_number; - int64_t signed_integer; - uint64_t unsigned_integer; - } payload{0}; - number_type type{number_type::signed_integer}; -}; - -/** - * Write the JSON type to the output stream - * - * @param out The output stream. - * @param type The json_type. - */ -inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept; -inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept; - -#if SIMDJSON_EXCEPTIONS -/** - * Send JSON type to an output stream. - * - * @param out The output stream. - * @param type The json_type. - * @throw simdjson_error if the result being printed has an error. If there is an error with the - * underlying output stream, that error will be propagated (simdjson_error will not be - * thrown). - */ -inline std::ostream& operator<<(std::ostream& out, simdjson_result &type) noexcept(false); -#endif - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_type &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - simdjson_inline ~simdjson_result() noexcept = default; ///< @private -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/json_type.h */ -/* begin file include/simdjson/generic/ondemand/token_position.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -/** @private Position in the JSON buffer indexes */ -using token_position = const uint32_t *; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/token_position.h */ -/* begin file include/simdjson/generic/ondemand/logger.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class json_iterator; -class value_iterator; - -namespace logger { - -#if SIMDJSON_VERBOSE_LOGGING - static constexpr const bool LOG_ENABLED = true; -#else - static constexpr const bool LOG_ENABLED = false; -#endif - -// We do not want these functions to be 'really inlined' since real inlining is -// for performance purposes and if you are using the loggers, you do not care about -// performance (or should not). -static inline void log_headers() noexcept; -static inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail) noexcept; -static inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept; -static inline void log_event(const json_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept; -static inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept; -static inline void log_value(const json_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept; -static inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail="") noexcept; -static inline void log_start_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept; -static inline void log_end_value(const json_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept; -static inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail="") noexcept; -static inline void log_error(const json_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept; - -static inline void log_event(const value_iterator &iter, const char *type, std::string_view detail="", int delta=0, int depth_delta=0) noexcept; -static inline void log_value(const value_iterator &iter, const char *type, std::string_view detail="", int delta=-1, int depth_delta=0) noexcept; -static inline void log_start_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept; -static inline void log_end_value(const value_iterator &iter, const char *type, int delta=-1, int depth_delta=0) noexcept; -static inline void log_error(const value_iterator &iter, const char *error, const char *detail="", int delta=-1, int depth_delta=0) noexcept; - -} // namespace logger -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/logger.h */ -/* begin file include/simdjson/generic/ondemand/raw_json_string.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class object; -class parser; -class json_iterator; - -/** - * A string escaped per JSON rules, terminated with quote ("). They are used to represent - * unescaped keys inside JSON documents. - * - * (In other words, a pointer to the beginning of a string, just after the start quote, inside a - * JSON file.) - * - * This class is deliberately simplistic and has little functionality. You can - * compare a raw_json_string instance with an unescaped C string, but - * that is nearly all you can do. - * - * The raw_json_string is unescaped. If you wish to write an unescaped version of it to your own - * buffer, you may do so using the parser.unescape(string, buff) method, using an ondemand::parser - * instance. Doing so requires you to have a sufficiently large buffer. - * - * The raw_json_string instances originate typically from field instance which in turn represent - * key-value pairs from object instances. From a field instance, you get the raw_json_string - * instance by calling key(). You can, if you want a more usable string_view instance, call - * the unescaped_key() method on the field instance. You may also create a raw_json_string from - * any other string value, with the value.get_raw_json_string() method. Again, you can get - * a more usable string_view instance by calling get_string(). - * - */ -class raw_json_string { -public: - /** - * Create a new invalid raw_json_string. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline raw_json_string() noexcept = default; - - /** - * Create a new invalid raw_json_string pointed at the given location in the JSON. - * - * The given location must be just *after* the beginning quote (") in the JSON file. - * - * It *must* be terminated by a ", and be a valid JSON string. - */ - simdjson_inline raw_json_string(const uint8_t * _buf) noexcept; - /** - * Get the raw pointer to the beginning of the string in the JSON (just after the "). - * - * It is possible for this function to return a null pointer if the instance - * has outlived its existence. - */ - simdjson_inline const char * raw() const noexcept; - - /** - * This compares the current instance to the std::string_view target: returns true if - * they are byte-by-byte equal (no escaping is done) on target.size() characters, - * and if the raw_json_string instance has a quote character at byte index target.size(). - * We never read more than length + 1 bytes in the raw_json_string instance. - * If length is smaller than target.size(), this will return false. - * - * The std::string_view instance may contain any characters. However, the caller - * is responsible for setting length so that length bytes may be read in the - * raw_json_string. - * - * Performance: the comparison may be done using memcmp which may be efficient - * for long strings. - */ - simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept; - - /** - * This compares the current instance to the std::string_view target: returns true if - * they are byte-by-byte equal (no escaping is done). - * The std::string_view instance should not contain unescaped quote characters: - * the caller is responsible for this check. See is_free_from_unescaped_quote. - * - * Performance: the comparison is done byte-by-byte which might be inefficient for - * long strings. - * - * If target is a compile-time constant, and your compiler likes you, - * you should be able to do the following without performance penalty... - * - * static_assert(raw_json_string::is_free_from_unescaped_quote(target), ""); - * s.unsafe_is_equal(target); - */ - simdjson_inline bool unsafe_is_equal(std::string_view target) const noexcept; - - /** - * This compares the current instance to the C string target: returns true if - * they are byte-by-byte equal (no escaping is done). - * The provided C string should not contain an unescaped quote character: - * the caller is responsible for this check. See is_free_from_unescaped_quote. - * - * If target is a compile-time constant, and your compiler likes you, - * you should be able to do the following without performance penalty... - * - * static_assert(raw_json_string::is_free_from_unescaped_quote(target), ""); - * s.unsafe_is_equal(target); - */ - simdjson_inline bool unsafe_is_equal(const char* target) const noexcept; - - /** - * This compares the current instance to the std::string_view target: returns true if - * they are byte-by-byte equal (no escaping is done). - */ - simdjson_inline bool is_equal(std::string_view target) const noexcept; - - /** - * This compares the current instance to the C string target: returns true if - * they are byte-by-byte equal (no escaping is done). - */ - simdjson_inline bool is_equal(const char* target) const noexcept; - - /** - * Returns true if target is free from unescaped quote. If target is known at - * compile-time, we might expect the computation to happen at compile time with - * many compilers (not all!). - */ - static simdjson_inline bool is_free_from_unescaped_quote(std::string_view target) noexcept; - static simdjson_inline bool is_free_from_unescaped_quote(const char* target) noexcept; - -private: - - - /** - * This will set the inner pointer to zero, effectively making - * this instance unusable. - */ - simdjson_inline void consume() noexcept { buf = nullptr; } - - /** - * Checks whether the inner pointer is non-null and thus usable. - */ - simdjson_inline simdjson_warn_unused bool alive() const noexcept { return buf != nullptr; } - - /** - * Unescape this JSON string, replacing \\ with \, \n with newline, etc. - * - * ## IMPORTANT: string_view lifetime - * - * The string_view is only valid until the next parse() call on the parser. - * - * @param iter A json_iterator, which contains a buffer where the string will be written. - */ - simdjson_inline simdjson_warn_unused simdjson_result unescape(json_iterator &iter) const noexcept; - - const uint8_t * buf{}; - friend class object; - friend class field; - friend class parser; - friend struct simdjson_result; -}; - -simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &, const raw_json_string &) noexcept; - -/** - * Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user is responsible - * for providing a string with no unescaped quote. Note that unescaped quotes cannot be present in valid JSON strings. - */ -simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept; -simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept; -simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept; -simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept; - - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - simdjson_inline ~simdjson_result() noexcept = default; ///< @private - - simdjson_inline simdjson_result raw() const noexcept; - simdjson_inline simdjson_warn_unused simdjson_result unescape(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/raw_json_string.h */ -/* begin file include/simdjson/generic/ondemand/token_iterator.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -/** - * Iterates through JSON tokens (`{` `}` `[` `]` `,` `:` `""` `123` `true` `false` `null`) - * detected by stage 1. - * - * @private This is not intended for external use. - */ -class token_iterator { -public: - /** - * Create a new invalid token_iterator. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline token_iterator() noexcept = default; - simdjson_inline token_iterator(token_iterator &&other) noexcept = default; - simdjson_inline token_iterator &operator=(token_iterator &&other) noexcept = default; - simdjson_inline token_iterator(const token_iterator &other) noexcept = default; - simdjson_inline token_iterator &operator=(const token_iterator &other) noexcept = default; - - /** - * Advance to the next token (returning the current one). - */ - simdjson_inline const uint8_t *return_current_and_advance() noexcept; - /** - * Reports the current offset in bytes from the start of the underlying buffer. - */ - simdjson_inline uint32_t current_offset() const noexcept; - /** - * Get the JSON text for a given token (relative). - * - * This is not null-terminated; it is a view into the JSON. - * - * @param delta The relative position of the token to retrieve. e.g. 0 = current token, - * 1 = next token, -1 = prev token. - * - * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... - */ - simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; - /** - * Get the maximum length of the JSON text for a given token. - * - * The length will include any whitespace at the end of the token. - * - * @param delta The relative position of the token to retrieve. e.g. 0 = current token, - * 1 = next token, -1 = prev token. - */ - simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept; - - /** - * Get the JSON text for a given token. - * - * This is not null-terminated; it is a view into the JSON. - * - * @param position The position of the token. - * - */ - simdjson_inline const uint8_t *peek(token_position position) const noexcept; - /** - * Get the maximum length of the JSON text for a given token. - * - * The length will include any whitespace at the end of the token. - * - * @param position The position of the token. - */ - simdjson_inline uint32_t peek_length(token_position position) const noexcept; - - /** - * Return the current index. - */ - simdjson_inline token_position position() const noexcept; - /** - * Reset to a previously saved index. - */ - simdjson_inline void set_position(token_position target_position) noexcept; - - // NOTE: we don't support a full C++ iterator interface, because we expect people to make - // different calls to advance the iterator based on *their own* state. - - simdjson_inline bool operator==(const token_iterator &other) const noexcept; - simdjson_inline bool operator!=(const token_iterator &other) const noexcept; - simdjson_inline bool operator>(const token_iterator &other) const noexcept; - simdjson_inline bool operator>=(const token_iterator &other) const noexcept; - simdjson_inline bool operator<(const token_iterator &other) const noexcept; - simdjson_inline bool operator<=(const token_iterator &other) const noexcept; - -protected: - simdjson_inline token_iterator(const uint8_t *buf, token_position position) noexcept; - - /** - * Get the index of the JSON text for a given token (relative). - * - * This is not null-terminated; it is a view into the JSON. - * - * @param delta The relative position of the token to retrieve. e.g. 0 = current token, - * 1 = next token, -1 = prev token. - */ - simdjson_inline uint32_t peek_index(int32_t delta=0) const noexcept; - /** - * Get the index of the JSON text for a given token. - * - * This is not null-terminated; it is a view into the JSON. - * - * @param position The position of the token. - * - */ - simdjson_inline uint32_t peek_index(token_position position) const noexcept; - - const uint8_t *buf{}; - token_position _position{}; - - friend class json_iterator; - friend class value_iterator; - friend class object; - friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept; - friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail) noexcept; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::token_iterator &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - simdjson_inline ~simdjson_result() noexcept = default; ///< @private -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/token_iterator.h */ -/* begin file include/simdjson/generic/ondemand/json_iterator.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class document; -class document_stream; -class object; -class array; -class value; -class raw_json_string; -class parser; - -/** - * Iterates through JSON tokens, keeping track of depth and string buffer. - * - * @private This is not intended for external use. - */ -class json_iterator { -protected: - token_iterator token{}; - ondemand::parser *parser{}; - /** - * Next free location in the string buffer. - * - * Used by raw_json_string::unescape() to have a place to unescape strings to. - */ - uint8_t *_string_buf_loc{}; - /** - * JSON error, if there is one. - * - * INCORRECT_TYPE and NO_SUCH_FIELD are *not* stored here, ever. - * - * PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first - * iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If - * this is not elided, we should make sure it's at least not using up a register. Failing that, - * we should store it in document so there's only one of them. - */ - error_code error{SUCCESS}; - /** - * Depth of the current token in the JSON. - * - * - 0 = finished with document - * - 1 = document root value (could be [ or {, not yet known) - * - 2 = , or } inside root array/object - * - 3 = key or value inside root array/object. - */ - depth_t _depth{}; - /** - * Beginning of the document indexes. - * Normally we have root == parser->implementation->structural_indexes.get() - * but this may differ, especially in streaming mode (where we have several - * documents); - */ - token_position _root{}; - /** - * Normally, a json_iterator operates over a single document, but in - * some cases, we may have a stream of documents. This attribute is meant - * as meta-data: the json_iterator works the same irrespective of the - * value of this attribute. - */ - bool _streaming{false}; - -public: - simdjson_inline json_iterator() noexcept = default; - simdjson_inline json_iterator(json_iterator &&other) noexcept; - simdjson_inline json_iterator &operator=(json_iterator &&other) noexcept; - simdjson_inline explicit json_iterator(const json_iterator &other) noexcept = default; - simdjson_inline json_iterator &operator=(const json_iterator &other) noexcept = default; - /** - * Skips a JSON value, whether it is a scalar, array or object. - */ - simdjson_warn_unused simdjson_inline error_code skip_child(depth_t parent_depth) noexcept; - - /** - * Tell whether the iterator is still at the start - */ - simdjson_inline bool at_root() const noexcept; - - /** - * Tell whether we should be expected to run in streaming - * mode (iterating over many documents). It is pure metadata - * that does not affect how the iterator works. It is used by - * start_root_array() and start_root_object(). - */ - simdjson_inline bool streaming() const noexcept; - - /** - * Get the root value iterator - */ - simdjson_inline token_position root_position() const noexcept; - /** - * Assert that we are at the document depth (== 1) - */ - simdjson_inline void assert_at_document_depth() const noexcept; - /** - * Assert that we are at the root of the document - */ - simdjson_inline void assert_at_root() const noexcept; - - /** - * Tell whether the iterator is at the EOF mark - */ - simdjson_inline bool at_end() const noexcept; - - /** - * Tell whether the iterator is live (has not been moved). - */ - simdjson_inline bool is_alive() const noexcept; - - /** - * Abandon this iterator, setting depth to 0 (as if the document is finished). - */ - simdjson_inline void abandon() noexcept; - - /** - * Advance the current token without modifying depth. - */ - simdjson_inline const uint8_t *return_current_and_advance() noexcept; - - /** - * Assert that there are at least the given number of tokens left. - * - * Has no effect in release builds. - */ - simdjson_inline void assert_more_tokens(uint32_t required_tokens=1) const noexcept; - /** - * Assert that the given position addresses an actual token (is within bounds). - * - * Has no effect in release builds. - */ - simdjson_inline void assert_valid_position(token_position position) const noexcept; - /** - * Get the JSON text for a given token (relative). - * - * This is not null-terminated; it is a view into the JSON. - * - * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. - * - * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... - */ - simdjson_inline const uint8_t *peek(int32_t delta=0) const noexcept; - /** - * Get the maximum length of the JSON text for the current token (or relative). - * - * The length will include any whitespace at the end of the token. - * - * @param delta The relative position of the token to retrieve. e.g. 0 = next token, -1 = prev token. - */ - simdjson_inline uint32_t peek_length(int32_t delta=0) const noexcept; - /** - * Get a pointer to the current location in the input buffer. - * - * This is not null-terminated; it is a view into the JSON. - * - * You may be pointing outside of the input buffer: it is not generally - * safe to dereference this pointer. - */ - simdjson_inline const uint8_t *unsafe_pointer() const noexcept; - /** - * Get the JSON text for a given token. - * - * This is not null-terminated; it is a view into the JSON. - * - * @param position The position of the token to retrieve. - * - * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... - */ - simdjson_inline const uint8_t *peek(token_position position) const noexcept; - /** - * Get the maximum length of the JSON text for the current token (or relative). - * - * The length will include any whitespace at the end of the token. - * - * @param position The position of the token to retrieve. - */ - simdjson_inline uint32_t peek_length(token_position position) const noexcept; - /** - * Get the JSON text for the last token in the document. - * - * This is not null-terminated; it is a view into the JSON. - * - * TODO consider a string_view, assuming the length will get stripped out by the optimizer when - * it isn't used ... - */ - simdjson_inline const uint8_t *peek_last() const noexcept; - - /** - * Ascend one level. - * - * Validates that the depth - 1 == parent_depth. - * - * @param parent_depth the expected parent depth. - */ - simdjson_inline void ascend_to(depth_t parent_depth) noexcept; - - /** - * Descend one level. - * - * Validates that the new depth == child_depth. - * - * @param child_depth the expected child depth. - */ - simdjson_inline void descend_to(depth_t child_depth) noexcept; - simdjson_inline void descend_to(depth_t child_depth, int32_t delta) noexcept; - - /** - * Get current depth. - */ - simdjson_inline depth_t depth() const noexcept; - - /** - * Get current (writeable) location in the string buffer. - */ - simdjson_inline uint8_t *&string_buf_loc() noexcept; - - /** - * Report an unrecoverable error, preventing further iteration. - * - * @param error The error to report. Must not be SUCCESS, UNINITIALIZED, INCORRECT_TYPE, or NO_SUCH_FIELD. - * @param message An error message to report with the error. - */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; - - /** - * Log error, but don't stop iteration. - * @param error The error to report. Must be INCORRECT_TYPE, or NO_SUCH_FIELD. - * @param message An error message to report with the error. - */ - simdjson_inline error_code optional_error(error_code error, const char *message) noexcept; - - template simdjson_warn_unused simdjson_inline bool copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t (&tmpbuf)[N]) noexcept; - - simdjson_inline token_position position() const noexcept; - /** - * Write the raw_json_string to the string buffer and return a string_view. - * Each raw_json_string should be unescaped once, or else the string buffer might - * overflow. - */ - simdjson_inline simdjson_result unescape(raw_json_string in) noexcept; - simdjson_inline void reenter_child(token_position position, depth_t child_depth) noexcept; - -#if SIMDJSON_DEVELOPMENT_CHECKS - simdjson_inline token_position start_position(depth_t depth) const noexcept; - simdjson_inline void set_start_position(depth_t depth, token_position position) noexcept; -#endif - - /* Useful for debugging and logging purposes. */ - inline std::string to_string() const noexcept; - - /** - * Returns the current location in the document if in bounds. - */ - inline simdjson_result current_location() noexcept; - - /** - * Updates this json iterator so that it is back at the beginning of the document, - * as if it had just been created. - */ - inline void rewind() noexcept; - /** - * This checks whether the {,},[,] are balanced so that the document - * ends with proper zero depth. This requires scanning the whole document - * and it may be expensive. It is expected that it will be rarely called. - * It does not attempt to match { with } and [ with ]. - */ - inline bool balanced() const noexcept; -protected: - simdjson_inline json_iterator(const uint8_t *buf, ondemand::parser *parser) noexcept; - /// The last token before the end - simdjson_inline token_position last_position() const noexcept; - /// The token *at* the end. This points at gibberish and should only be used for comparison. - simdjson_inline token_position end_position() const noexcept; - /// The end of the buffer. - simdjson_inline token_position end() const noexcept; - - friend class document; - friend class document_stream; - friend class object; - friend class array; - friend class value; - friend class raw_json_string; - friend class parser; - friend class value_iterator; - friend simdjson_inline void logger::log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept; - friend simdjson_inline void logger::log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail) noexcept; -}; // json_iterator - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - - simdjson_inline simdjson_result() noexcept = default; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/json_iterator.h */ -/* begin file include/simdjson/generic/ondemand/value_iterator.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class document; -class object; -class array; -class value; -class raw_json_string; -class parser; - -/** - * Iterates through a single JSON value at a particular depth. - * - * Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects - * the caller to call the right ones. - * - * @private This is not intended for external use. - */ -class value_iterator { -protected: - /** The underlying JSON iterator */ - json_iterator *_json_iter{}; - /** The depth of this value */ - depth_t _depth{}; - /** - * The starting token index for this value - */ - token_position _start_position{}; - -public: - simdjson_inline value_iterator() noexcept = default; - - /** - * Denote that we're starting a document. - */ - simdjson_inline void start_document() noexcept; - - /** - * Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object. - * - * Optimized for scalars. - */ - simdjson_warn_unused simdjson_inline error_code skip_child() noexcept; - - /** - * Tell whether the iterator is at the EOF mark - */ - simdjson_inline bool at_end() const noexcept; - - /** - * Tell whether the iterator is at the start of the value - */ - simdjson_inline bool at_start() const noexcept; - - /** - * Tell whether the value is open--if the value has not been used, or the array/object is still open. - */ - simdjson_inline bool is_open() const noexcept; - - /** - * Tell whether the value is at an object's first field (just after the {). - */ - simdjson_inline bool at_first_field() const noexcept; - - /** - * Abandon all iteration. - */ - simdjson_inline void abandon() noexcept; - - /** - * Get the child value as a value_iterator. - */ - simdjson_inline value_iterator child_value() const noexcept; - - /** - * Get the depth of this value. - */ - simdjson_inline int32_t depth() const noexcept; - - /** - * Get the JSON type of this value. - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result type() const noexcept; - - /** - * @addtogroup object Object iteration - * - * Methods to iterate and find object fields. These methods generally *assume* the value is - * actually an object; the caller is responsible for keeping track of that fact. - * - * @{ - */ - - /** - * Start an object iteration. - * - * @returns Whether the object had any fields (returns false for empty). - * @error INCORRECT_TYPE if there is no opening { - */ - simdjson_warn_unused simdjson_inline simdjson_result start_object() noexcept; - /** - * Start an object iteration from the root. - * - * @returns Whether the object had any fields (returns false for empty). - * @error INCORRECT_TYPE if there is no opening { - * @error TAPE_ERROR if there is no matching } at end of document - */ - simdjson_warn_unused simdjson_inline simdjson_result start_root_object() noexcept; - - /** - * Start an object iteration after the user has already checked and moved past the {. - * - * Does not move the iterator unless the object is empty ({}). - * - * @returns Whether the object had any fields (returns false for empty). - * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent* - * array or object is incomplete). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_object() noexcept; - /** - * Start an object iteration from the root, after the user has already checked and moved past the {. - * - * Does not move the iterator unless the object is empty ({}). - * - * @returns Whether the object had any fields (returns false for empty). - * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent* - * array or object is incomplete). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_object() noexcept; - - /** - * Moves to the next field in an object. - * - * Looks for , and }. If } is found, the object is finished and the iterator advances past it. - * Otherwise, it advances to the next value. - * - * @return whether there is another field in the object. - * @error TAPE_ERROR If there is a comma missing between fields. - * @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value. - */ - simdjson_warn_unused simdjson_inline simdjson_result has_next_field() noexcept; - - /** - * Get the current field's key. - */ - simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; - - /** - * Pass the : in the field and move to its value. - */ - simdjson_warn_unused simdjson_inline error_code field_value() noexcept; - - /** - * Find the next field with the given key. - * - * Assumes you have called next_field() or otherwise matched the previous value. - * - * This means the iterator must be sitting at the next key: - * - * ``` - * { "a": 1, "b": 2 } - * ^ - * ``` - * - * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to - * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may - * fail to match some keys with escapes (\u, \n, etc.). - */ - simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept; - - /** - * Find the next field with the given key, *without* unescaping. This assumes object order: it - * will not find the field if it was already passed when looking for some *other* field. - * - * Assumes you have called next_field() or otherwise matched the previous value. - * - * This means the iterator must be sitting at the next key: - * - * ``` - * { "a": 1, "b": 2 } - * ^ - * ``` - * - * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to - * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may - * fail to match some keys with escapes (\u, \n, etc.). - */ - simdjson_warn_unused simdjson_inline simdjson_result find_field_raw(const std::string_view key) noexcept; - - /** - * Find the field with the given key without regard to order, and *without* unescaping. - * - * This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning. - * - * Assumes you have called next_field() or otherwise matched the previous value. - * - * This means the iterator must be sitting at the next key: - * - * ``` - * { "a": 1, "b": 2 } - * ^ - * ``` - * - * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to - * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may - * fail to match some keys with escapes (\u, \n, etc.). - */ - simdjson_warn_unused simdjson_inline simdjson_result find_field_unordered_raw(const std::string_view key) noexcept; - - /** @} */ - - /** - * @addtogroup array Array iteration - * Methods to iterate over array elements. These methods generally *assume* the value is actually - * an object; the caller is responsible for keeping track of that fact. - * @{ - */ - - /** - * Check for an opening [ and start an array iteration. - * - * @returns Whether the array had any elements (returns false for empty). - * @error INCORRECT_TYPE If there is no [. - */ - simdjson_warn_unused simdjson_inline simdjson_result start_array() noexcept; - /** - * Check for an opening [ and start an array iteration while at the root. - * - * @returns Whether the array had any elements (returns false for empty). - * @error INCORRECT_TYPE If there is no [. - * @error TAPE_ERROR if there is no matching ] at end of document - */ - simdjson_warn_unused simdjson_inline simdjson_result start_root_array() noexcept; - - /** - * Start an array iteration, after the user has already checked and moved past the [. - * - * Does not move the iterator unless the array is empty ([]). - * - * @returns Whether the array had any elements (returns false for empty). - * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent* - * array or object is incomplete). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_array() noexcept; - /** - * Start an array iteration from the root, after the user has already checked and moved past the [. - * - * Does not move the iterator unless the array is empty ([]). - * - * @returns Whether the array had any elements (returns false for empty). - * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent* - * array or object is incomplete). - */ - simdjson_warn_unused simdjson_inline simdjson_result started_root_array() noexcept; - - /** - * Moves to the next element in an array. - * - * Looks for , and ]. If ] is found, the array is finished and the iterator advances past it. - * Otherwise, it advances to the next value. - * - * @return Whether there is another element in the array. - * @error TAPE_ERROR If there is a comma missing between elements. - */ - simdjson_warn_unused simdjson_inline simdjson_result has_next_element() noexcept; - - /** - * Get a child value iterator. - */ - simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; - - /** @} */ - - /** - * @defgroup scalar Scalar values - * @addtogroup scalar - * @{ - */ - - simdjson_warn_unused simdjson_inline simdjson_result get_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline bool is_null() noexcept; - simdjson_warn_unused simdjson_inline bool is_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - - simdjson_warn_unused simdjson_inline simdjson_result get_root_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_bool() noexcept; - simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result is_root_integer() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type() noexcept; - simdjson_warn_unused simdjson_inline simdjson_result get_root_number() noexcept; - simdjson_inline bool is_root_null() noexcept; - - simdjson_inline error_code error() const noexcept; - simdjson_inline uint8_t *&string_buf_loc() noexcept; - simdjson_inline const json_iterator &json_iter() const noexcept; - simdjson_inline json_iterator &json_iter() noexcept; - - simdjson_inline void assert_is_valid() const noexcept; - simdjson_inline bool is_valid() const noexcept; - - /** @} */ -protected: - /** - * Restarts an array iteration. - * @returns Whether the array has any elements (returns false for empty). - */ - simdjson_inline simdjson_result reset_array() noexcept; - /** - * Restarts an object iteration. - * @returns Whether the object has any fields (returns false for empty). - */ - simdjson_inline simdjson_result reset_object() noexcept; - /** - * move_at_start(): moves us so that we are pointing at the beginning of - * the container. It updates the index so that at_start() is true and it - * syncs the depth. The user can then create a new container instance. - * - * Usage: used with value::count_elements(). - **/ - simdjson_inline void move_at_start() noexcept; - - /** - * move_at_container_start(): moves us so that we are pointing at the beginning of - * the container so that assert_at_container_start() passes. - * - * Usage: used with reset_array() and reset_object(). - **/ - simdjson_inline void move_at_container_start() noexcept; - /* Useful for debugging and logging purposes. */ - inline std::string to_string() const noexcept; - simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept; - - simdjson_inline bool parse_null(const uint8_t *json) const noexcept; - simdjson_inline simdjson_result parse_bool(const uint8_t *json) const noexcept; - simdjson_inline const uint8_t *peek_start() const noexcept; - simdjson_inline uint32_t peek_start_length() const noexcept; - - /** - * The general idea of the advance_... methods and the peek_* methods - * is that you first peek and check that you have desired type. If you do, - * and only if you do, then you advance. - * - * We used to unconditionally advance. But this made reasoning about our - * current state difficult. - * Suppose you always advance. Look at the 'value' matching the key - * "shadowable" in the following example... - * - * ({"globals":{"a":{"shadowable":[}}}}) - * - * If the user thinks it is a Boolean and asks for it, then we check the '[', - * decide it is not a Boolean, but still move into the next character ('}'). Now - * we are left pointing at '}' right after a '['. And we have not yet reported - * an error, only that we do not have a Boolean. - * - * If, instead, you just stand your ground until it is content that you know, then - * you will only even move beyond the '[' if the user tells you that you have an - * array. So you will be at the '}' character inside the array and, hopefully, you - * will then catch the error because an array cannot start with '}', but the code - * processing Boolean values does not know this. - * - * So the contract is: first call 'peek_...' and then call 'advance_...' only - * if you have determined that it is a type you can handle. - * - * Unfortunately, it makes the code more verbose, longer and maybe more error prone. - */ - - simdjson_inline void advance_scalar(const char *type) noexcept; - simdjson_inline void advance_root_scalar(const char *type) noexcept; - simdjson_inline void advance_non_root_scalar(const char *type) noexcept; - - simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept; - simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept; - simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; - - - simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; - simdjson_inline error_code end_container() noexcept; - - /** - * Advance to a place expecting a value (increasing depth). - * - * @return The current token (the one left behind). - * @error TAPE_ERROR If the document ended early. - */ - simdjson_inline simdjson_result advance_to_value() noexcept; - - simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; - simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; - - simdjson_inline bool is_at_start() const noexcept; - /** - * is_at_iterator_start() returns true on an array or object after it has just been - * created, whether the instance is empty or not. - * - * Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS) - */ - simdjson_inline bool is_at_iterator_start() const noexcept; - - /** - * Assuming that we are within an object, this returns true if we - * are pointing at a key. - * - * Usage: the skip_child() method should never be used while we are pointing - * at a key inside an object. - */ - simdjson_inline bool is_at_key() const noexcept; - - inline void assert_at_start() const noexcept; - inline void assert_at_container_start() const noexcept; - inline void assert_at_root() const noexcept; - inline void assert_at_child() const noexcept; - inline void assert_at_next() const noexcept; - inline void assert_at_non_root_start() const noexcept; - - /** Get the starting position of this value */ - simdjson_inline token_position start_position() const noexcept; - - /** @copydoc error_code json_iterator::position() const noexcept; */ - simdjson_inline token_position position() const noexcept; - /** @copydoc error_code json_iterator::end_position() const noexcept; */ - simdjson_inline token_position last_position() const noexcept; - /** @copydoc error_code json_iterator::end_position() const noexcept; */ - simdjson_inline token_position end_position() const noexcept; - /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ - simdjson_inline error_code report_error(error_code error, const char *message) noexcept; - - friend class document; - friend class object; - friend class array; - friend class value; -}; // value_iterator - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value_iterator &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/value_iterator.h */ -/* begin file include/simdjson/generic/ondemand/array_iterator.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class array; -class value; -class document; - -/** - * A forward-only JSON array. - * - * This is an input_iterator, meaning: - * - It is forward-only - * - * must be called exactly once per element. - * - ++ must be called exactly once in between each * (*, ++, *, ++, * ...) - */ -class array_iterator { -public: - /** Create a new, invalid array iterator. */ - simdjson_inline array_iterator() noexcept = default; - - // - // Iterator interface - // - - /** - * Get the current element. - * - * Part of the std::iterator interface. - */ - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - /** - * Check if we are at the end of the JSON. - * - * Part of the std::iterator interface. - * - * @return true if there are no more elements in the JSON array. - */ - simdjson_inline bool operator==(const array_iterator &) const noexcept; - /** - * Check if there are more elements in the JSON array. - * - * Part of the std::iterator interface. - * - * @return true if there are more elements in the JSON array. - */ - simdjson_inline bool operator!=(const array_iterator &) const noexcept; - /** - * Move to the next element. - * - * Part of the std::iterator interface. - */ - simdjson_inline array_iterator &operator++() noexcept; - -private: - value_iterator iter{}; - - simdjson_inline array_iterator(const value_iterator &iter) noexcept; - - friend class array; - friend class value; - friend struct simdjson_result; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array_iterator &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - - // - // Iterator interface - // - - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline bool operator==(const simdjson_result &) const noexcept; - simdjson_inline bool operator!=(const simdjson_result &) const noexcept; - simdjson_inline simdjson_result &operator++() noexcept; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/array_iterator.h */ -/* begin file include/simdjson/generic/ondemand/object_iterator.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class field; - -class object_iterator { -public: - /** - * Create a new invalid object_iterator. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline object_iterator() noexcept = default; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - // MUST ONLY BE CALLED ONCE PER ITERATION. - simdjson_inline simdjson_result operator*() noexcept; - // Assumes it's being compared with the end. true if depth < iter->depth. - simdjson_inline bool operator==(const object_iterator &) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const object_iterator &) const noexcept; - // Checks for ']' and ',' - simdjson_inline object_iterator &operator++() noexcept; - -private: - /** - * The underlying JSON iterator. - * - * PERF NOTE: expected to be elided in favor of the parent document: this is set when the object - * is first used, and never changes afterwards. - */ - value_iterator iter{}; - - simdjson_inline object_iterator(const value_iterator &iter) noexcept; - friend struct simdjson_result; - friend class object; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object_iterator &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - simdjson_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - // Assumes it's being compared with the end. true if depth < iter->depth. - simdjson_inline bool operator==(const simdjson_result &) const noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_inline bool operator!=(const simdjson_result &) const noexcept; - // Checks for ']' and ',' - simdjson_inline simdjson_result &operator++() noexcept; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/object_iterator.h */ -/* begin file include/simdjson/generic/ondemand/array.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class value; -class document; - -/** - * A forward-only JSON array. - */ -class array { -public: - /** - * Create a new invalid array. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline array() noexcept = default; - - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result end() noexcept; - /** - * This method scans the array and counts the number of elements. - * The count_elements method should always be called before you have begun - * iterating through the array: it is expected that you are pointing at - * the beginning of the array. - * The runtime complexity is linear in the size of the array. After - * calling this function, if successful, the array is 'rewinded' at its - * beginning as if it had never been accessed. If the JSON is malformed (e.g., - * there is a missing comma), then an error is returned and it is no longer - * safe to continue. - * - * To check that an array is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result count_elements() & noexcept; - /** - * This method scans the beginning of the array and checks whether the - * array is empty. - * The runtime complexity is constant time. After - * calling this function, if successful, the array is 'rewinded' at its - * beginning as if it had never been accessed. If the JSON is malformed (e.g., - * there is a missing comma), then an error is returned and it is no longer - * safe to continue. - */ - simdjson_inline simdjson_result is_empty() & noexcept; - /** - * Reset the iterator so that we are pointing back at the - * beginning of the array. You should still consume values only once even if you - * can iterate through the array more than once. If you unescape a string - * within the array more than once, you have unsafe code. Note that rewinding - * an array means that you may need to reparse it anew: it is not a free - * operation. - * - * @returns true if the array contains some elements (not empty) - */ - inline simdjson_result reset() & noexcept; - /** - * Get the value associated with the given JSON pointer. We use the RFC 6901 - * https://tools.ietf.org/html/rfc6901 standard, interpreting the current node - * as the root of its own JSON document. - * - * ondemand::parser parser; - * auto json = R"([ { "foo": { "a": [ 10, 20, 30 ] }} ])"_padded; - * auto doc = parser.iterate(json); - * doc.at_pointer("/0/foo/a/1") == 20 - * - * Note that at_pointer() called on the document automatically calls the document's rewind - * method between each call. It invalidates all previously accessed arrays, objects and values - * that have not been consumed. Yet it is not the case when calling at_pointer on an array - * instance: there is no rewind and no invalidation. - * - * You may only call at_pointer on an array after it has been created, but before it has - * been first accessed. When calling at_pointer on an array, the pointer is advanced to - * the location indicated by the JSON pointer (in case of success). It is no longer possible - * to call at_pointer on the same array. - * - * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching. - * - * @return The value associated with the given JSON pointer, or: - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed - */ - inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - /** - * Consumes the array and returns a string_view instance corresponding to the - * array as represented in JSON. It points inside the original document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - - /** - * Get the value at the given index. This function has linear-time complexity. - * This function should only be called once on an array instance since the array iterator is not reset between each call. - * - * @return The value at the given index, or: - * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length - */ - simdjson_inline simdjson_result at(size_t index) noexcept; -protected: - /** - * Go to the end of the array, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - - /** - * Begin array iteration. - * - * @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the - * resulting array. - * @error INCORRECT_TYPE if the iterator is not at [. - */ - static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; - /** - * Begin array iteration from the root. - * - * @param iter The iterator. Must be where the initial [ is expected. Will be *moved* into the - * resulting array. - * @error INCORRECT_TYPE if the iterator is not at [. - * @error TAPE_ERROR if there is no closing ] at the end of the document. - */ - static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; - /** - * Begin array iteration. - * - * This version of the method should be called after the initial [ has been verified, and is - * intended for use by switch statements that check the type of a value. - * - * @param iter The iterator. Must be after the initial [. Will be *moved* into the resulting array. - */ - static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; - - /** - * Create an array at the given Internal array creation. Call array::start() or array::started() instead of this. - * - * @param iter The iterator. Must either be at the start of the first element with iter.is_alive() - * == true, or past the [] with is_alive() == false if the array is empty. Will be *moved* - * into the resulting array. - */ - simdjson_inline array(const value_iterator &iter) noexcept; - - /** - * Iterator marking current position. - * - * iter.is_alive() == false indicates iteration is complete. - */ - value_iterator iter{}; - - friend class value; - friend class document; - friend struct simdjson_result; - friend struct simdjson_result; - friend class array_iterator; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - inline simdjson_result count_elements() & noexcept; - inline simdjson_result is_empty() & noexcept; - inline simdjson_result reset() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/array.h */ -/* begin file include/simdjson/generic/ondemand/document.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class parser; -class array; -class object; -class value; -class raw_json_string; -class array_iterator; -class document_stream; - -/** - * A JSON document. It holds a json_iterator instance. - * - * Used by tokens to get text, and string buffer location. - * - * You must keep the document around during iteration. - */ -class document { -public: - /** - * Create a new invalid document. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline document() noexcept = default; - simdjson_inline document(const document &other) noexcept = delete; // pass your documents by reference, not by copy - simdjson_inline document(document &&other) noexcept = default; - simdjson_inline document &operator=(const document &other) noexcept = delete; - simdjson_inline document &operator=(document &&other) noexcept = default; - - /** - * Cast this JSON value to an array. - * - * @returns An object that can be used to iterate the array. - * @returns INCORRECT_TYPE If the JSON value is not an array. - */ - simdjson_inline simdjson_result get_array() & noexcept; - /** - * Cast this JSON value to an object. - * - * @returns An object that can be used to look up or iterate fields. - * @returns INCORRECT_TYPE If the JSON value is not an object. - */ - simdjson_inline simdjson_result get_object() & noexcept; - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - /** - * Cast this JSON value (inside string) to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - /** - * Cast this JSON value to a signed integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer. - */ - simdjson_inline simdjson_result get_int64() noexcept; - /** - * Cast this JSON value (inside string) to a signed integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer. - */ - simdjson_inline simdjson_result get_int64_in_string() noexcept; - /** - * Cast this JSON value to a double. - * - * @returns A double. - * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number. - */ - simdjson_inline simdjson_result get_double() noexcept; - - /** - * Cast this JSON value (inside string) to a double. - * - * @returns A double. - * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number. - */ - simdjson_inline simdjson_result get_double_in_string() noexcept; - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Important: Calling get_string() twice on the same document is an error. - * - * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next - * time it parses a document or when it is destroyed. - * @returns INCORRECT_TYPE if the JSON value is not a string. - */ - simdjson_inline simdjson_result get_string() noexcept; - /** - * Cast this JSON value to a raw_json_string. - * - * The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n). - * - * @returns A pointer to the raw JSON for the given string. - * @returns INCORRECT_TYPE if the JSON value is not a string. - */ - simdjson_inline simdjson_result get_raw_json_string() noexcept; - /** - * Cast this JSON value to a bool. - * - * @returns A bool value. - * @returns INCORRECT_TYPE if the JSON value is not true or false. - */ - simdjson_inline simdjson_result get_bool() noexcept; - /** - * Cast this JSON value to a value when the document is an object or an array. - * - * @returns A value if a JSON array or object cannot be found. - * @returns SCALAR_DOCUMENT_AS_VALUE error is the document is a scalar (see is_scalar() function). - */ - simdjson_inline simdjson_result get_value() noexcept; - - /** - * Checks if this JSON value is null. - * - * @returns Whether the value is null. - */ - simdjson_inline bool is_null() noexcept; - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * get_object(), get_array(), get_raw_json_string(), or get_string() instead. - * - * @returns A value of the given type, parsed from the JSON. - * @returns INCORRECT_TYPE If the JSON value is not the given type. - */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value - * - * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. - * - * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. - * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. - */ - template simdjson_inline error_code get(T &out) & noexcept; - /** @overload template error_code get(T &out) & noexcept */ - template simdjson_inline error_code get(T &out) && noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an array. - * - * @returns An object that can be used to iterate the array. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array. - */ - simdjson_inline operator array() & noexcept(false); - /** - * Cast this JSON value to an object. - * - * @returns An object that can be used to look up or iterate fields. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object. - */ - simdjson_inline operator object() & noexcept(false); - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline operator uint64_t() noexcept(false); - /** - * Cast this JSON value to a signed integer. - * - * @returns A signed 64-bit integer. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer. - */ - simdjson_inline operator int64_t() noexcept(false); - /** - * Cast this JSON value to a double. - * - * @returns A double. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number. - */ - simdjson_inline operator double() noexcept(false); - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next - * time it parses a document or when it is destroyed. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. - */ - simdjson_inline operator std::string_view() noexcept(false); - /** - * Cast this JSON value to a raw_json_string. - * - * The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n). - * - * @returns A pointer to the raw JSON for the given string. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. - */ - simdjson_inline operator raw_json_string() noexcept(false); - /** - * Cast this JSON value to a bool. - * - * @returns A bool value. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false. - */ - simdjson_inline operator bool() noexcept(false); - /** - * Cast this JSON value to a value. - * - * @returns A value value. - * @exception if a JSON value cannot be found - */ - simdjson_inline operator value() noexcept(false); -#endif - /** - * This method scans the array and counts the number of elements. - * The count_elements method should always be called before you have begun - * iterating through the array: it is expected that you are pointing at - * the beginning of the array. - * The runtime complexity is linear in the size of the array. After - * calling this function, if successful, the array is 'rewinded' at its - * beginning as if it had never been accessed. If the JSON is malformed (e.g., - * there is a missing comma), then an error is returned and it is no longer - * safe to continue. - */ - simdjson_inline simdjson_result count_elements() & noexcept; - /** - * This method scans the object and counts the number of key-value pairs. - * The count_fields method should always be called before you have begun - * iterating through the object: it is expected that you are pointing at - * the beginning of the object. - * The runtime complexity is linear in the size of the object. After - * calling this function, if successful, the object is 'rewinded' at its - * beginning as if it had never been accessed. If the JSON is malformed (e.g., - * there is a missing comma), then an error is returned and it is no longer - * safe to continue. - * - * To check that an object is empty, it is more performant to use - * the is_empty() method. - */ - simdjson_inline simdjson_result count_fields() & noexcept; - /** - * Get the value at the given index in the array. This function has linear-time complexity. - * This function should only be called once on an array instance since the array iterator is not reset between each call. - * - * @return The value at the given index, or: - * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length - */ - simdjson_inline simdjson_result at(size_t index) & noexcept; - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the - * JSON `{ "x": 1, "y": 2, "z": 3 }`: - * - * ```c++ - * simdjson::ondemand::parser parser; - * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); - * double z = obj.find_field("z"); - * double y = obj.find_field("y"); - * double x = obj.find_field("x"); - * ``` - * - * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. - * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. - * - * - * You must consume the fields on an object one at a time. A request for a new key - * invalidates previous field values: it makes them unsafe. E.g., the array - * given by content["bids"].get_array() should not be accessed after you have called - * content["asks"].get_array(). You can detect such mistakes by first compiling and running - * the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an - * OUT_OF_ORDER_ITERATION error is generated. - * - * You are expected to access keys only once. You should access the value corresponding to - * a key a single time. Doing object["mykey"].to_string()and then again object["mykey"].to_string() - * is an error. - * - * @param key The key to look up. - * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. - */ - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field(const char *key) & noexcept; - - /** - * Look up a field by name on an object, without regard to key order. - * - * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies - * and often appears negligible. It starts out normally, starting out at the last field; but if - * the field is not found, it scans from the beginning of the object to see if it missed it. That - * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object - * in question is large. The fact that the extra code is there also bumps the executable size. - * - * It is the default, however, because it would be highly surprising (and hard to debug) if the - * default behavior failed to look up a field just because it was in the wrong order--and many - * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order. - * - * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the - * field wasn't there when they aren't). - * - * You must consume the fields on an object one at a time. A request for a new key - * invalidates previous field values: it makes them unsafe. E.g., the array - * given by content["bids"].get_array() should not be accessed after you have called - * content["asks"].get_array(). You can detect such mistakes by first compiling and running - * the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an - * OUT_OF_ORDER_ITERATION error is generated. - * - * You are expected to access keys only once. You should access the value corresponding to a key - * a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() - * is an error. - * - * @param key The key to look up. - * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. - */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](const char *key) & noexcept; - - /** - * Get the type of this JSON value. - * - * NOTE: If you're only expecting a value to be one type (a typical case), it's generally - * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just - * let it throw an exception). - * - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result type() noexcept; - - /** - * Checks whether the document is a scalar (string, number, null, Boolean). - * Returns false when there it is an array or object. - * - * @returns true if the type is string, number, null, Boolean - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_scalar() noexcept; - - /** - * Checks whether the document is a negative number. - * - * @returns true if the number if negative. - */ - simdjson_inline bool is_negative() noexcept; - /** - * Checks whether the document is an integer number. Note that - * this requires to partially parse the number string. If - * the value is determined to be an integer, it may still - * not parse properly as an integer in subsequent steps - * (e.g., it might overflow). - * - * @returns true if the number if negative. - */ - simdjson_inline simdjson_result is_integer() noexcept; - /** - * Determine the number type (integer or floating-point number) as quickly - * as possible. This function does not fully validate the input. It is - * useful when you only need to classify the numbers, without parsing them. - * - * If you are planning to retrieve the value or you need full validation, - * consider using the get_number() method instead: it will fully parse - * and validate the input, and give you access to the type: - * get_number().get_number_type(). - * - * get_number_type() is number_type::unsigned_integer if we have - * an integer greater or equal to 9223372036854775808 - * get_number_type() is number_type::signed_integer if we have an - * integer that is less than 9223372036854775808 - * Otherwise, get_number_type() has value number_type::floating_point_number - * - * This function requires processing the number string, but it is expected - * to be faster than get_number().get_number_type() because it is does not - * parse the number value. - * - * @returns the type of the number - */ - simdjson_inline simdjson_result get_number_type() noexcept; - - /** - * Attempt to parse an ondemand::number. An ondemand::number may - * contain an integer value or a floating-point value, the simdjson - * library will autodetect the type. Thus it is a dynamically typed - * number. Before accessing the value, you must determine the detected - * type. - * - * number.get_number_type() is number_type::signed_integer if we have - * an integer in [-9223372036854775808,9223372036854775808) - * You can recover the value by calling number.get_int64() and you - * have that number.is_int64() is true. - * - * number.get_number_type() is number_type::unsigned_integer if we have - * an integer in [9223372036854775808,18446744073709551616) - * You can recover the value by calling number.get_uint64() and you - * have that number.is_uint64() is true. - * - * Otherwise, number.get_number_type() has value number_type::floating_point_number - * and we have a binary64 number. - * You can recover the value by calling number.get_double() and you - * have that number.is_double() is true. - * - * You must check the type before accessing the value: it is an error - * to call "get_int64()" when number.get_number_type() is not - * number_type::signed_integer and when number.is_int64() is false. - */ - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - - /** - * Get the raw JSON for this token. - * - * The string_view will always point into the input buffer. - * - * The string_view will start at the beginning of the token, and include the entire token - * *as well as all spaces until the next token (or EOF).* This means, for example, that a - * string token always begins with a " and is always terminated by the final ", possibly - * followed by a number of spaces. - * - * The string_view is *not* null-terminated. If this is a scalar (string, number, - * boolean, or null), the character after the end of the string_view may be the padded buffer. - * - * Tokens include: - * - { - * - [ - * - "a string (possibly with UTF-8 or backslashed characters like \\\")". - * - -1.2e-100 - * - true - * - false - * - null - */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - /** - * Reset the iterator inside the document instance so we are pointing back at the - * beginning of the document, as if it had just been created. It invalidates all - * values, objects and arrays that you have created so far (including unescaped strings). - */ - inline void rewind() noexcept; - /** - * Returns debugging information. - */ - inline std::string to_debug_string() noexcept; - /** - * Some unrecoverable error conditions may render the document instance unusable. - * The is_alive() method returns true when the document is still suitable. - */ - inline bool is_alive() noexcept; - - /** - * Returns the current location in the document if in bounds. - */ - inline simdjson_result current_location() noexcept; - - /** - * Returns the current depth in the document if in bounds. - * - * E.g., - * 0 = finished with document - * 1 = document root value (could be [ or {, not yet known) - * 2 = , or } inside root array/object - * 3 = key or value inside root array/object. - */ - simdjson_inline int32_t current_depth() const noexcept; - - /** - * Get the value associated with the given JSON pointer. We use the RFC 6901 - * https://tools.ietf.org/html/rfc6901 standard. - * - * ondemand::parser parser; - * auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded; - * auto doc = parser.iterate(json); - * doc.at_pointer("/foo/a/1") == 20 - * - * It is allowed for a key to be the empty string: - * - * ondemand::parser parser; - * auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded; - * auto doc = parser.iterate(json); - * doc.at_pointer("//a/1") == 20 - * - * Note that at_pointer() automatically calls rewind between each call. Thus - * all values, objects and arrays that you have created so far (including unescaped strings) - * are invalidated. After calling at_pointer, you need to consume the result: string values - * should be stored in your own variables, arrays should be decoded and stored in your own array-like - * structures and so forth. - * - * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching - * - * @return The value associated with the given JSON pointer, or: - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed - * - SCALAR_DOCUMENT_AS_VALUE if the json_pointer is empty and the document is not a scalar (see is_scalar() function). - */ - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - /** - * Consumes the document and returns a string_view instance corresponding to the - * document as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; -protected: - /** - * Consumes the document. - */ - simdjson_inline error_code consume() noexcept; - - simdjson_inline document(ondemand::json_iterator &&iter) noexcept; - simdjson_inline const uint8_t *text(uint32_t idx) const noexcept; - - simdjson_inline value_iterator resume_value_iterator() noexcept; - simdjson_inline value_iterator get_root_value_iterator() noexcept; - simdjson_inline simdjson_result start_or_resume_object() noexcept; - static simdjson_inline document start(ondemand::json_iterator &&iter) noexcept; - - // - // Fields - // - json_iterator iter{}; ///< Current position in the document - static constexpr depth_t DOCUMENT_DEPTH = 0; ///< document depth is always 0 - - friend class array_iterator; - friend class value; - friend class ondemand::parser; - friend class object; - friend class array; - friend class field; - friend class token; - friend class document_stream; -}; - - -/** - * A document_reference is a thin wrapper around a document reference instance. - */ -class document_reference { -public: - simdjson_inline document_reference() noexcept; - simdjson_inline document_reference(document &d) noexcept; - simdjson_inline document_reference(const document_reference &other) noexcept = default; - simdjson_inline document_reference& operator=(const document_reference &other) noexcept = default; - simdjson_inline void rewind() noexcept; - simdjson_inline simdjson_result get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - - simdjson_inline bool is_null() noexcept; - simdjson_inline simdjson_result raw_json() noexcept; - simdjson_inline operator document&() const noexcept; - -#if SIMDJSON_EXCEPTIONS - simdjson_inline operator array() & noexcept(false); - simdjson_inline operator object() & noexcept(false); - simdjson_inline operator uint64_t() noexcept(false); - simdjson_inline operator int64_t() noexcept(false); - simdjson_inline operator double() noexcept(false); - simdjson_inline operator std::string_view() noexcept(false); - simdjson_inline operator raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char *key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char *key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; - - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - simdjson_inline simdjson_result raw_json_token() noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; -private: - document *doc{nullptr}; -}; -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - simdjson_inline error_code rewind() noexcept; - - simdjson_inline simdjson_result get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_from_string() noexcept; - simdjson_inline simdjson_result get_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline bool is_null() noexcept; - - template simdjson_inline simdjson_result get() & noexcept; - template simdjson_inline simdjson_result get() && noexcept; - - template simdjson_inline error_code get(T &out) & noexcept; - template simdjson_inline error_code get(T &out) && noexcept; - -#if SIMDJSON_EXCEPTIONS - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object() & noexcept(false); - simdjson_inline operator uint64_t() noexcept(false); - simdjson_inline operator int64_t() noexcept(false); - simdjson_inline operator double() noexcept(false); - simdjson_inline operator std::string_view() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char *key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char *key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; -}; - - -} // namespace simdjson - - - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_reference value, error_code error) noexcept; - simdjson_inline simdjson_result() noexcept = default; - simdjson_inline error_code rewind() noexcept; - - simdjson_inline simdjson_result get_array() & noexcept; - simdjson_inline simdjson_result get_object() & noexcept; - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline simdjson_result get_value() noexcept; - simdjson_inline bool is_null() noexcept; - -#if SIMDJSON_EXCEPTIONS - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object() & noexcept(false); - simdjson_inline operator uint64_t() noexcept(false); - simdjson_inline operator int64_t() noexcept(false); - simdjson_inline operator double() noexcept(false); - simdjson_inline operator std::string_view() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) & noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(const char *key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](const char *key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result current_location() noexcept; - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline bool is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - /** @copydoc simdjson_inline std::string_view document_reference::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; -}; - - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/document.h */ -/* begin file include/simdjson/generic/ondemand/value.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class array; -class document; -class field; -class object; -class raw_json_string; - -/** - * An ephemeral JSON value returned during iteration. It is only valid for as long as you do - * not access more data in the JSON document. - */ -class value { -public: - /** - * Create a new invalid value. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline value() noexcept = default; - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * You may use get_double(), get_bool(), get_uint64(), get_int64(), - * get_object(), get_array(), get_raw_json_string(), or get_string() instead. - * - * @returns A value of the given type, parsed from the JSON. - * @returns INCORRECT_TYPE If the JSON value is not the given type. - */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library."); - } - - /** - * Get this value as the given type. - * - * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool - * - * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. - * @returns INCORRECT_TYPE If the JSON value is not an object. - * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. - */ - template simdjson_inline error_code get(T &out) noexcept; - - /** - * Cast this JSON value to an array. - * - * @returns An object that can be used to iterate the array. - * @returns INCORRECT_TYPE If the JSON value is not an array. - */ - simdjson_inline simdjson_result get_array() noexcept; - - /** - * Cast this JSON value to an object. - * - * @returns An object that can be used to look up or iterate fields. - * @returns INCORRECT_TYPE If the JSON value is not an object. - */ - simdjson_inline simdjson_result get_object() noexcept; - - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A unsigned 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64() noexcept; - - /** - * Cast this JSON value (inside string) to a unsigned integer. - * - * @returns A unsigned 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - - /** - * Cast this JSON value to a signed integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer. - */ - simdjson_inline simdjson_result get_int64() noexcept; - - /** - * Cast this JSON value (inside string) to a signed integer. - * - * @returns A signed 64-bit integer. - * @returns INCORRECT_TYPE If the JSON value is not a 64-bit integer. - */ - simdjson_inline simdjson_result get_int64_in_string() noexcept; - - /** - * Cast this JSON value to a double. - * - * @returns A double. - * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number. - */ - simdjson_inline simdjson_result get_double() noexcept; - - /** - * Cast this JSON value (inside string) to a double - * - * @returns A double. - * @returns INCORRECT_TYPE If the JSON value is not a valid floating-point number. - */ - simdjson_inline simdjson_result get_double_in_string() noexcept; - - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * Important: a value should be consumed once. Calling get_string() twice on the same value - * is an error. - * - * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next - * time it parses a document or when it is destroyed. - * @returns INCORRECT_TYPE if the JSON value is not a string. - */ - simdjson_inline simdjson_result get_string() noexcept; - - /** - * Cast this JSON value to a raw_json_string. - * - * The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n). - * - * @returns A pointer to the raw JSON for the given string. - * @returns INCORRECT_TYPE if the JSON value is not a string. - */ - simdjson_inline simdjson_result get_raw_json_string() noexcept; - - /** - * Cast this JSON value to a bool. - * - * @returns A bool value. - * @returns INCORRECT_TYPE if the JSON value is not true or false. - */ - simdjson_inline simdjson_result get_bool() noexcept; - - /** - * Checks if this JSON value is null. - * - * @returns Whether the value is null. - */ - simdjson_inline bool is_null() noexcept; - -#if SIMDJSON_EXCEPTIONS - /** - * Cast this JSON value to an array. - * - * @returns An object that can be used to iterate the array. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an array. - */ - simdjson_inline operator array() noexcept(false); - /** - * Cast this JSON value to an object. - * - * @returns An object that can be used to look up or iterate fields. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not an object. - */ - simdjson_inline operator object() noexcept(false); - /** - * Cast this JSON value to an unsigned integer. - * - * @returns A signed 64-bit integer. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit unsigned integer. - */ - simdjson_inline operator uint64_t() noexcept(false); - /** - * Cast this JSON value to a signed integer. - * - * @returns A signed 64-bit integer. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a 64-bit integer. - */ - simdjson_inline operator int64_t() noexcept(false); - /** - * Cast this JSON value to a double. - * - * @returns A double. - * @exception simdjson_error(INCORRECT_TYPE) If the JSON value is not a valid floating-point number. - */ - simdjson_inline operator double() noexcept(false); - /** - * Cast this JSON value to a string. - * - * The string is guaranteed to be valid UTF-8. - * - * Equivalent to get(). - * - * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next - * time it parses a document or when it is destroyed. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. - */ - simdjson_inline operator std::string_view() noexcept(false); - /** - * Cast this JSON value to a raw_json_string. - * - * The string is guaranteed to be valid UTF-8, and may have escapes in it (e.g. \\ or \n). - * - * @returns A pointer to the raw JSON for the given string. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not a string. - */ - simdjson_inline operator raw_json_string() noexcept(false); - /** - * Cast this JSON value to a bool. - * - * @returns A bool value. - * @exception simdjson_error(INCORRECT_TYPE) if the JSON value is not true or false. - */ - simdjson_inline operator bool() noexcept(false); -#endif - - /** - * Begin array iteration. - * - * Part of the std::iterable interface. - * - * @returns INCORRECT_TYPE If the JSON value is not an array. - */ - simdjson_inline simdjson_result begin() & noexcept; - /** - * Sentinel representing the end of the array. - * - * Part of the std::iterable interface. - */ - simdjson_inline simdjson_result end() & noexcept; - /** - * This method scans the array and counts the number of elements. - * The count_elements method should always be called before you have begun - * iterating through the array: it is expected that you are pointing at - * the beginning of the array. - * The runtime complexity is linear in the size of the array. After - * calling this function, if successful, the array is 'rewinded' at its - * beginning as if it had never been accessed. If the JSON is malformed (e.g., - * there is a missing comma), then an error is returned and it is no longer - * safe to continue. - * - * Performance hint: You should only call count_elements() as a last - * resort as it may require scanning the document twice or more. - */ - simdjson_inline simdjson_result count_elements() & noexcept; - /** - * This method scans the object and counts the number of key-value pairs. - * The count_fields method should always be called before you have begun - * iterating through the object: it is expected that you are pointing at - * the beginning of the object. - * The runtime complexity is linear in the size of the object. After - * calling this function, if successful, the object is 'rewinded' at its - * beginning as if it had never been accessed. If the JSON is malformed (e.g., - * there is a missing comma), then an error is returned and it is no longer - * safe to continue. - * - * To check that an object is empty, it is more performant to use - * the is_empty() method on the object instance. - * - * Performance hint: You should only call count_fields() as a last - * resort as it may require scanning the document twice or more. - */ - simdjson_inline simdjson_result count_fields() & noexcept; - /** - * Get the value at the given index in the array. This function has linear-time complexity. - * This function should only be called once on an array instance since the array iterator is not reset between each call. - * - * @return The value at the given index, or: - * - INDEX_OUT_OF_BOUNDS if the array index is larger than an array length - */ - simdjson_inline simdjson_result at(size_t index) noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the - * JSON `{ "x": 1, "y": 2, "z": 3 }`: - * - * ```c++ - * simdjson::ondemand::parser parser; - * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); - * double z = obj.find_field("z"); - * double y = obj.find_field("y"); - * double x = obj.find_field("x"); - * ``` - * If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful - * that only one field is returned. - - * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. - * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. - * - * @param key The key to look up. - * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. - */ - simdjson_inline simdjson_result find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field(const char *key) noexcept; - - /** - * Look up a field by name on an object, without regard to key order. - * - * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies - * and often appears negligible. It starts out normally, starting out at the last field; but if - * the field is not found, it scans from the beginning of the object to see if it missed it. That - * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object - * in question is large. The fact that the extra code is there also bumps the executable size. - * - * It is the default, however, because it would be highly surprising (and hard to debug) if the - * default behavior failed to look up a field just because it was in the wrong order--and many - * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order. - * - * If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful - * that only one field is returned. - * - * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the - * field wasn't there when they aren't). - * - * @param key The key to look up. - * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. - */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char *key) noexcept; - - /** - * Get the type of this JSON value. - * - * NOTE: If you're only expecting a value to be one type (a typical case), it's generally - * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just - * let it throw an exception). - * - * @return The type of JSON value (json_type::array, json_type::object, json_type::string, - * json_type::number, json_type::boolean, or json_type::null). - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result type() noexcept; - - /** - * Checks whether the value is a scalar (string, number, null, Boolean). - * Returns false when there it is an array or object. - * - * @returns true if the type is string, number, null, Boolean - * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". - */ - simdjson_inline simdjson_result is_scalar() noexcept; - - /** - * Checks whether the value is a negative number. - * - * @returns true if the number if negative. - */ - simdjson_inline bool is_negative() noexcept; - /** - * Checks whether the value is an integer number. Note that - * this requires to partially parse the number string. If - * the value is determined to be an integer, it may still - * not parse properly as an integer in subsequent steps - * (e.g., it might overflow). - * - * Performance note: if you call this function systematically - * before parsing a number, you may have fallen for a performance - * anti-pattern. - * - * @returns true if the number if negative. - */ - simdjson_inline simdjson_result is_integer() noexcept; - /** - * Determine the number type (integer or floating-point number) as quickly - * as possible. This function does not fully validate the input. It is - * useful when you only need to classify the numbers, without parsing them. - * - * If you are planning to retrieve the value or you need full validation, - * consider using the get_number() method instead: it will fully parse - * and validate the input, and give you access to the type: - * get_number().get_number_type(). - * - * get_number_type() is number_type::unsigned_integer if we have - * an integer greater or equal to 9223372036854775808 - * get_number_type() is number_type::signed_integer if we have an - * integer that is less than 9223372036854775808 - * Otherwise, get_number_type() has value number_type::floating_point_number - * - * This function requires processing the number string, but it is expected - * to be faster than get_number().get_number_type() because it is does not - * parse the number value. - * - * @returns the type of the number - */ - simdjson_inline simdjson_result get_number_type() noexcept; - - /** - * Attempt to parse an ondemand::number. An ondemand::number may - * contain an integer value or a floating-point value, the simdjson - * library will autodetect the type. Thus it is a dynamically typed - * number. Before accessing the value, you must determine the detected - * type. - * - * number.get_number_type() is number_type::signed_integer if we have - * an integer in [-9223372036854775808,9223372036854775808) - * You can recover the value by calling number.get_int64() and you - * have that number.is_int64() is true. - * - * number.get_number_type() is number_type::unsigned_integer if we have - * an integer in [9223372036854775808,18446744073709551616) - * You can recover the value by calling number.get_uint64() and you - * have that number.is_uint64() is true. - * - * Otherwise, number.get_number_type() has value number_type::floating_point_number - * and we have a binary64 number. - * You can recover the value by calling number.get_double() and you - * have that number.is_double() is true. - * - * You must check the type before accessing the value: it is an error - * to call "get_int64()" when number.get_number_type() is not - * number_type::signed_integer and when number.is_int64() is false. - * - * Performance note: this is designed with performance in mind. When - * calling 'get_number()', you scan the number string only once, determining - * efficiently the type and storing it in an efficient manner. - */ - simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; - - - /** - * Get the raw JSON for this token. - * - * The string_view will always point into the input buffer. - * - * The string_view will start at the beginning of the token, and include the entire token - * *as well as all spaces until the next token (or EOF).* This means, for example, that a - * string token always begins with a " and is always terminated by the final ", possibly - * followed by a number of spaces. - * - * The string_view is *not* null-terminated. However, if this is a scalar (string, number, - * boolean, or null), the character after the end of the string_view is guaranteed to be - * a non-space token. - * - * Tokens include: - * - { - * - [ - * - "a string (possibly with UTF-8 or backslashed characters like \\\")". - * - -1.2e-100 - * - true - * - false - * - null - */ - simdjson_inline std::string_view raw_json_token() noexcept; - - /** - * Returns the current location in the document if in bounds. - */ - simdjson_inline simdjson_result current_location() noexcept; - - /** - * Returns the current depth in the document if in bounds. - * - * E.g., - * 0 = finished with document - * 1 = document root value (could be [ or {, not yet known) - * 2 = , or } inside root array/object - * 3 = key or value inside root array/object. - */ - simdjson_inline int32_t current_depth() const noexcept; - - /** - * Get the value associated with the given JSON pointer. We use the RFC 6901 - * https://tools.ietf.org/html/rfc6901 standard. - * - * ondemand::parser parser; - * auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded; - * auto doc = parser.iterate(json); - * doc.at_pointer("/foo/a/1") == 20 - * - * It is allowed for a key to be the empty string: - * - * ondemand::parser parser; - * auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded; - * auto doc = parser.iterate(json); - * doc.at_pointer("//a/1") == 20 - * - * Note that at_pointer() called on the document automatically calls the document's rewind - * method between each call. It invalidates all previously accessed arrays, objects and values - * that have not been consumed. - * - * Calling at_pointer() on non-document instances (e.g., arrays and objects) is not - * standardized (by RFC 6901). We provide some experimental support for JSON pointers - * on non-document instances. Yet it is not the case when calling at_pointer on an array - * or an object instance: there is no rewind and no invalidation. - * - * You may only call at_pointer on an array after it has been created, but before it has - * been first accessed. When calling at_pointer on an array, the pointer is advanced to - * the location indicated by the JSON pointer (in case of success). It is no longer possible - * to call at_pointer on the same array. - * - * You may call at_pointer more than once on an object, but each time the pointer is advanced - * to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding - * key (as well as the current key) can no longer be used with following JSON pointer calls. - * - * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching - * - * @return The value associated with the given JSON pointer, or: - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed - */ - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - -protected: - /** - * Create a value. - */ - simdjson_inline value(const value_iterator &iter) noexcept; - - /** - * Skip this value, allowing iteration to continue. - */ - simdjson_inline void skip() noexcept; - - /** - * Start a value at the current position. - * - * (It should already be started; this is just a self-documentation method.) - */ - static simdjson_inline value start(const value_iterator &iter) noexcept; - - /** - * Resume a value. - */ - static simdjson_inline value resume(const value_iterator &iter) noexcept; - - /** - * Get the object, starting or resuming it as necessary - */ - simdjson_inline simdjson_result start_or_resume_object() noexcept; - - // simdjson_inline void log_value(const char *type) const noexcept; - // simdjson_inline void log_error(const char *message) const noexcept; - - value_iterator iter{}; - - friend class document; - friend class array_iterator; - friend class field; - friend class object; - friend struct simdjson_result; - friend struct simdjson_result; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - - simdjson_inline simdjson_result get_array() noexcept; - simdjson_inline simdjson_result get_object() noexcept; - - simdjson_inline simdjson_result get_uint64() noexcept; - simdjson_inline simdjson_result get_uint64_in_string() noexcept; - simdjson_inline simdjson_result get_int64() noexcept; - simdjson_inline simdjson_result get_int64_in_string() noexcept; - simdjson_inline simdjson_result get_double() noexcept; - simdjson_inline simdjson_result get_double_in_string() noexcept; - simdjson_inline simdjson_result get_string() noexcept; - simdjson_inline simdjson_result get_raw_json_string() noexcept; - simdjson_inline simdjson_result get_bool() noexcept; - simdjson_inline bool is_null() noexcept; - - template simdjson_inline simdjson_result get() noexcept; - - template simdjson_inline error_code get(T &out) noexcept; - -#if SIMDJSON_EXCEPTIONS - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object() noexcept(false); - simdjson_inline operator uint64_t() noexcept(false); - simdjson_inline operator int64_t() noexcept(false); - simdjson_inline operator double() noexcept(false); - simdjson_inline operator std::string_view() noexcept(false); - simdjson_inline operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false); - simdjson_inline operator bool() noexcept(false); -#endif - simdjson_inline simdjson_result count_elements() & noexcept; - simdjson_inline simdjson_result count_fields() & noexcept; - simdjson_inline simdjson_result at(size_t index) noexcept; - simdjson_inline simdjson_result begin() & noexcept; - simdjson_inline simdjson_result end() & noexcept; - - /** - * Look up a field by name on an object (order-sensitive). - * - * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the - * JSON `{ "x": 1, "y": 2, "z": 3 }`: - * - * ```c++ - * simdjson::ondemand::parser parser; - * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); - * double z = obj.find_field("z"); - * double y = obj.find_field("y"); - * double x = obj.find_field("x"); - * ``` - * - * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. - * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. - * - * @param key The key to look up. - * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. - */ - simdjson_inline simdjson_result find_field(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field(const char *key) noexcept; - - /** - * Look up a field by name on an object, without regard to key order. - * - * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies - * and often appears negligible. It starts out normally, starting out at the last field; but if - * the field is not found, it scans from the beginning of the object to see if it missed it. That - * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object - * in question is large. The fact that the extra code is there also bumps the executable size. - * - * It is the default, however, because it would be highly surprising (and hard to debug) if the - * default behavior failed to look up a field just because it was in the wrong order--and many - * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order. - * - * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the - * field wasn't there when they aren't). - * - * @param key The key to look up. - * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. - */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result find_field_unordered(const char *key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ - simdjson_inline simdjson_result operator[](const char *key) noexcept; - - /** - * Get the type of this JSON value. - * - * NOTE: If you're only expecting a value to be one type (a typical case), it's generally - * better to just call .get_double, .get_string, etc. and check for INCORRECT_TYPE (or just - * let it throw an exception). - */ - simdjson_inline simdjson_result type() noexcept; - simdjson_inline simdjson_result is_scalar() noexcept; - simdjson_inline simdjson_result is_negative() noexcept; - simdjson_inline simdjson_result is_integer() noexcept; - simdjson_inline simdjson_result get_number_type() noexcept; - simdjson_inline simdjson_result get_number() noexcept; - - /** @copydoc simdjson_inline std::string_view value::raw_json_token() const noexcept */ - simdjson_inline simdjson_result raw_json_token() noexcept; - - /** @copydoc simdjson_inline simdjson_result current_location() noexcept */ - simdjson_inline simdjson_result current_location() noexcept; - /** @copydoc simdjson_inline int32_t current_depth() const noexcept */ - simdjson_inline int32_t current_depth() const noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/value.h */ -/* begin file include/simdjson/generic/ondemand/field.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -/** - * A JSON field (key/value pair) in an object. - * - * Returned from object iteration. - * - * Extends from std::pair so you can use C++ algorithms that rely on pairs. - */ -class field : public std::pair { -public: - /** - * Create a new invalid field. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline field() noexcept; - - /** - * Get the key as a string_view (for higher speed, consider raw_key). - * We deliberately use a more cumbersome name (unescaped_key) to force users - * to think twice about using it. - * - * This consumes the key: once you have called unescaped_key(), you cannot - * call it again nor can you call key(). - */ - simdjson_inline simdjson_warn_unused simdjson_result unescaped_key() noexcept; - /** - * Get the key as a raw_json_string. Can be used for direct comparison with - * an unescaped C string: e.g., key() == "test". - */ - simdjson_inline raw_json_string key() const noexcept; - /** - * Get the field value. - */ - simdjson_inline ondemand::value &value() & noexcept; - /** - * @overload ondemand::value &ondemand::value() & noexcept - */ - simdjson_inline ondemand::value value() && noexcept; - -protected: - simdjson_inline field(raw_json_string key, ondemand::value &&value) noexcept; - static simdjson_inline simdjson_result start(value_iterator &parent_iter) noexcept; - static simdjson_inline simdjson_result start(const value_iterator &parent_iter, raw_json_string key) noexcept; - friend struct simdjson_result; - friend class object_iterator; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::field &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - - simdjson_inline simdjson_result unescaped_key() noexcept; - simdjson_inline simdjson_result key() noexcept; - simdjson_inline simdjson_result value() noexcept; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/field.h */ -/* begin file include/simdjson/generic/ondemand/object.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -/** - * A forward-only JSON object field iterator. - */ -class object { -public: - /** - * Create a new invalid object. - * - * Exists so you can declare a variable and later assign to it before use. - */ - simdjson_inline object() noexcept = default; - - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - /** - * Look up a field by name on an object (order-sensitive). - * - * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the - * JSON `{ "x": 1, "y": 2, "z": 3 }`: - * - * ```c++ - * simdjson::ondemand::parser parser; - * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); - * double z = obj.find_field("z"); - * double y = obj.find_field("y"); - * double x = obj.find_field("x"); - * ``` - * If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful - * that only one field is returned. - * - * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. - * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. - * - * You must consume the fields on an object one at a time. A request for a new key - * invalidates previous field values: it makes them unsafe. The value instance you get - * from `content["bids"]` becomes invalid when you call `content["asks"]`. The array - * given by content["bids"].get_array() should not be accessed after you have called - * content["asks"].get_array(). You can detect such mistakes by first compiling and running - * the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an - * OUT_OF_ORDER_ITERATION error is generated. - * - * You are expected to access keys only once. You should access the value corresponding to a - * key a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() - * is an error. - * - * @param key The key to look up. - * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. - */ - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - - /** - * Look up a field by name on an object, without regard to key order. - * - * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies - * and often appears negligible. It starts out normally, starting out at the last field; but if - * the field is not found, it scans from the beginning of the object to see if it missed it. That - * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object - * in question is large. The fact that the extra code is there also bumps the executable size. - * - * It is the default, however, because it would be highly surprising (and hard to debug) if the - * default behavior failed to look up a field just because it was in the wrong order--and many - * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order. - * - * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the - * field wasn't there when they aren't). - * - * If you have multiple fields with a matching key ({"x": 1, "x": 1}) be mindful - * that only one field is returned. - * - * You must consume the fields on an object one at a time. A request for a new key - * invalidates previous field values: it makes them unsafe. The value instance you get - * from `content["bids"]` becomes invalid when you call `content["asks"]`. The array - * given by content["bids"].get_array() should not be accessed after you have called - * content["asks"].get_array(). You can detect such mistakes by first compiling and running - * the code in Debug mode (or with the macro `SIMDJSON_DEVELOPMENT_CHECKS` set to 1): an - * OUT_OF_ORDER_ITERATION error is generated. - * - * You are expected to access keys only once. You should access the value corresponding to a key - * a single time. Doing object["mykey"].to_string() and then again object["mykey"].to_string() is an error. - * - * @param key The key to look up. - * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. - */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - - /** - * Get the value associated with the given JSON pointer. We use the RFC 6901 - * https://tools.ietf.org/html/rfc6901 standard, interpreting the current node - * as the root of its own JSON document. - * - * ondemand::parser parser; - * auto json = R"({ "foo": { "a": [ 10, 20, 30 ] }})"_padded; - * auto doc = parser.iterate(json); - * doc.at_pointer("/foo/a/1") == 20 - * - * It is allowed for a key to be the empty string: - * - * ondemand::parser parser; - * auto json = R"({ "": { "a": [ 10, 20, 30 ] }})"_padded; - * auto doc = parser.iterate(json); - * doc.at_pointer("//a/1") == 20 - * - * Note that at_pointer() called on the document automatically calls the document's rewind - * method between each call. It invalidates all previously accessed arrays, objects and values - * that have not been consumed. Yet it is not the case when calling at_pointer on an object - * instance: there is no rewind and no invalidation. - * - * You may call at_pointer more than once on an object, but each time the pointer is advanced - * to be within the value matched by the key indicated by the JSON pointer query. Thus any preceding - * key (as well as the current key) can no longer be used with following JSON pointer calls. - * - * Also note that at_pointer() relies on find_field() which implies that we do not unescape keys when matching. - * - * @return The value associated with the given JSON pointer, or: - * - NO_SUCH_FIELD if a field does not exist in an object - * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length - * - INCORRECT_TYPE if a non-integer is used to access an array - * - INVALID_JSON_POINTER if the JSON pointer is invalid and cannot be parsed - */ - inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - - /** - * Reset the iterator so that we are pointing back at the - * beginning of the object. You should still consume values only once even if you - * can iterate through the object more than once. If you unescape a string within - * the object more than once, you have unsafe code. Note that rewinding an object - * means that you may need to reparse it anew: it is not a free operation. - * - * @returns true if the object contains some elements (not empty) - */ - inline simdjson_result reset() & noexcept; - /** - * This method scans the beginning of the object and checks whether the - * object is empty. - * The runtime complexity is constant time. After - * calling this function, if successful, the object is 'rewinded' at its - * beginning as if it had never been accessed. If the JSON is malformed (e.g., - * there is a missing comma), then an error is returned and it is no longer - * safe to continue. - */ - inline simdjson_result is_empty() & noexcept; - /** - * This method scans the object and counts the number of key-value pairs. - * The count_fields method should always be called before you have begun - * iterating through the object: it is expected that you are pointing at - * the beginning of the object. - * The runtime complexity is linear in the size of the object. After - * calling this function, if successful, the object is 'rewinded' at its - * beginning as if it had never been accessed. If the JSON is malformed (e.g., - * there is a missing comma), then an error is returned and it is no longer - * safe to continue. - * - * To check that an object is empty, it is more performant to use - * the is_empty() method. - * - * Performance hint: You should only call count_fields() as a last - * resort as it may require scanning the document twice or more. - */ - simdjson_inline simdjson_result count_fields() & noexcept; - /** - * Consumes the object and returns a string_view instance corresponding to the - * object as represented in JSON. It points inside the original byte array containing - * the JSON document. - */ - simdjson_inline simdjson_result raw_json() noexcept; - -protected: - /** - * Go to the end of the object, no matter where you are right now. - */ - simdjson_inline error_code consume() noexcept; - static simdjson_inline simdjson_result start(value_iterator &iter) noexcept; - static simdjson_inline simdjson_result start_root(value_iterator &iter) noexcept; - static simdjson_inline simdjson_result started(value_iterator &iter) noexcept; - static simdjson_inline object resume(const value_iterator &iter) noexcept; - simdjson_inline object(const value_iterator &iter) noexcept; - - simdjson_warn_unused simdjson_inline error_code find_field_raw(const std::string_view key) noexcept; - - value_iterator iter{}; - - friend class value; - friend class document; - friend struct simdjson_result; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; - - simdjson_inline simdjson_result begin() noexcept; - simdjson_inline simdjson_result end() noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field(std::string_view key) && noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; - simdjson_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; - simdjson_inline simdjson_result operator[](std::string_view key) && noexcept; - simdjson_inline simdjson_result at_pointer(std::string_view json_pointer) noexcept; - inline simdjson_result reset() noexcept; - inline simdjson_result is_empty() noexcept; - inline simdjson_result count_fields() & noexcept; - -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/object.h */ -/* begin file include/simdjson/generic/ondemand/parser.h */ - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class array; -class object; -class value; -class raw_json_string; -class document_stream; - -/** - * The default batch size for document_stream instances for this On Demand kernel. - * Note that different On Demand kernel may use a different DEFAULT_BATCH_SIZE value - * in the future. - */ -static constexpr size_t DEFAULT_BATCH_SIZE = 1000000; -/** - * Some adversary might try to set the batch size to 0 or 1, which might cause problems. - * We set a minimum of 32B since anything else is highly likely to be an error. In practice, - * most users will want a much larger batch size. - * - * All non-negative MINIMAL_BATCH_SIZE values should be 'safe' except that, obviously, no JSON - * document can ever span 0 or 1 byte and that very large values would create memory allocation issues. - */ -static constexpr size_t MINIMAL_BATCH_SIZE = 32; - -/** - * A JSON fragment iterator. - * - * This holds the actual iterator as well as the buffer for writing strings. - */ -class parser { -public: - /** - * Create a JSON parser. - * - * The new parser will have zero capacity. - */ - inline explicit parser(size_t max_capacity = SIMDJSON_MAXSIZE_BYTES) noexcept; - - inline parser(parser &&other) noexcept = default; - simdjson_inline parser(const parser &other) = delete; - simdjson_inline parser &operator=(const parser &other) = delete; - simdjson_inline parser &operator=(parser &&other) noexcept = default; - - /** Deallocate the JSON parser. */ - inline ~parser() noexcept = default; - - /** - * Start iterating an on-demand JSON document. - * - * ondemand::parser parser; - * document doc = parser.iterate(json); - * - * It is expected that the content is a valid UTF-8 file, containing a valid JSON document. - * Otherwise the iterate method may return an error. In particular, the whole input should be - * valid: we do not attempt to tolerate incorrect content either before or after a JSON - * document. - * - * ### IMPORTANT: Validate what you use - * - * Calling iterate on an invalid JSON document may not immediately trigger an error. The call to - * iterate does not parse and validate the whole document. - * - * ### IMPORTANT: Buffer Lifetime - * - * Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as - * long as the document iteration. - * - * ### IMPORTANT: Document Lifetime - * - * Only one iteration at a time can happen per parser, and the parser *must* be kept alive during - * iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before - * you call parse() again or destroy the parser. - * - * ### REQUIRED: Buffer Padding - * - * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what - * those bytes are initialized to, as long as they are allocated. - * - * @param json The JSON to parse. - * @param len The length of the JSON. - * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). - * - * @return The document, or an error: - * - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes. - * - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory - * allocation fails. - * - EMPTY if the document is all whitespace. - * - UTF8_ERROR if the document is not valid UTF-8. - * - UNESCAPED_CHARS if a string contains control characters that must be escaped - * - UNCLOSED_STRING if there is an unclosed string in the document. - */ - simdjson_warn_unused simdjson_result iterate(padded_string_view json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const char *json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const uint8_t *json, size_t len, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(std::string_view json, size_t capacity) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const std::string &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(const simdjson_result &json) & noexcept; - /** @overload simdjson_result iterate(padded_string_view json) & noexcept */ - simdjson_warn_unused simdjson_result iterate(padded_string &&json) & noexcept = delete; - - /** - * @private - * - * Start iterating an on-demand JSON document. - * - * ondemand::parser parser; - * json_iterator doc = parser.iterate(json); - * - * ### IMPORTANT: Buffer Lifetime - * - * Because parsing is done while you iterate, you *must* keep the JSON buffer around at least as - * long as the document iteration. - * - * ### IMPORTANT: Document Lifetime - * - * Only one iteration at a time can happen per parser, and the parser *must* be kept alive during - * iteration to ensure intermediate buffers can be accessed. Any document must be destroyed before - * you call parse() again or destroy the parser. - * - * The ondemand::document instance holds the iterator. The document must remain in scope - * while you are accessing instances of ondemand::value, ondemand::object, ondemand::array. - * - * ### REQUIRED: Buffer Padding - * - * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what - * those bytes are initialized to, as long as they are allocated. - * - * @param json The JSON to parse. - * - * @return The iterator, or an error: - * - INSUFFICIENT_PADDING if the input has less than SIMDJSON_PADDING extra bytes. - * - MEMALLOC if realloc_if_needed the parser does not have enough capacity, and memory - * allocation fails. - * - EMPTY if the document is all whitespace. - * - UTF8_ERROR if the document is not valid UTF-8. - * - UNESCAPED_CHARS if a string contains control characters that must be escaped - * - UNCLOSED_STRING if there is an unclosed string in the document. - */ - simdjson_warn_unused simdjson_result iterate_raw(padded_string_view json) & noexcept; - - - /** - * Parse a buffer containing many JSON documents. - * - * auto json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"_padded; - * ondemand::parser parser; - * ondemand::document_stream docs = parser.iterate_many(json); - * for (auto & doc : docs) { - * std::cout << doc["foo"] << std::endl; - * } - * // Prints 1 2 3 - * - * No copy of the input buffer is made. - * - * The function is lazy: it may be that no more than one JSON document at a time is parsed. - * - * The caller is responsabile to ensure that the input string data remains unchanged and is - * not deleted during the loop. - * - * ### Format - * - * The buffer must contain a series of one or more JSON documents, concatenated into a single - * buffer, separated by ASCII whitespace. It effectively parses until it has a fully valid document, - * then starts parsing the next document at that point. (It does this with more parallelism and - * lookahead than you might think, though.) - * - * documents that consist of an object or array may omit the whitespace between them, concatenating - * with no separator. Documents that consist of a single primitive (i.e. documents that are not - * arrays or objects) MUST be separated with ASCII whitespace. - * - * The characters inside a JSON document, and between JSON documents, must be valid Unicode (UTF-8). - * - * The documents must not exceed batch_size bytes (by default 1MB) or they will fail to parse. - * Setting batch_size to excessively large or excessively small values may impact negatively the - * performance. - * - * ### REQUIRED: Buffer Padding - * - * The buffer must have at least SIMDJSON_PADDING extra allocated bytes. It does not matter what - * those bytes are initialized to, as long as they are allocated. - * - * ### Threads - * - * When compiled with SIMDJSON_THREADS_ENABLED, this method will use a single thread under the - * hood to do some lookahead. - * - * ### Parser Capacity - * - * If the parser's current capacity is less than batch_size, it will allocate enough capacity - * to handle it (up to max_capacity). - * - * @param buf The concatenated JSON to parse. - * @param len The length of the concatenated JSON. - * @param batch_size The batch size to use. MUST be larger than the largest document. The sweet - * spot is cache-related: small enough to fit in cache, yet big enough to - * parse as many documents as possible in one tight loop. - * Defaults to 10MB, which has been a reasonable sweet spot in our tests. - * @return The stream, or an error. An empty input will yield 0 documents rather than an EMPTY error. Errors: - * - MEMALLOC if the parser does not have enough capacity and memory allocation fails - * - CAPACITY if the parser does not have enough capacity and batch_size > max_capacity. - * - other json errors if parsing fails. You should not rely on these errors to always the same for the - * same document: they may vary under runtime dispatch (so they may vary depending on your system and hardware). - */ - inline simdjson_result iterate_many(const uint8_t *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const char *buf, size_t len, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept; - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const std::string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept; - inline simdjson_result iterate_many(const std::string &&s, size_t batch_size) = delete;// unsafe - /** @overload parse_many(const uint8_t *buf, size_t len, size_t batch_size) */ - inline simdjson_result iterate_many(const padded_string &s, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept; - inline simdjson_result iterate_many(const padded_string &&s, size_t batch_size) = delete;// unsafe - - /** @private We do not want to allow implicit conversion from C string to std::string. */ - simdjson_result iterate_many(const char *buf, size_t batch_size = DEFAULT_BATCH_SIZE) noexcept = delete; - - /** The capacity of this parser (the largest document it can process). */ - simdjson_inline size_t capacity() const noexcept; - /** The maximum capacity of this parser (the largest document it is allowed to process). */ - simdjson_inline size_t max_capacity() const noexcept; - simdjson_inline void set_max_capacity(size_t max_capacity) noexcept; - /** The maximum depth of this parser (the most deeply nested objects and arrays it can process). */ - simdjson_inline size_t max_depth() const noexcept; - - /** - * Ensure this parser has enough memory to process JSON documents up to `capacity` bytes in length - * and `max_depth` depth. - * - * @param capacity The new capacity. - * @param max_depth The new max_depth. Defaults to DEFAULT_MAX_DEPTH. - * @return The error, if there is one. - */ - simdjson_warn_unused error_code allocate(size_t capacity, size_t max_depth=DEFAULT_MAX_DEPTH) noexcept; - - #ifdef SIMDJSON_THREADS_ENABLED - /** - * The parser instance can use threads when they are available to speed up some - * operations. It is enabled by default. Changing this attribute will change the - * behavior of the parser for future operations. - */ - bool threaded{true}; - #endif - - /** - * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. - * The provided pointer is advanced to the end of the string by reference, and a string_view instance - * is returned. You can ensure that your buffer is large enough by allocating a block of memory at least - * as large as the input JSON plus SIMDJSON_PADDING and then unescape all strings to this one buffer. - * - * This unescape function is a low-level function. If you want a more user-friendly approach, you should - * avoid raw_json_string instances (e.g., by calling unescaped_key() instead of key() or get_string() - * instead of get_raw_json_string()). - * - * ## IMPORTANT: string_view lifetime - * - * The string_view is only valid as long as the bytes in dst. - * - * @param raw_json_string input - * @param dst A pointer to a buffer at least large enough to write this string as well as - * an additional SIMDJSON_PADDING bytes. - * @return A string_view pointing at the unescaped string in dst - * @error STRING_ERROR if escapes are incorrect. - */ - simdjson_inline simdjson_result unescape(raw_json_string in, uint8_t *&dst) const noexcept; -private: - /** @private [for benchmarking access] The implementation to use */ - std::unique_ptr implementation{}; - size_t _capacity{0}; - size_t _max_capacity; - size_t _max_depth{DEFAULT_MAX_DEPTH}; - std::unique_ptr string_buf{}; -#if SIMDJSON_DEVELOPMENT_CHECKS - std::unique_ptr start_positions{}; -#endif - - friend class json_iterator; - friend class document_stream; -}; - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::parser &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/parser.h */ -/* begin file include/simdjson/generic/ondemand/document_stream.h */ -#ifdef SIMDJSON_THREADS_ENABLED -#include -#include -#include -#endif - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -class parser; -class json_iterator; -class document; - -#ifdef SIMDJSON_THREADS_ENABLED -/** @private Custom worker class **/ -struct stage1_worker { - stage1_worker() noexcept = default; - stage1_worker(const stage1_worker&) = delete; - stage1_worker(stage1_worker&&) = delete; - stage1_worker operator=(const stage1_worker&) = delete; - ~stage1_worker(); - /** - * We only start the thread when it is needed, not at object construction, this may throw. - * You should only call this once. - **/ - void start_thread(); - /** - * Start a stage 1 job. You should first call 'run', then 'finish'. - * You must call start_thread once before. - */ - void run(document_stream * ds, parser * stage1, size_t next_batch_start); - /** Wait for the run to finish (blocking). You should first call 'run', then 'finish'. **/ - void finish(); - -private: - - /** - * Normally, we would never stop the thread. But we do in the destructor. - * This function is only safe assuming that you are not waiting for results. You - * should have called run, then finish, and be done. - **/ - void stop_thread(); - - std::thread thread{}; - /** These three variables define the work done by the thread. **/ - ondemand::parser * stage1_thread_parser{}; - size_t _next_batch_start{}; - document_stream * owner{}; - /** - * We have two state variables. This could be streamlined to one variable in the future but - * we use two for clarity. - */ - bool has_work{false}; - bool can_work{true}; - - /** - * We lock using a mutex. - */ - std::mutex locking_mutex{}; - std::condition_variable cond_var{}; - - friend class document_stream; -}; -#endif // SIMDJSON_THREADS_ENABLED - -/** - * A forward-only stream of documents. - * - * Produced by parser::iterate_many. - * - */ -class document_stream { -public: - /** - * Construct an uninitialized document_stream. - * - * ```c++ - * document_stream docs; - * auto error = parser.iterate_many(json).get(docs); - * ``` - */ - simdjson_inline document_stream() noexcept; - /** Move one document_stream to another. */ - simdjson_inline document_stream(document_stream &&other) noexcept = default; - /** Move one document_stream to another. */ - simdjson_inline document_stream &operator=(document_stream &&other) noexcept = default; - - simdjson_inline ~document_stream() noexcept; - - /** - * Returns the input size in bytes. - */ - inline size_t size_in_bytes() const noexcept; - - /** - * After iterating through the stream, this method - * returns the number of bytes that were not parsed at the end - * of the stream. If truncated_bytes() differs from zero, - * then the input was truncated maybe because incomplete JSON - * documents were found at the end of the stream. You - * may need to process the bytes in the interval [size_in_bytes()-truncated_bytes(), size_in_bytes()). - * - * You should only call truncated_bytes() after streaming through all - * documents, like so: - * - * document_stream stream = parser.iterate_many(json,window); - * for(auto & doc : stream) { - * // do something with doc - * } - * size_t truncated = stream.truncated_bytes(); - * - */ - inline size_t truncated_bytes() const noexcept; - - class iterator { - public: - using value_type = simdjson_result; - using reference = value_type; - - using difference_type = std::ptrdiff_t; - - using iterator_category = std::input_iterator_tag; - - /** - * Default constructor. - */ - simdjson_inline iterator() noexcept; - /** - * Get the current document (or error). - */ - simdjson_inline simdjson_result operator*() noexcept; - /** - * Advance to the next document (prefix). - */ - inline iterator& operator++() noexcept; - /** - * Check if we're at the end yet. - * @param other the end iterator to compare to. - */ - simdjson_inline bool operator!=(const iterator &other) const noexcept; - /** - * @private - * - * Gives the current index in the input document in bytes. - * - * document_stream stream = parser.parse_many(json,window); - * for(auto i = stream.begin(); i != stream.end(); ++i) { - * auto doc = *i; - * size_t index = i.current_index(); - * } - * - * This function (current_index()) is experimental and the usage - * may change in future versions of simdjson: we find the API somewhat - * awkward and we would like to offer something friendlier. - */ - simdjson_inline size_t current_index() const noexcept; - - /** - * @private - * - * Gives a view of the current document at the current position. - * - * document_stream stream = parser.iterate_many(json,window); - * for(auto i = stream.begin(); i != stream.end(); ++i) { - * std::string_view v = i.source(); - * } - * - * The returned string_view instance is simply a map to the (unparsed) - * source string: it may thus include white-space characters and all manner - * of padding. - * - * This function (source()) is experimental and the usage - * may change in future versions of simdjson: we find the API somewhat - * awkward and we would like to offer something friendlier. - * - */ - simdjson_inline std::string_view source() const noexcept; - - /** - * Returns error of the stream (if any). - */ - inline error_code error() const noexcept; - - private: - simdjson_inline iterator(document_stream *s, bool finished) noexcept; - /** The document_stream we're iterating through. */ - document_stream* stream; - /** Whether we're finished or not. */ - bool finished; - - friend class document; - friend class document_stream; - friend class json_iterator; - }; - - /** - * Start iterating the documents in the stream. - */ - simdjson_inline iterator begin() noexcept; - /** - * The end of the stream, for iterator comparison purposes. - */ - simdjson_inline iterator end() noexcept; - -private: - - document_stream &operator=(const document_stream &) = delete; // Disallow copying - document_stream(const document_stream &other) = delete; // Disallow copying - - /** - * Construct a document_stream. Does not allocate or parse anything until the iterator is - * used. - * - * @param parser is a reference to the parser instance used to generate this document_stream - * @param buf is the raw byte buffer we need to process - * @param len is the length of the raw byte buffer in bytes - * @param batch_size is the size of the windows (must be strictly greater or equal to the largest JSON document) - */ - simdjson_inline document_stream( - ondemand::parser &parser, - const uint8_t *buf, - size_t len, - size_t batch_size - ) noexcept; - - /** - * Parse the first document in the buffer. Used by begin(), to handle allocation and - * initialization. - */ - inline void start() noexcept; - - /** - * Parse the next document found in the buffer previously given to document_stream. - * - * The content should be a valid JSON document encoded as UTF-8. If there is a - * UTF-8 BOM, the caller is responsible for omitting it, UTF-8 BOM are - * discouraged. - * - * You do NOT need to pre-allocate a parser. This function takes care of - * pre-allocating a capacity defined by the batch_size defined when creating the - * document_stream object. - * - * The function returns simdjson::EMPTY if there is no more data to be parsed. - * - * The function returns simdjson::SUCCESS (as integer = 0) in case of success - * and indicates that the buffer has successfully been parsed to the end. - * Every document it contained has been parsed without error. - * - * The function returns an error code from simdjson/simdjson.h in case of failure - * such as simdjson::CAPACITY, simdjson::MEMALLOC, simdjson::DEPTH_ERROR and so forth; - * the simdjson::error_message function converts these error codes into a string). - * - * You can also check validity by calling parser.is_valid(). The same parser can - * and should be reused for the other documents in the buffer. - */ - inline void next() noexcept; - - /** Move the json_iterator of the document to the location of the next document in the stream. */ - inline void next_document() noexcept; - - /** Get the next document index. */ - inline size_t next_batch_start() const noexcept; - - /** Pass the next batch through stage 1 with the given parser. */ - inline error_code run_stage1(ondemand::parser &p, size_t batch_start) noexcept; - - // Fields - ondemand::parser *parser; - const uint8_t *buf; - size_t len; - size_t batch_size; - /** - * We are going to use just one document instance. The document owns - * the json_iterator. It implies that we only ever pass a reference - * to the document to the users. - */ - document doc{}; - /** The error (or lack thereof) from the current document. */ - error_code error; - size_t batch_start{0}; - size_t doc_index{}; - - #ifdef SIMDJSON_THREADS_ENABLED - /** Indicates whether we use threads. Note that this needs to be a constant during the execution of the parsing. */ - bool use_thread; - - inline void load_from_stage1_thread() noexcept; - - /** Start a thread to run stage 1 on the next batch. */ - inline void start_stage1_thread() noexcept; - - /** Wait for the stage 1 thread to finish and capture the results. */ - inline void finish_stage1_thread() noexcept; - - /** The error returned from the stage 1 thread. */ - error_code stage1_thread_error{UNINITIALIZED}; - /** The thread used to run stage 1 against the next batch in the background. */ - std::unique_ptr worker{new(std::nothrow) stage1_worker()}; - /** - * The parser used to run stage 1 in the background. Will be swapped - * with the regular parser when finished. - */ - ondemand::parser stage1_thread_parser{}; - - friend struct stage1_worker; - #endif // SIMDJSON_THREADS_ENABLED - - friend class parser; - friend class document; - friend class json_iterator; - friend struct simdjson_result; - friend struct internal::simdjson_result_base; -}; // document_stream - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { -template<> -struct simdjson_result : public SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base { -public: - simdjson_inline simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_stream &&value) noexcept; ///< @private - simdjson_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_inline simdjson_result() noexcept = default; -}; - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/document_stream.h */ -/* begin file include/simdjson/generic/ondemand/serialization.h */ - -namespace simdjson { -/** - * Create a string-view instance out of a document instance. The string-view instance - * contains JSON text that is suitable to be parsed as JSON again. - */ -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document& x) noexcept; -/** - * Create a string-view instance out of a value instance. The string-view instance - * contains JSON text that is suitable to be parsed as JSON again. The value must - * not have been accessed previously. - */ -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value& x) noexcept; -/** - * Create a string-view instance out of an object instance. The string-view instance - * contains JSON text that is suitable to be parsed as JSON again. - */ -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object& x) noexcept; -/** - * Create a string-view instance out of an array instance. The string-view instance - * contains JSON text that is suitable to be parsed as JSON again. - */ -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array& x) noexcept; -inline simdjson_result to_json_string(simdjson_result x); -inline simdjson_result to_json_string(simdjson_result x); -inline simdjson_result to_json_string(simdjson_result x); -inline simdjson_result to_json_string(simdjson_result x); -} // namespace simdjson - -/** - * We want to support argument-dependent lookup (ADL). - * Hence we should define operator<< in the namespace - * where the argument (here value, object, etc.) resides. - * Credit: @madhur4127 - * See https://github.com/simdjson/simdjson/issues/1768 - */ -namespace simdjson { namespace SIMDJSON_BUILTIN_IMPLEMENTATION { namespace ondemand { - -/** - * Print JSON to an output stream. - * - * @param out The output stream. - * @param value The element. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. - */ -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value x); -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); -#endif -/** - * Print JSON to an output stream. - * - * @param out The output stream. - * @param value The array. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. - */ -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array value); -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); -#endif -/** - * Print JSON to an output stream. - * - * @param out The output stream. - * @param value The array. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. - */ -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document& value); -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x); -#endif -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_reference& value); -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x); -#endif -/** - * Print JSON to an output stream. - * - * @param out The output stream. - * @param value The object. - * @throw if there is an error with the underlying output stream. simdjson itself will not throw. - */ -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object value); -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x); -#endif -}}} // namespace simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand -/* end file include/simdjson/generic/ondemand/serialization.h */ -/* end file include/simdjson/generic/ondemand.h */ - -// Inline definitions -/* begin file include/simdjson/generic/implementation_simdjson_result_base-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { - -// -// internal::implementation_simdjson_result_base inline implementation -// - -template -simdjson_inline void implementation_simdjson_result_base::tie(T &value, error_code &error) && noexcept { - error = this->second; - if (!error) { - value = std::forward>(*this).first; - } -} - -template -simdjson_warn_unused simdjson_inline error_code implementation_simdjson_result_base::get(T &value) && noexcept { - error_code error; - std::forward>(*this).tie(value, error); - return error; -} - -template -simdjson_inline error_code implementation_simdjson_result_base::error() const noexcept { - return this->second; -} - -#if SIMDJSON_EXCEPTIONS - -template -simdjson_inline T& implementation_simdjson_result_base::value() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return this->first; -} - -template -simdjson_inline T&& implementation_simdjson_result_base::value() && noexcept(false) { - return std::forward>(*this).take_value(); -} - -template -simdjson_inline T&& implementation_simdjson_result_base::take_value() && noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return std::forward(this->first); -} - -template -simdjson_inline implementation_simdjson_result_base::operator T&&() && noexcept(false) { - return std::forward>(*this).take_value(); -} - -#endif // SIMDJSON_EXCEPTIONS - -template -simdjson_inline const T& implementation_simdjson_result_base::value_unsafe() const& noexcept { - return this->first; -} - -template -simdjson_inline T& implementation_simdjson_result_base::value_unsafe() & noexcept { - return this->first; -} - -template -simdjson_inline T&& implementation_simdjson_result_base::value_unsafe() && noexcept { - return std::forward(this->first); -} - -template -simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value, error_code error) noexcept - : first{std::forward(value)}, second{error} {} -template -simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(error_code error) noexcept - : implementation_simdjson_result_base(T{}, error) {} -template -simdjson_inline implementation_simdjson_result_base::implementation_simdjson_result_base(T &&value) noexcept - : implementation_simdjson_result_base(std::forward(value), SUCCESS) {} - -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson -/* end file include/simdjson/generic/implementation_simdjson_result_base-inl.h */ -/* begin file include/simdjson/generic/ondemand-inl.h */ -/* begin file include/simdjson/generic/ondemand/json_type-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -inline std::ostream& operator<<(std::ostream& out, json_type type) noexcept { - switch (type) { - case json_type::array: out << "array"; break; - case json_type::object: out << "object"; break; - case json_type::number: out << "number"; break; - case json_type::string: out << "string"; break; - case json_type::boolean: out << "boolean"; break; - case json_type::null: out << "null"; break; - default: SIMDJSON_UNREACHABLE(); - } - return out; -} - -inline std::ostream& operator<<(std::ostream& out, number_type type) noexcept { - switch (type) { - case number_type::signed_integer: out << "integer in [-9223372036854775808,9223372036854775808)"; break; - case number_type::unsigned_integer: out << "unsigned integer in [9223372036854775808,18446744073709551616)"; break; - case number_type::floating_point_number: out << "floating-point number (binary64)"; break; - default: SIMDJSON_UNREACHABLE(); - } - return out; -} -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson_result &type) noexcept(false) { - return out << type.value(); -} -#endif - - - -simdjson_inline number_type number::get_number_type() const noexcept { - return type; -} - -simdjson_inline bool number::is_uint64() const noexcept { - return get_number_type() == number_type::unsigned_integer; -} - -simdjson_inline uint64_t number::get_uint64() const noexcept { - return payload.unsigned_integer; -} - -simdjson_inline number::operator uint64_t() const noexcept { - return get_uint64(); -} - - -simdjson_inline bool number::is_int64() const noexcept { - return get_number_type() == number_type::signed_integer; -} - -simdjson_inline int64_t number::get_int64() const noexcept { - return payload.signed_integer; -} - -simdjson_inline number::operator int64_t() const noexcept { - return get_int64(); -} - -simdjson_inline bool number::is_double() const noexcept { - return get_number_type() == number_type::floating_point_number; -} - -simdjson_inline double number::get_double() const noexcept { - return payload.floating_point_number; -} - -simdjson_inline number::operator double() const noexcept { - return get_double(); -} - -simdjson_inline double number::as_double() const noexcept { - if(is_double()) { - return payload.floating_point_number; - } - if(is_int64()) { - return double(payload.signed_integer); - } - return double(payload.unsigned_integer); -} - -simdjson_inline void number::append_s64(int64_t value) noexcept { - payload.signed_integer = value; - type = number_type::signed_integer; -} - -simdjson_inline void number::append_u64(uint64_t value) noexcept { - payload.unsigned_integer = value; - type = number_type::unsigned_integer; -} - -simdjson_inline void number::append_double(double value) noexcept { - payload.floating_point_number = value; - type = number_type::floating_point_number; -} - -simdjson_inline void number::skip_double() noexcept { - type = number_type::floating_point_number; -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_type &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/json_type-inl.h */ -/* begin file include/simdjson/generic/ondemand/logger-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { -namespace logger { - -static constexpr const char * DASHES = "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"; -static constexpr const int LOG_EVENT_LEN = 20; -static constexpr const int LOG_BUFFER_LEN = 30; -static constexpr const int LOG_SMALL_BUFFER_LEN = 10; -static int log_depth = 0; // Not threadsafe. Log only. - -// Helper to turn unprintable or newline characters into spaces -static inline char printable_char(char c) { - if (c >= 0x20) { - return c; - } else { - return ' '; - } -} - -inline void log_event(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept { - log_line(iter, "", type, detail, delta, depth_delta); -} - -inline void log_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept { - log_line(iter, index, depth, "", type, detail); -} -inline void log_value(const json_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept { - log_line(iter, "", type, detail, delta, depth_delta); -} - -inline void log_start_value(const json_iterator &iter, token_position index, depth_t depth, const char *type, std::string_view detail) noexcept { - log_line(iter, index, depth, "+", type, detail); - if (LOG_ENABLED) { log_depth++; } -} -inline void log_start_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept { - log_line(iter, "+", type, "", delta, depth_delta); - if (LOG_ENABLED) { log_depth++; } -} - -inline void log_end_value(const json_iterator &iter, const char *type, int delta, int depth_delta) noexcept { - if (LOG_ENABLED) { log_depth--; } - log_line(iter, "-", type, "", delta, depth_delta); -} - -inline void log_error(const json_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept { - log_line(iter, "ERROR: ", error, detail, delta, depth_delta); -} -inline void log_error(const json_iterator &iter, token_position index, depth_t depth, const char *error, const char *detail) noexcept { - log_line(iter, index, depth, "ERROR: ", error, detail); -} - -inline void log_event(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept { - log_event(iter.json_iter(), type, detail, delta, depth_delta); -} - -inline void log_value(const value_iterator &iter, const char *type, std::string_view detail, int delta, int depth_delta) noexcept { - log_value(iter.json_iter(), type, detail, delta, depth_delta); -} - -inline void log_start_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept { - log_start_value(iter.json_iter(), type, delta, depth_delta); -} - -inline void log_end_value(const value_iterator &iter, const char *type, int delta, int depth_delta) noexcept { - log_end_value(iter.json_iter(), type, delta, depth_delta); -} - -inline void log_error(const value_iterator &iter, const char *error, const char *detail, int delta, int depth_delta) noexcept { - log_error(iter.json_iter(), error, detail, delta, depth_delta); -} - -inline void log_headers() noexcept { - if (LOG_ENABLED) { - // Technically a static variable is not thread-safe, but if you are using threads - // and logging... well... - static bool displayed_hint{false}; - log_depth = 0; - printf("\n"); - if(!displayed_hint) { - // We only print this helpful header once. - printf("# Logging provides the depth and position of the iterator user-visible steps:\n"); - printf("# +array says 'this is where we were when we discovered the start array'\n"); - printf("# -array says 'this is where we were when we ended the array'\n"); - printf("# skip says 'this is a structural or value I am skipping'\n"); - printf("# +/-skip says 'this is a start/end array or object I am skipping'\n"); - printf("#\n"); - printf("# The indentation of the terms (array, string,...) indicates the depth,\n"); - printf("# in addition to the depth being displayed.\n"); - printf("#\n"); - printf("# Every token in the document has a single depth determined by the tokens before it,\n"); - printf("# and is not affected by what the token actually is.\n"); - printf("#\n"); - printf("# Not all structural elements are presented as tokens in the logs.\n"); - printf("#\n"); - printf("# We never give control to the user within an empty array or an empty object.\n"); - printf("#\n"); - printf("# Inside an array, having a depth greater than the array's depth means that\n"); - printf("# we are pointing inside a value.\n"); - printf("# Having a depth equal to the array means that we are pointing right before a value.\n"); - printf("# Having a depth smaller than the array means that we have moved beyond the array.\n"); - displayed_hint = true; - } - printf("\n"); - printf("| %-*s ", LOG_EVENT_LEN, "Event"); - printf("| %-*s ", LOG_BUFFER_LEN, "Buffer"); - printf("| %-*s ", LOG_SMALL_BUFFER_LEN, "Next"); - // printf("| %-*s ", 5, "Next#"); - printf("| %-*s ", 5, "Depth"); - printf("| Detail "); - printf("|\n"); - - printf("|%.*s", LOG_EVENT_LEN+2, DASHES); - printf("|%.*s", LOG_BUFFER_LEN+2, DASHES); - printf("|%.*s", LOG_SMALL_BUFFER_LEN+2, DASHES); - // printf("|%.*s", 5+2, DASHES); - printf("|%.*s", 5+2, DASHES); - printf("|--------"); - printf("|\n"); - fflush(stdout); - } -} - -inline void log_line(const json_iterator &iter, const char *title_prefix, const char *title, std::string_view detail, int delta, int depth_delta) noexcept { - log_line(iter, iter.position()+delta, depth_t(iter.depth()+depth_delta), title_prefix, title, detail); -} -inline void log_line(const json_iterator &iter, token_position index, depth_t depth, const char *title_prefix, const char *title, std::string_view detail) noexcept { - if (LOG_ENABLED) { - const int indent = depth*2; - const auto buf = iter.token.buf; - printf("| %*s%s%-*s ", - indent, "", - title_prefix, - LOG_EVENT_LEN - indent - int(strlen(title_prefix)), title - ); - { - // Print the current structural. - printf("| "); - auto current_structural = &buf[*index]; - for (int i=0;i(buf); } - - -simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { - size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; - } - } - return true; -} - -simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { - size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; - } - } - return true; -} - - -simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string_view target) const noexcept { - // If we are going to call memcmp, then we must know something about the length of the raw_json_string. - return (length >= target.size()) && (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); -} - -simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and - // the raw content is quote terminated within a valid JSON string. - if(target.size() <= SIMDJSON_PADDING) { - return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); - } - const char * r{raw()}; - size_t pos{0}; - for(;pos < target.size();pos++) { - if(r[pos] != target[pos]) { return false; } - } - if(r[pos] != '"') { return false; } - return true; -} - -simdjson_inline bool raw_json_string::is_equal(std::string_view target) const noexcept { - const char * r{raw()}; - size_t pos{0}; - bool escaping{false}; - for(;pos < target.size();pos++) { - if(r[pos] != target[pos]) { return false; } - // if target is a compile-time constant and it is free from - // quotes, then the next part could get optimized away through - // inlining. - if((target[pos] == '"') && !escaping) { - // We have reached the end of the raw_json_string but - // the target is not done. - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; - } - } - if(r[pos] != '"') { return false; } - return true; -} - - -simdjson_inline bool raw_json_string::unsafe_is_equal(const char * target) const noexcept { - // Assumptions: 'target' does not contain unescaped quote characters, is null terminated and - // the raw content is quote terminated within a valid JSON string. - const char * r{raw()}; - size_t pos{0}; - for(;target[pos];pos++) { - if(r[pos] != target[pos]) { return false; } - } - if(r[pos] != '"') { return false; } - return true; -} - -simdjson_inline bool raw_json_string::is_equal(const char* target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and - // the raw content is quote terminated within a valid JSON string. - const char * r{raw()}; - size_t pos{0}; - bool escaping{false}; - for(;target[pos];pos++) { - if(r[pos] != target[pos]) { return false; } - // if target is a compile-time constant and it is free from - // quotes, then the next part could get optimized away through - // inlining. - if((target[pos] == '"') && !escaping) { - // We have reached the end of the raw_json_string but - // the target is not done. - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; - } - } - if(r[pos] != '"') { return false; } - return true; -} - -simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept { - return a.unsafe_is_equal(c); -} - -simdjson_unused simdjson_inline bool operator==(std::string_view c, const raw_json_string &a) noexcept { - return a == c; -} - -simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::string_view c) noexcept { - return !(a == c); -} - -simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept { - return !(a == c); -} - - -simdjson_inline simdjson_warn_unused simdjson_result raw_json_string::unescape(json_iterator &iter) const noexcept { - return iter.unescape(*this); -} - - -simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept { - bool in_escape = false; - const char *s = str.raw(); - while (true) { - switch (*s) { - case '\\': in_escape = !in_escape; break; - case '"': if (in_escape) { in_escape = false; } else { return out; } break; - default: if (in_escape) { in_escape = false; } - } - out << *s; - s++; - } -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); -} -simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &iter) const noexcept { - if (error()) { return error(); } - return first.unescape(iter); -} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/raw_json_string-inl.h */ -/* begin file include/simdjson/generic/ondemand/token_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline token_iterator::token_iterator( - const uint8_t *_buf, - token_position position -) noexcept : buf{_buf}, _position{position} -{ -} - -simdjson_inline uint32_t token_iterator::current_offset() const noexcept { - return *(_position); -} - - -simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept { - return &buf[*(_position++)]; -} - -simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept { - return &buf[*position]; -} -simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept { - return *position; -} -simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept { - return *(position+1) - *position; -} - -simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept { - return &buf[*(_position+delta)]; -} -simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept { - return *(_position+delta); -} -simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept { - return *(_position+delta+1) - *(_position+delta); -} - -simdjson_inline token_position token_iterator::position() const noexcept { - return _position; -} -simdjson_inline void token_iterator::set_position(token_position target_position) noexcept { - _position = target_position; -} - -simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept { - return _position == other._position; -} -simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept { - return _position != other._position; -} -simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept { - return _position > other._position; -} -simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept { - return _position >= other._position; -} -simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept { - return _position < other._position; -} -simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept { - return _position <= other._position; -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::token_iterator &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/token_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/json_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept - : token(std::forward(other.token)), - parser{other.parser}, - _string_buf_loc{other._string_buf_loc}, - error{other.error}, - _depth{other._depth}, - _root{other._root}, - _streaming{other._streaming} -{ - other.parser = nullptr; -} -simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept { - token = other.token; - parser = other.parser; - _string_buf_loc = other._string_buf_loc; - error = other.error; - _depth = other._depth; - _root = other._root; - _streaming = other._streaming; - other.parser = nullptr; - return *this; -} - -simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept - : token(buf, &_parser->implementation->structural_indexes[0]), - parser{_parser}, - _string_buf_loc{parser->string_buf.get()}, - _depth{1}, - _root{parser->implementation->structural_indexes.get()}, - _streaming{false} - -{ - logger::log_headers(); -#if SIMDJSON_CHECK_EOF - assert_more_tokens(); -#endif -} - -inline void json_iterator::rewind() noexcept { - token.set_position( root_position() ); - logger::log_headers(); // We start again - _string_buf_loc = parser->string_buf.get(); - _depth = 1; -} - -inline bool json_iterator::balanced() const noexcept { - token_iterator ti(token); - int32_t count{0}; - ti.set_position( root_position() ); - while(ti.peek() <= peek_last()) { - switch (*ti.return_current_and_advance()) - { - case '[': case '{': - count++; - break; - case ']': case '}': - count--; - break; - default: - break; - } - } - return count == 0; -} - - -// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller -// relating depth and parent_depth, which is a desired effect. The warning does not show up if the -// skip_child() function is not marked inline). -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept { - if (depth() <= parent_depth) { return SUCCESS; } - switch (*return_current_and_advance()) { - // TODO consider whether matching braces is a requirement: if non-matching braces indicates - // *missing* braces, then future lookups are not in the object/arrays they think they are, - // violating the rule "validate enough structure that the user can be confident they are - // looking at the right values." - // PERF TODO we can eliminate the switch here with a lookup of how much to add to depth - - // For the first open array/object in a value, we've already incremented depth, so keep it the same - // We never stop at colon, but if we did, it wouldn't affect depth - case '[': case '{': case ':': - logger::log_start_value(*this, "skip"); - break; - // If there is a comma, we have just finished a value in an array/object, and need to get back in - case ',': - logger::log_value(*this, "skip"); - break; - // ] or } means we just finished a value and need to jump out of the array/object - case ']': case '}': - logger::log_end_value(*this, "skip"); - _depth--; - if (depth() <= parent_depth) { return SUCCESS; } -#if SIMDJSON_CHECK_EOF - // If there are no more tokens, the parent is incomplete. - if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - break; - case '"': - if(*peek() == ':') { - // We are at a key!!! - // This might happen if you just started an object and you skip it immediately. - // Performance note: it would be nice to get rid of this check as it is somewhat - // expensive. - // https://github.com/simdjson/simdjson/issues/1742 - logger::log_value(*this, "key"); - return_current_and_advance(); // eat up the ':' - break; // important!!! - } - simdjson_fallthrough; - // Anything else must be a scalar value - default: - // For the first scalar, we will have incremented depth already, so we decrement it here. - logger::log_value(*this, "skip"); - _depth--; - if (depth() <= parent_depth) { return SUCCESS; } - break; - } - - // Now that we've considered the first value, we only increment/decrement for arrays/objects - while (position() < end_position()) { - switch (*return_current_and_advance()) { - case '[': case '{': - logger::log_start_value(*this, "skip"); - _depth++; - break; - // TODO consider whether matching braces is a requirement: if non-matching braces indicates - // *missing* braces, then future lookups are not in the object/arrays they think they are, - // violating the rule "validate enough structure that the user can be confident they are - // looking at the right values." - // PERF TODO we can eliminate the switch here with a lookup of how much to add to depth - case ']': case '}': - logger::log_end_value(*this, "skip"); - _depth--; - if (depth() <= parent_depth) { return SUCCESS; } - break; - default: - logger::log_value(*this, "skip", ""); - break; - } - } - - return report_error(TAPE_ERROR, "not enough close braces"); -} - -SIMDJSON_POP_DISABLE_WARNINGS - -simdjson_inline bool json_iterator::at_root() const noexcept { - return position() == root_position(); -} - -simdjson_inline bool json_iterator::streaming() const noexcept { - return _streaming; -} - -simdjson_inline token_position json_iterator::root_position() const noexcept { - return _root; -} - -simdjson_inline void json_iterator::assert_at_document_depth() const noexcept { - SIMDJSON_ASSUME( _depth == 1 ); -} - -simdjson_inline void json_iterator::assert_at_root() const noexcept { - SIMDJSON_ASSUME( _depth == 1 ); -#ifndef SIMDJSON_CLANG_VISUAL_STUDIO - // Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument - // has side effects that will be discarded. - SIMDJSON_ASSUME( token.position() == _root ); -#endif -} - -simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept { - assert_valid_position(token._position + required_tokens - 1); -} - -simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept { -#ifndef SIMDJSON_CLANG_VISUAL_STUDIO - SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] ); - SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] ); -#endif -} - -simdjson_inline bool json_iterator::at_end() const noexcept { - return position() == end_position(); -} -simdjson_inline token_position json_iterator::end_position() const noexcept { - uint32_t n_structural_indexes{parser->implementation->n_structural_indexes}; - return &parser->implementation->structural_indexes[n_structural_indexes]; -} - -inline std::string json_iterator::to_string() const noexcept { - if( !is_alive() ) { return "dead json_iterator instance"; } - const char * current_structural = reinterpret_cast(token.peek()); - return std::string("json_iterator [ depth : ") + std::to_string(_depth) - + std::string(", structural : '") + std::string(current_structural,1) - + std::string("', offset : ") + std::to_string(token.current_offset()) - + std::string("', error : ") + error_message(error) - + std::string(" ]"); -} - -inline simdjson_result json_iterator::current_location() noexcept { - if (!is_alive()) { // Unrecoverable error - if (!at_root()) { - return reinterpret_cast(token.peek(-1)); - } else { - return reinterpret_cast(token.peek()); - } - } - if (at_end()) { - return OUT_OF_BOUNDS; - } - return reinterpret_cast(token.peek()); -} - -simdjson_inline bool json_iterator::is_alive() const noexcept { - return parser; -} - -simdjson_inline void json_iterator::abandon() noexcept { - parser = nullptr; - _depth = 0; -} - -simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept { -#if SIMDJSON_CHECK_EOF - assert_more_tokens(); -#endif // SIMDJSON_CHECK_EOF - return token.return_current_and_advance(); -} - -simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept { - // deliberately done without safety guard: - return token.peek(0); -} - -simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept { -#if SIMDJSON_CHECK_EOF - assert_more_tokens(delta+1); -#endif // SIMDJSON_CHECK_EOF - return token.peek(delta); -} - -simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept { -#if SIMDJSON_CHECK_EOF - assert_more_tokens(delta+1); -#endif // #if SIMDJSON_CHECK_EOF - return token.peek_length(delta); -} - -simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept { - // todo: currently we require end-of-string buffering, but the following - // assert_valid_position should be turned on if/when we lift that condition. - // assert_valid_position(position); - // This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF - // is ON by default, we have no choice but to disable it for real with a comment. - return token.peek(position); -} - -simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept { -#if SIMDJSON_CHECK_EOF - assert_valid_position(position); -#endif // SIMDJSON_CHECK_EOF - return token.peek_length(position); -} - -simdjson_inline token_position json_iterator::last_position() const noexcept { - // The following line fails under some compilers... - // SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0); - // since it has side-effects. - uint32_t n_structural_indexes{parser->implementation->n_structural_indexes}; - SIMDJSON_ASSUME(n_structural_indexes > 0); - return &parser->implementation->structural_indexes[n_structural_indexes - 1]; -} -simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept { - return token.peek(last_position()); -} - -simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept { - SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1); - SIMDJSON_ASSUME(_depth == parent_depth + 1); - _depth = parent_depth; -} - -simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept { - SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX); - SIMDJSON_ASSUME(_depth == child_depth - 1); - _depth = child_depth; -} - -simdjson_inline depth_t json_iterator::depth() const noexcept { - return _depth; -} - -simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept { - return _string_buf_loc; -} - -simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept { - SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD); - logger::log_error(*this, message); - error = _error; - return error; -} - -simdjson_inline token_position json_iterator::position() const noexcept { - return token.position(); -} - -simdjson_inline simdjson_result json_iterator::unescape(raw_json_string in) noexcept { - return parser->unescape(in, _string_buf_loc); -} - -simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept { - SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX); - SIMDJSON_ASSUME(_depth == child_depth - 1); -#if SIMDJSON_DEVELOPMENT_CHECKS -#ifndef SIMDJSON_CLANG_VISUAL_STUDIO - SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]); -#endif -#endif - token.set_position(position); - _depth = child_depth; -} - -#if SIMDJSON_DEVELOPMENT_CHECKS - -simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept { - return parser->start_positions[depth]; -} - -simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept { - parser->start_positions[depth] = position; -} - -#endif - - -simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept { - SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD); - logger::log_error(*this, message); - return _error; -} - -template -simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t (&tmpbuf)[N]) noexcept { - // Let us guard against silly cases: - if((N < max_len) || (N == 0)) { return false; } - // Truncate whitespace to fit the buffer. - if (max_len > N-1) { - // if (jsoncharutils::is_not_structural_or_whitespace(json[N-1])) { return false; } - max_len = N-1; - } - - // Copy to the buffer. - std::memcpy(tmpbuf, json, max_len); - tmpbuf[max_len] = ' '; - return true; -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_iterator &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/json_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/value_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline value_iterator::value_iterator( - json_iterator *json_iter, - depth_t depth, - token_position start_position -) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} -{ -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_root_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { - assert_at_container_start(); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - if (*_json_iter->peek() == '}') { - logger::log_value(*_json_iter, "empty object"); - _json_iter->return_current_and_advance(); - end_container(); - return false; - } - return true; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - if (*_json_iter->peek_last() != '}') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); - } - // If the last character is } *and* the first gibberish character is also '}' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed object. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return started_object(); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { -#if SIMDJSON_CHECK_EOF - if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } - // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - _json_iter->ascend_to(depth()-1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); - - // It's illegal to call this unless there are more tokens: anything that ends in } or ] is - // obligated to verify there are more tokens if they are not the top level. - switch (*_json_iter->return_current_and_advance()) { - case '}': - logger::log_end_value(*_json_iter, "object"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between object fields"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { - error_code error; - bool has_value; - // - // Initially, the object can be in one of a few different places: - // - // 1. The start of the object, at the first field: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - if (at_first_field()) { - has_value = true; - - // - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this isn't perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - return false; - - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - if ((error = skip_child() )) { abandon(); return error; } - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - while (has_value) { - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - //if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); // Skip the value entirely - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - - // If the loop ended, we're out of fields to look at. - return false; -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { - /** - * When find_field_unordered_raw is called, we can either be pointing at the - * first key, pointing outside (at the closing brace) or if a key was matched - * we can be either pointing right afterthe ':' right before the value (that we need skip), - * or we may have consumed the value and we might be at a comma or at the - * final brace (ready for a call to has_next_field()). - */ - error_code error; - bool has_value; - - // First, we scan from that point to the end. - // If we don't find a match, we may loop back around, and scan from the beginning to that point. - token_position search_start = _json_iter->position(); - - // We want to know whether we need to go back to the beginning. - bool at_first = at_first_field(); - /////////////// - // Initially, the object can be in one of a few different places: - // - // 1. At the first key: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - // - if (at_first) { - has_value = true; - - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { - -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this isn't perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - SIMDJSON_TRY(reset_object().get(has_value)); - at_first = true; - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - // If someone queried a key but they not did access the value, then we are left pointing - // at the ':' and we need to move forward through the value... If the value was - // processed then skip_child() does not move the iterator (but may adjust the depth). - if ((error = skip_child() )) { abandon(); return error; } - search_start = _json_iter->position(); - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - - // After initial processing, we will be in one of two states: - // - // ``` - // // At the beginning of a field - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // At the end of the object - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // ``` - // - // Next, we find a match starting from the current position. - while (has_value) { - SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field - - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - // if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - // Performance note: it maybe wasteful to rewind to the beginning when there might be - // no other query following. Indeed, it would require reskipping the whole object. - // Instead, you can just stay where you are. If there is a new query, there is always time - // to rewind. - if(at_first) { return false; } - - // If we reach the end without finding a match, search the rest of the fields starting at the - // beginning of the object. - // (We have already run through the object before, so we've already validated its structure. We - // don't check errors in this bit.) - SIMDJSON_TRY(reset_object().get(has_value)); - while (true) { - SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object - SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field - - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - error = field_key().get(actual_key); SIMDJSON_ASSUME(!error); - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - error = field_value(); SIMDJSON_ASSUME(!error); - - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - // if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); - // If we reached the end of the key-value pair we started from, then we know - // that the key is not there so we return false. We are either right before - // the next comma or the final brace. - if(_json_iter->position() == search_start) { return false; } - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error); - // If we make the mistake of exiting here, then we could be left pointing at a key - // in the middle of an object. That's not an allowable state. - } - // If the loop ended, we're out of fields to look at. The program should - // never reach this point. - return false; -} -SIMDJSON_POP_DISABLE_WARNINGS - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::field_key() noexcept { - assert_at_next(); - - const uint8_t *key = _json_iter->return_current_and_advance(); - if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); } - return raw_json_string(key); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept { - assert_at_next(); - - if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); } - _json_iter->descend_to(depth()+1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_array() noexcept { - SIMDJSON_TRY( start_container('[', "Not an array", "array") ); - return started_array(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_array() noexcept { - SIMDJSON_TRY( start_container('[', "Not an array", "array") ); - return started_root_array(); -} - -inline std::string value_iterator::to_string() const noexcept { - auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", "); - if(_json_iter != nullptr) { answer += _json_iter->to_string(); } - answer += std::string(" ]"); - return answer; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_array() noexcept { - assert_at_container_start(); - if (*_json_iter->peek() == ']') { - logger::log_value(*_json_iter, "empty array"); - _json_iter->return_current_and_advance(); - SIMDJSON_TRY( end_container() ); - return false; - } - _json_iter->descend_to(depth()+1); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - return true; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_array() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - if (*_json_iter->peek_last() != ']') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end"); - } - // If the last character is ] *and* the first gibberish character is also ']' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed array. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return started_array(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_element() noexcept { - assert_at_next(); - - logger::log_event(*this, "has_next_element"); - switch (*_json_iter->return_current_and_advance()) { - case ']': - logger::log_end_value(*_json_iter, "array"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - _json_iter->descend_to(depth()+1); - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between array elements"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::parse_bool(const uint8_t *json) const noexcept { - auto not_true = atomparsing::str4ncmp(json, "true"); - auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e'); - bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]); - if (error) { return incorrect_type_error("Not a boolean"); } - return simdjson_result(!not_true); -} -simdjson_inline bool value_iterator::parse_null(const uint8_t *json) const noexcept { - return !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_string() noexcept { - return get_raw_json_string().unescape(json_iter()); -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_raw_json_string() noexcept { - auto json = peek_scalar("string"); - if (*json != '"') { return incorrect_type_error("Not a string"); } - advance_scalar("string"); - return raw_json_string(json+1); -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_uint64() noexcept { - auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64")); - if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_uint64_in_string() noexcept { - auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64")); - if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_int64() noexcept { - auto result = numberparsing::parse_integer(peek_non_root_scalar("int64")); - if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_int64_in_string() noexcept { - auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64")); - if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_double() noexcept { - auto result = numberparsing::parse_double(peek_non_root_scalar("double")); - if(result.error() == SUCCESS) { advance_non_root_scalar("double"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_double_in_string() noexcept { - auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double")); - if(result.error() == SUCCESS) { advance_non_root_scalar("double"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_bool() noexcept { - auto result = parse_bool(peek_non_root_scalar("bool")); - if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); } - return result; -} -simdjson_inline bool value_iterator::is_null() noexcept { - auto result = parse_null(peek_non_root_scalar("null")); - if(result) { advance_non_root_scalar("null"); } - return result; -} -simdjson_inline bool value_iterator::is_negative() noexcept { - return numberparsing::is_negative(peek_non_root_scalar("numbersign")); -} -simdjson_inline bool value_iterator::is_root_negative() noexcept { - return numberparsing::is_negative(peek_root_scalar("numbersign")); -} -simdjson_inline simdjson_result value_iterator::is_integer() noexcept { - return numberparsing::is_integer(peek_non_root_scalar("integer")); -} -simdjson_inline simdjson_result value_iterator::get_number_type() noexcept { - return numberparsing::get_number_type(peek_non_root_scalar("integer")); -} -simdjson_inline simdjson_result value_iterator::get_number() noexcept { - number num; - error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num); - if(error) { return error; } - return num; -} - -simdjson_inline simdjson_result value_iterator::is_root_integer() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("is_root_integer"); - uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - return false; // if there are more than 20 characters, it cannot be represented as an integer. - } - return numberparsing::is_integer(tmpbuf); -} - -simdjson_inline simdjson_result value_iterator::get_root_number_type() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("number"); - // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/, - // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest - // number: -0.e-308. - uint8_t tmpbuf[1074+8+1]; - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); - return NUMBER_ERROR; - } - return numberparsing::get_number_type(tmpbuf); -} -simdjson_inline simdjson_result value_iterator::get_root_number() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("number"); - // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/, - // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest - // number: -0.e-308. - uint8_t tmpbuf[1074+8+1]; - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); - return NUMBER_ERROR; - } - number num; - error_code error = numberparsing::parse_number(tmpbuf, num); - if(error) { return error; } - advance_root_scalar("number"); - return num; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_string() noexcept { - return get_string(); -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_raw_json_string() noexcept { - return get_raw_json_string(); -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_uint64() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("uint64"); - uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters"); - return NUMBER_ERROR; - } - auto result = numberparsing::parse_unsigned(tmpbuf); - if(result.error() == SUCCESS) { advance_root_scalar("uint64"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_uint64_in_string() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("uint64"); - uint8_t tmpbuf[20+1]; // <20 digits> is the longest possible unsigned integer - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters"); - return NUMBER_ERROR; - } - auto result = numberparsing::parse_unsigned_in_string(tmpbuf); - if(result.error() == SUCCESS) { advance_root_scalar("uint64"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_int64() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("int64"); - uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters"); - return NUMBER_ERROR; - } - - auto result = numberparsing::parse_integer(tmpbuf); - if(result.error() == SUCCESS) { advance_root_scalar("int64"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_int64_in_string() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("int64"); - uint8_t tmpbuf[20+1]; // -<19 digits> is the longest possible integer - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters"); - return NUMBER_ERROR; - } - - auto result = numberparsing::parse_integer_in_string(tmpbuf); - if(result.error() == SUCCESS) { advance_root_scalar("int64"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_double() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("double"); - // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/, - // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest - // number: -0.e-308. - uint8_t tmpbuf[1074+8+1]; - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); - return NUMBER_ERROR; - } - auto result = numberparsing::parse_double(tmpbuf); - if(result.error() == SUCCESS) { advance_root_scalar("double"); } - return result; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_double_in_string() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("double"); - // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/, - // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest - // number: -0.e-308. - uint8_t tmpbuf[1074+8+1]; - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { - logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters"); - return NUMBER_ERROR; - } - auto result = numberparsing::parse_double_in_string(tmpbuf); - if(result.error() == SUCCESS) { advance_root_scalar("double"); } - return result; -} -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::get_root_bool() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("bool"); - uint8_t tmpbuf[5+1]; - if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf)) { return incorrect_type_error("Not a boolean"); } - auto result = parse_bool(tmpbuf); - if(result.error() == SUCCESS) { advance_root_scalar("bool"); } - return result; -} -simdjson_inline bool value_iterator::is_root_null() noexcept { - auto max_len = peek_start_length(); - auto json = peek_root_scalar("null"); - bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") && - (max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[5]))); - if(result) { advance_root_scalar("null"); } - return result; -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept { - SIMDJSON_ASSUME( _json_iter->token._position > _start_position ); - SIMDJSON_ASSUME( _json_iter->_depth >= _depth ); - - return _json_iter->skip_child(depth()); -} - -simdjson_inline value_iterator value_iterator::child() const noexcept { - assert_at_child(); - return { _json_iter, depth()+1, _json_iter->token.position() }; -} - -// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller -// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is -// marked non-inline. -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_inline bool value_iterator::is_open() const noexcept { - return _json_iter->depth() >= depth(); -} -SIMDJSON_POP_DISABLE_WARNINGS - -simdjson_inline bool value_iterator::at_end() const noexcept { - return _json_iter->at_end(); -} - -simdjson_inline bool value_iterator::at_start() const noexcept { - return _json_iter->token.position() == start_position(); -} - -simdjson_inline bool value_iterator::at_first_field() const noexcept { - SIMDJSON_ASSUME( _json_iter->token._position > _start_position ); - return _json_iter->token.position() == start_position() + 1; -} - -simdjson_inline void value_iterator::abandon() noexcept { - _json_iter->abandon(); -} - -simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept { - return _depth; -} -simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept { - return _json_iter->error; -} -simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept { - return _json_iter->string_buf_loc(); -} -simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept { - return *_json_iter; -} -simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept { - return *_json_iter; -} - -simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept { - return _json_iter->peek(start_position()); -} -simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept { - return _json_iter->peek_length(start_position()); -} - -simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept { - logger::log_value(*_json_iter, start_position(), depth(), type); - // If we're not at the position anymore, we don't want to advance the cursor. - if (!is_at_start()) { return peek_start(); } - - // Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value. - assert_at_start(); - return _json_iter->peek(); -} - -simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept { - logger::log_value(*_json_iter, start_position(), depth(), type); - // If we're not at the position anymore, we don't want to advance the cursor. - if (!is_at_start()) { return; } - - // Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value. - assert_at_start(); - _json_iter->return_current_and_advance(); - _json_iter->ascend_to(depth()-1); -} - -simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept { - logger::log_start_value(*_json_iter, start_position(), depth(), type); - // If we're not at the position anymore, we don't want to advance the cursor. - const uint8_t *json; - if (!is_at_start()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - json = peek_start(); - if (*json != start_char) { return incorrect_type_error(incorrect_type_message); } - } else { - assert_at_start(); - /** - * We should be prudent. Let us peek. If it is not the right type, we - * return an error. Only once we have determined that we have the right - * type are we allowed to advance! - */ - json = _json_iter->peek(); - if (*json != start_char) { return incorrect_type_error(incorrect_type_message); } - _json_iter->return_current_and_advance(); - } - - - return SUCCESS; -} - - -simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept { - logger::log_value(*_json_iter, start_position(), depth(), type); - if (!is_at_start()) { return peek_start(); } - - assert_at_root(); - return _json_iter->peek(); -} -simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept { - logger::log_value(*_json_iter, start_position(), depth(), type); - if (!is_at_start()) { return peek_start(); } - - assert_at_non_root_start(); - return _json_iter->peek(); -} - -simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept { - logger::log_value(*_json_iter, start_position(), depth(), type); - if (!is_at_start()) { return; } - - assert_at_root(); - _json_iter->return_current_and_advance(); - _json_iter->ascend_to(depth()-1); -} -simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept { - logger::log_value(*_json_iter, start_position(), depth(), type); - if (!is_at_start()) { return; } - - assert_at_non_root_start(); - _json_iter->return_current_and_advance(); - _json_iter->ascend_to(depth()-1); -} - -simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept { - logger::log_error(*_json_iter, start_position(), depth(), message); - return INCORRECT_TYPE; -} - -simdjson_inline bool value_iterator::is_at_start() const noexcept { - return position() == start_position(); -} - -simdjson_inline bool value_iterator::is_at_key() const noexcept { - // Keys are at the same depth as the object. - // Note here that we could be safer and check that we are within an object, - // but we do not. - return _depth == _json_iter->_depth && *_json_iter->peek() == '"'; -} - -simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept { - // We can legitimately be either at the first value ([1]), or after the array if it's empty ([]). - auto delta = position() - start_position(); - return delta == 1 || delta == 2; -} - -inline void value_iterator::assert_at_start() const noexcept { - SIMDJSON_ASSUME( _json_iter->token._position == _start_position ); - SIMDJSON_ASSUME( _json_iter->_depth == _depth ); - SIMDJSON_ASSUME( _depth > 0 ); -} - -inline void value_iterator::assert_at_container_start() const noexcept { - SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 ); - SIMDJSON_ASSUME( _json_iter->_depth == _depth ); - SIMDJSON_ASSUME( _depth > 0 ); -} - -inline void value_iterator::assert_at_next() const noexcept { - SIMDJSON_ASSUME( _json_iter->token._position > _start_position ); - SIMDJSON_ASSUME( _json_iter->_depth == _depth ); - SIMDJSON_ASSUME( _depth > 0 ); -} - -simdjson_inline void value_iterator::move_at_start() noexcept { - _json_iter->_depth = _depth; - _json_iter->token.set_position(_start_position); -} - -simdjson_inline void value_iterator::move_at_container_start() noexcept { - _json_iter->_depth = _depth; - _json_iter->token.set_position(_start_position + 1); -} - -simdjson_inline simdjson_result value_iterator::reset_array() noexcept { - move_at_container_start(); - return started_array(); -} - -simdjson_inline simdjson_result value_iterator::reset_object() noexcept { - move_at_container_start(); - return started_object(); -} - -inline void value_iterator::assert_at_child() const noexcept { - SIMDJSON_ASSUME( _json_iter->token._position > _start_position ); - SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 ); - SIMDJSON_ASSUME( _depth > 0 ); -} - -inline void value_iterator::assert_at_root() const noexcept { - assert_at_start(); - SIMDJSON_ASSUME( _depth == 1 ); -} - -inline void value_iterator::assert_at_non_root_start() const noexcept { - assert_at_start(); - SIMDJSON_ASSUME( _depth > 1 ); -} - -inline void value_iterator::assert_is_valid() const noexcept { - SIMDJSON_ASSUME( _json_iter != nullptr ); -} - -simdjson_inline bool value_iterator::is_valid() const noexcept { - return _json_iter != nullptr; -} - -simdjson_inline simdjson_result value_iterator::type() const noexcept { - switch (*peek_start()) { - case '{': - return json_type::object; - case '[': - return json_type::array; - case '"': - return json_type::string; - case 'n': - return json_type::null; - case 't': case 'f': - return json_type::boolean; - case '-': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - return json_type::number; - default: - return TAPE_ERROR; - } -} - -simdjson_inline token_position value_iterator::start_position() const noexcept { - return _start_position; -} - -simdjson_inline token_position value_iterator::position() const noexcept { - return _json_iter->position(); -} - -simdjson_inline token_position value_iterator::end_position() const noexcept { - return _json_iter->end_position(); -} - -simdjson_inline token_position value_iterator::last_position() const noexcept { - return _json_iter->last_position(); -} - -simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept { - return _json_iter->report_error(error, message); -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value_iterator &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/value_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/array_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline array_iterator::array_iterator(const value_iterator &_iter) noexcept - : iter{_iter} -{} - -simdjson_inline simdjson_result array_iterator::operator*() noexcept { - if (iter.error()) { iter.abandon(); return iter.error(); } - return value(iter.child()); -} -simdjson_inline bool array_iterator::operator==(const array_iterator &other) const noexcept { - return !(*this != other); -} -simdjson_inline bool array_iterator::operator!=(const array_iterator &) const noexcept { - return iter.is_open(); -} -simdjson_inline array_iterator &array_iterator::operator++() noexcept { - error_code error; - // PERF NOTE this is a safety rail ... users should exit loops as soon as they receive an error, so we'll never get here. - // However, it does not seem to make a perf difference, so we add it out of an abundance of caution. - if (( error = iter.error() )) { return *this; } - if (( error = iter.skip_child() )) { return *this; } - if (( error = iter.has_next_element().error() )) { return *this; } - return *this; -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array_iterator &&value -) noexcept - : SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base(std::forward(value)) -{ - first.iter.assert_is_valid(); -} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : SIMDJSON_BUILTIN_IMPLEMENTATION::implementation_simdjson_result_base({}, error) -{ -} - -simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; -} -simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; -} -simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; -} -simdjson_inline simdjson_result &simdjson_result::operator++() noexcept { - // Clear the error if there is one, so we don't yield it twice - if (error()) { second = SUCCESS; return *this; } - ++(first); - return *this; -} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/array_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/object_iterator-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -// -// object_iterator -// - -simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept - : iter{_iter} -{} - -simdjson_inline simdjson_result object_iterator::operator*() noexcept { - error_code error = iter.error(); - if (error) { iter.abandon(); return error; } - auto result = field::start(iter); - // TODO this is a safety rail ... users should exit loops as soon as they receive an error. - // Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free. - if (result.error()) { iter.abandon(); } - return result; -} -simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept { - return !(*this != other); -} -simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept { - return iter.is_open(); -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_inline object_iterator &object_iterator::operator++() noexcept { - // TODO this is a safety rail ... users should exit loops as soon as they receive an error. - // Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free. - if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error - - simdjson_unused error_code error; - if ((error = iter.skip_child() )) { return *this; } - - simdjson_unused bool has_value; - if ((error = iter.has_next_field().get(has_value) )) { return *this; }; - return *this; -} -SIMDJSON_POP_DISABLE_WARNINGS - -// -// ### Live States -// -// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is -// always SUCCESS: -// -// - Start: This is the state when the object is first found and the iterator is just past the {. -// In this state, at_start == true. -// - Next: After we hand a scalar value to the user, or an array/object which they then fully -// iterate over, the iterator is at the , or } before the next value. In this state, -// depth == iter.depth, at_start == false, and error == SUCCESS. -// - Unfinished Business: When we hand an array/object to the user which they do not fully -// iterate over, we need to finish that iteration by skipping child values until we reach the -// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS. -// -// ## Error States -// -// In error states, we will yield exactly one more value before stopping. iter.depth == depth -// and at_start is always false. We decrement after yielding the error, moving to the Finished -// state. -// -// - Chained Error: When the object iterator is part of an error chain--for example, in -// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an -// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and -// iter.depth == depth, and at_start == false. We decrement depth when we yield the error. -// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields, -// we flag that as an error and treat it exactly the same as a Chained Error. In this state, -// error == TAPE_ERROR, iter.depth == depth, and at_start == false. -// -// Errors that occur while reading a field to give to the user (such as when the key is not a -// string or the field is missing a colon) are yielded immediately. Depth is then decremented, -// moving to the Finished state without transitioning through an Error state at all. -// -// ## Terminal State -// -// The terminal state has iter.depth < depth. at_start is always false. -// -// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth. -// In this state, iter.depth < depth, at_start == false, and error == SUCCESS. -// - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object_iterator &&value -) noexcept - : implementation_simdjson_result_base(std::forward(value)) -{ - first.iter.assert_is_valid(); -} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base({}, error) -{ -} - -simdjson_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { return error(); } - return *first; -} -// If we're iterating and there is an error, return the error once. -simdjson_inline bool simdjson_result::operator==(const simdjson_result &other) const noexcept { - if (!first.iter.is_valid()) { return !error(); } - return first == other.first; -} -// If we're iterating and there is an error, return the error once. -simdjson_inline bool simdjson_result::operator!=(const simdjson_result &other) const noexcept { - if (!first.iter.is_valid()) { return error(); } - return first != other.first; -} -// Checks for ']' and ',' -simdjson_inline simdjson_result &simdjson_result::operator++() noexcept { - // Clear the error if there is one, so we don't yield it twice - if (error()) { second = SUCCESS; return *this; } - ++first; - return *this; -} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/object_iterator-inl.h */ -/* begin file include/simdjson/generic/ondemand/array-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -// -// ### Live States -// -// While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is -// always SUCCESS: -// -// - Start: This is the state when the array is first found and the iterator is just past the `{`. -// In this state, at_start == true. -// - Next: After we hand a scalar value to the user, or an array/object which they then fully -// iterate over, the iterator is at the `,` before the next value (or `]`). In this state, -// depth == iter->depth, at_start == false, and error == SUCCESS. -// - Unfinished Business: When we hand an array/object to the user which they do not fully -// iterate over, we need to finish that iteration by skipping child values until we reach the -// Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS. -// -// ## Error States -// -// In error states, we will yield exactly one more value before stopping. iter->depth == depth -// and at_start is always false. We decrement after yielding the error, moving to the Finished -// state. -// -// - Chained Error: When the array iterator is part of an error chain--for example, in -// `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an -// array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and -// iter->depth == depth, and at_start == false. We decrement depth when we yield the error. -// - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements, -// we flag that as an error and treat it exactly the same as a Chained Error. In this state, -// error == TAPE_ERROR, iter->depth == depth, and at_start == false. -// -// ## Terminal State -// -// The terminal state has iter->depth < depth. at_start is always false. -// -// - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this -// by decrementing depth. In this state, iter->depth < depth, at_start == false, and -// error == SUCCESS. -// - -simdjson_inline array::array(const value_iterator &_iter) noexcept - : iter{_iter} -{ -} - -simdjson_inline simdjson_result array::start(value_iterator &iter) noexcept { - // We don't need to know if the array is empty to start iteration, but we do want to know if there - // is an error--thus `simdjson_unused`. - simdjson_unused bool has_value; - SIMDJSON_TRY( iter.start_array().get(has_value) ); - return array(iter); -} -simdjson_inline simdjson_result array::start_root(value_iterator &iter) noexcept { - simdjson_unused bool has_value; - SIMDJSON_TRY( iter.start_root_array().get(has_value) ); - return array(iter); -} -simdjson_inline simdjson_result array::started(value_iterator &iter) noexcept { - bool has_value; - SIMDJSON_TRY(iter.started_array().get(has_value)); - return array(iter); -} - -simdjson_inline simdjson_result array::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return array_iterator(iter); -} -simdjson_inline simdjson_result array::end() noexcept { - return array_iterator(iter); -} -simdjson_inline error_code array::consume() noexcept { - auto error = iter.json_iter().skip_child(iter.depth()-1); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result array::raw_json() noexcept { - const uint8_t * starting_point{iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter._json_iter->unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_inline simdjson_result array::count_elements() & noexcept { - size_t count{0}; - // Important: we do not consume any of the values. - for(simdjson_unused auto v : *this) { count++; } - // The above loop will always succeed, but we want to report errors. - if(iter.error()) { return iter.error(); } - // We need to move back at the start because we expect users to iterate through - // the array after counting the number of elements. - iter.reset_array(); - return count; -} -SIMDJSON_POP_DISABLE_WARNINGS - -simdjson_inline simdjson_result array::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_array().get(is_not_empty); - if(error) { return error; } - return !is_not_empty; -} - -inline simdjson_result array::reset() & noexcept { - return iter.reset_array(); -} - -inline simdjson_result array::at_pointer(std::string_view json_pointer) noexcept { - if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; } - json_pointer = json_pointer.substr(1); - // - means "the append position" or "the element after the end of the array" - // We don't support this, because we're returning a real element, not a position. - if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; } - - // Read the array index - size_t array_index = 0; - size_t i; - for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { - uint8_t digit = uint8_t(json_pointer[i] - '0'); - // Check for non-digit in array index. If it's there, we're trying to get a field in an object - if (digit > 9) { return INCORRECT_TYPE; } - array_index = array_index*10 + digit; - } - - // 0 followed by other digits is invalid - if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" - - // Empty string is invalid; so is a "/" with no digits before it - if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" - // Get the child - auto child = at(array_index); - // If there is an error, it ends here - if(child.error()) { - return child; - } - - // If there is a /, we're not done yet, call recursively. - if (i < json_pointer.length()) { - child = child.at_pointer(json_pointer.substr(i)); - } - return child; -} - -simdjson_inline simdjson_result array::at(size_t index) noexcept { - size_t i = 0; - for (auto value : *this) { - if (i == index) { return value; } - i++; - } - return INDEX_OUT_OF_BOUNDS; -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array &&value -) noexcept - : implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept - : implementation_simdjson_result_base(error) -{ -} - -simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::is_empty() & noexcept { - if (error()) { return error(); } - return first.is_empty(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/array-inl.h */ -/* begin file include/simdjson/generic/ondemand/document-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} -{ - logger::log_start_value(iter, "document"); -} - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); -} - -inline void document::rewind() noexcept { - iter.rewind(); -} - -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); -} - -inline simdjson_result document::current_location() noexcept { - return iter.current_location(); -} - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); -} - -inline bool document::is_alive() noexcept { - return iter.is_alive(); -} -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); -} -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); -} -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } -} -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - iter.assert_at_document_depth(); - switch (*iter.peek()) { - case '[': - case '{': - return value(get_root_value_iterator()); - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - // return value(get_root_value_iterator()); - } -} -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); -} -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); -} -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(); -} -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(); -} -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(); -} -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(); -} -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(); -} -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(); -} -simdjson_inline simdjson_result document::get_string() noexcept { - return get_root_value_iterator().get_root_string(); -} -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(); -} -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(); -} -simdjson_inline bool document::is_null() noexcept { - return get_root_value_iterator().is_root_null(); -} - -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return get_string(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); -} -template simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); -} - -#if SIMDJSON_EXCEPTIONS -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { - auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } - return answer; -} -simdjson_inline simdjson_result document::count_fields() & noexcept { - auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } - return answer; -} -simdjson_inline simdjson_result document::at(size_t index) & noexcept { - auto a = get_array(); - return a.at(index); -} -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} - -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); -} - -simdjson_inline simdjson_result document::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); -} - -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); -} - -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(); -} - -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(); -} - -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(); -} - - -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_start_length()); -} - -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); - } - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - return INVALID_JSON_POINTER; - } -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base( - error - ) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_string() noexcept { - if (error()) { return error(); } - return first.get_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline bool simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} - -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { - if (error()) { return error(); } - return first.get(); -} -template -simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first).get(); -} -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { - if (error()) { return error(); } - return first.get(out); -} -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document &out) && noexcept { - if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} - - -simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} - - -#if SIMDJSON_EXCEPTIONS -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} - - -} // namespace simdjson - - -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_uint64(); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_int64(); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_double(); } -simdjson_inline simdjson_result document_reference::get_string() noexcept { return doc->get_string(); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_raw_json_string(); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_bool(); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline bool document_reference::is_null() noexcept { return doc->is_null(); } - -#if SIMDJSON_EXCEPTIONS -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return uint64_t(*doc); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return int64_t(*doc); } -simdjson_inline document_reference::operator double() noexcept(false) { return double(*doc); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return raw_json_string(*doc); } -simdjson_inline document_reference::operator bool() noexcept(false) { return bool(*doc); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->is_integer(); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_number_type(); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_number(); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - - - -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} - - -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_string() noexcept { - if (error()) { return error(); } - return first.get_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline bool simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline bool simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object() & noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} - - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/document-inl.h */ -/* begin file include/simdjson/generic/ondemand/value-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} -{ -} -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} - -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string() noexcept { - return iter.get_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline bool value::is_null() noexcept { - return iter.is_null(); -} - -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } - -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); -} - -#if SIMDJSON_EXCEPTIONS -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} -#endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); -} - -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); -} - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); -} - -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); -} - -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} - -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} - -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - return INVALID_JSON_POINTER; - } -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; -} - -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} - -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; -} - -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_string() noexcept { - if (error()) { return error(); } - return first.get_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline bool simdjson_result::is_null() noexcept { - if (error()) { return false; } - return first.is_null(); -} - -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); -} -template<> simdjson_inline error_code simdjson_result::get(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/value-inl.h */ -/* begin file include/simdjson/generic/ondemand/field-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -// clang 6 doesn't think the default constructor can be noexcept, so we make it explicit -simdjson_inline field::field() noexcept : std::pair() {} - -simdjson_inline field::field(raw_json_string key, ondemand::value &&value) noexcept - : std::pair(key, std::forward(value)) -{ -} - -simdjson_inline simdjson_result field::start(value_iterator &parent_iter) noexcept { - raw_json_string key; - SIMDJSON_TRY( parent_iter.field_key().get(key) ); - SIMDJSON_TRY( parent_iter.field_value() ); - return field::start(parent_iter, key); -} - -simdjson_inline simdjson_result field::start(const value_iterator &parent_iter, raw_json_string key) noexcept { - return field(key, parent_iter.child()); -} - -simdjson_inline simdjson_warn_unused simdjson_result field::unescaped_key() noexcept { - SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() but Visual Studio won't let us. - simdjson_result answer = first.unescape(second.iter.json_iter()); - first.consume(); - return answer; -} - -simdjson_inline raw_json_string field::key() const noexcept { - SIMDJSON_ASSUME(first.buf != nullptr); // We would like to call .alive() by Visual Studio won't let us. - return first; -} - -simdjson_inline value &field::value() & noexcept { - return second; -} - -simdjson_inline value field::value() && noexcept { - return std::forward(*this).second; -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::field &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} - -simdjson_inline simdjson_result simdjson_result::key() noexcept { - if (error()) { return error(); } - return first.key(); -} -simdjson_inline simdjson_result simdjson_result::unescaped_key() noexcept { - if (error()) { return error(); } - return first.unescaped_key(); -} -simdjson_inline simdjson_result simdjson_result::value() noexcept { - if (error()) { return error(); } - return std::move(first.value()); -} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/field-inl.h */ -/* begin file include/simdjson/generic/ondemand/object-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline simdjson_result object::find_field_unordered(const std::string_view key) & noexcept { - bool has_value; - SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) ); - if (!has_value) { return NO_SUCH_FIELD; } - return value(iter.child()); -} -simdjson_inline simdjson_result object::find_field_unordered(const std::string_view key) && noexcept { - bool has_value; - SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) ); - if (!has_value) { return NO_SUCH_FIELD; } - return value(iter.child()); -} -simdjson_inline simdjson_result object::operator[](const std::string_view key) & noexcept { - return find_field_unordered(key); -} -simdjson_inline simdjson_result object::operator[](const std::string_view key) && noexcept { - return std::forward(*this).find_field_unordered(key); -} -simdjson_inline simdjson_result object::find_field(const std::string_view key) & noexcept { - bool has_value; - SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) ); - if (!has_value) { return NO_SUCH_FIELD; } - return value(iter.child()); -} -simdjson_inline simdjson_result object::find_field(const std::string_view key) && noexcept { - bool has_value; - SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) ); - if (!has_value) { return NO_SUCH_FIELD; } - return value(iter.child()); -} - -simdjson_inline simdjson_result object::start(value_iterator &iter) noexcept { - SIMDJSON_TRY( iter.start_object().error() ); - return object(iter); -} -simdjson_inline simdjson_result object::start_root(value_iterator &iter) noexcept { - SIMDJSON_TRY( iter.start_root_object().error() ); - return object(iter); -} -simdjson_inline error_code object::consume() noexcept { - if(iter.is_at_key()) { - /** - * whenever you are pointing at a key, calling skip_child() is - * unsafe because you will hit a string and you will assume that - * it is string value, and this mistake will lead you to make bad - * depth computation. - */ - /** - * We want to 'consume' the key. We could really - * just do _json_iter->return_current_and_advance(); at this - * point, but, for clarity, we will use the high-level API to - * eat the key. We assume that the compiler optimizes away - * most of the work. - */ - simdjson_unused raw_json_string actual_key; - auto error = iter.field_key().get(actual_key); - if (error) { iter.abandon(); return error; }; - // Let us move to the value while we are at it. - if ((error = iter.field_value())) { iter.abandon(); return error; } - } - auto error_skip = iter.json_iter().skip_child(iter.depth()-1); - if(error_skip) { iter.abandon(); } - return error_skip; -} - -simdjson_inline simdjson_result object::raw_json() noexcept { - const uint8_t * starting_point{iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - const uint8_t * final_point{iter._json_iter->peek(0)}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result object::started(value_iterator &iter) noexcept { - SIMDJSON_TRY( iter.started_object().error() ); - return object(iter); -} - -simdjson_inline object object::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline object::object(const value_iterator &_iter) noexcept - : iter{_iter} -{ -} - -simdjson_inline simdjson_result object::begin() noexcept { -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } -#endif - return object_iterator(iter); -} -simdjson_inline simdjson_result object::end() noexcept { - return object_iterator(iter); -} - -inline simdjson_result object::at_pointer(std::string_view json_pointer) noexcept { - if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; } - json_pointer = json_pointer.substr(1); - size_t slash = json_pointer.find('/'); - std::string_view key = json_pointer.substr(0, slash); - // Grab the child with the given key - simdjson_result child; - - // If there is an escape character in the key, unescape it and then get the child. - size_t escape = key.find('~'); - if (escape != std::string_view::npos) { - // Unescape the key - std::string unescaped(key); - do { - switch (unescaped[escape+1]) { - case '0': - unescaped.replace(escape, 2, "~"); - break; - case '1': - unescaped.replace(escape, 2, "/"); - break; - default: - return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer"); - } - escape = unescaped.find('~', escape+1); - } while (escape != std::string::npos); - child = find_field(unescaped); // Take note find_field does not unescape keys when matching - } else { - child = find_field(key); - } - if(child.error()) { - return child; // we do not continue if there was an error - } - // If there is a /, we have to recurse and look up more of the path - if (slash != std::string_view::npos) { - child = child.at_pointer(json_pointer.substr(slash)); - } - return child; -} - -simdjson_inline simdjson_result object::count_fields() & noexcept { - size_t count{0}; - // Important: we do not consume any of the values. - for(simdjson_unused auto v : *this) { count++; } - // The above loop will always succeed, but we want to report errors. - if(iter.error()) { return iter.error(); } - // We need to move back at the start because we expect users to iterate through - // the object after counting the number of elements. - iter.reset_object(); - return count; -} - -simdjson_inline simdjson_result object::is_empty() & noexcept { - bool is_not_empty; - auto error = iter.reset_object().get(is_not_empty); - if(error) { return error; } - return !is_not_empty; -} - -simdjson_inline simdjson_result object::reset() & noexcept { - return iter.reset_object(); -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -simdjson_inline simdjson_result simdjson_result::begin() noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() noexcept { - if (error()) { return error(); } - return first.end(); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first)[key]; -} -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { - if (error()) { return error(); } - return std::forward(first).find_field(key); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } - return first.at_pointer(json_pointer); -} - -inline simdjson_result simdjson_result::reset() noexcept { - if (error()) { return error(); } - return first.reset(); -} - -inline simdjson_result simdjson_result::is_empty() noexcept { - if (error()) { return error(); } - return first.is_empty(); -} - -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/object-inl.h */ -/* begin file include/simdjson/generic/ondemand/parser-inl.h */ -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -simdjson_inline parser::parser(size_t max_capacity) noexcept - : _max_capacity{max_capacity} { -} - -simdjson_warn_unused simdjson_inline error_code parser::allocate(size_t new_capacity, size_t new_max_depth) noexcept { - if (new_capacity > max_capacity()) { return CAPACITY; } - if (string_buf && new_capacity == capacity() && new_max_depth == max_depth()) { return SUCCESS; } - - // string_capacity copied from document::allocate - _capacity = 0; - size_t string_capacity = SIMDJSON_ROUNDUP_N(5 * new_capacity / 3 + SIMDJSON_PADDING, 64); - string_buf.reset(new (std::nothrow) uint8_t[string_capacity]); -#if SIMDJSON_DEVELOPMENT_CHECKS - start_positions.reset(new (std::nothrow) token_position[new_max_depth]); -#endif - if (implementation) { - SIMDJSON_TRY( implementation->set_capacity(new_capacity) ); - SIMDJSON_TRY( implementation->set_max_depth(new_max_depth) ); - } else { - SIMDJSON_TRY( simdjson::get_active_implementation()->create_dom_parser_implementation(new_capacity, new_max_depth, implementation) ); - } - _capacity = new_capacity; - _max_depth = new_max_depth; - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - // Allocate if needed - if (capacity() < json.length() || !string_buf) { - SIMDJSON_TRY( allocate(json.length(), max_depth()) ); - } - - // Run stage 1. - SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); - return document::start({ reinterpret_cast(json.data()), this }); -} - -simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const char *json, size_t len, size_t allocated) & noexcept { - return iterate(padded_string_view(json, len, allocated)); -} - -simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const uint8_t *json, size_t len, size_t allocated) & noexcept { - return iterate(padded_string_view(json, len, allocated)); -} - -simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(std::string_view json, size_t allocated) & noexcept { - return iterate(padded_string_view(json, allocated)); -} - -simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const std::string &json) & noexcept { - return iterate(padded_string_view(json)); -} - -simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result &result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result without throwing an exception - SIMDJSON_TRY( result.error() ); - padded_string_view json = result.value_unsafe(); - return iterate(json); -} - -simdjson_warn_unused simdjson_inline simdjson_result parser::iterate(const simdjson_result &result) & noexcept { - // We don't presently have a way to temporarily get a const T& from a simdjson_result without throwing an exception - SIMDJSON_TRY( result.error() ); - const padded_string &json = result.value_unsafe(); - return iterate(json); -} - -simdjson_warn_unused simdjson_inline simdjson_result parser::iterate_raw(padded_string_view json) & noexcept { - if (json.padding() < SIMDJSON_PADDING) { return INSUFFICIENT_PADDING; } - - // Allocate if needed - if (capacity() < json.length()) { - SIMDJSON_TRY( allocate(json.length(), max_depth()) ); - } - - // Run stage 1. - SIMDJSON_TRY( implementation->stage1(reinterpret_cast(json.data()), json.length(), stage1_mode::regular) ); - return json_iterator(reinterpret_cast(json.data()), this); -} - -inline simdjson_result parser::iterate_many(const uint8_t *buf, size_t len, size_t batch_size) noexcept { - if(batch_size < MINIMAL_BATCH_SIZE) { batch_size = MINIMAL_BATCH_SIZE; } - return document_stream(*this, buf, len, batch_size); -} -inline simdjson_result parser::iterate_many(const char *buf, size_t len, size_t batch_size) noexcept { - return iterate_many(reinterpret_cast(buf), len, batch_size); -} -inline simdjson_result parser::iterate_many(const std::string &s, size_t batch_size) noexcept { - return iterate_many(s.data(), s.length(), batch_size); -} -inline simdjson_result parser::iterate_many(const padded_string &s, size_t batch_size) noexcept { - return iterate_many(s.data(), s.length(), batch_size); -} - -simdjson_inline size_t parser::capacity() const noexcept { - return _capacity; -} -simdjson_inline size_t parser::max_capacity() const noexcept { - return _max_capacity; -} -simdjson_inline size_t parser::max_depth() const noexcept { - return _max_depth; -} - -simdjson_inline void parser::set_max_capacity(size_t max_capacity) noexcept { - size_t MINIMAL_DOCUMENT_CAPACITY = 32; - if(max_capacity < MINIMAL_DOCUMENT_CAPACITY) { - _max_capacity = max_capacity; - } else { - _max_capacity = MINIMAL_DOCUMENT_CAPACITY; - } -} - -simdjson_inline simdjson_warn_unused simdjson_result parser::unescape(raw_json_string in, uint8_t *&dst) const noexcept { - uint8_t *end = implementation->parse_string(in.buf, dst); - if (!end) { return STRING_ERROR; } - std::string_view result(reinterpret_cast(dst), end-dst); - dst = end; - return result; -} - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::parser &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson -/* end file include/simdjson/generic/ondemand/parser-inl.h */ -/* begin file include/simdjson/generic/ondemand/document_stream-inl.h */ -#include -#include -#include -namespace simdjson { -namespace SIMDJSON_BUILTIN_IMPLEMENTATION { -namespace ondemand { - -#ifdef SIMDJSON_THREADS_ENABLED - -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} - -#endif // SIMDJSON_THREADS_ENABLED - -simdjson_inline document_stream::document_stream( - ondemand::parser &_parser, - const uint8_t *_buf, - size_t _len, - size_t _batch_size -) noexcept - : parser{&_parser}, - buf{_buf}, - len{_len}, - batch_size{_batch_size <= MINIMAL_BATCH_SIZE ? MINIMAL_BATCH_SIZE : _batch_size}, - error{SUCCESS} - #ifdef SIMDJSON_THREADS_ENABLED - , use_thread(_parser.threaded) // we need to make a copy because _parser.threaded can change - #endif -{ -#ifdef SIMDJSON_THREADS_ENABLED - if(worker.get() == nullptr) { - error = MEMALLOC; - } -#endif -} - -simdjson_inline document_stream::document_stream() noexcept - : parser{nullptr}, - buf{nullptr}, - len{0}, - batch_size{0}, - error{UNINITIALIZED} - #ifdef SIMDJSON_THREADS_ENABLED - , use_thread(false) - #endif -{ -} - -simdjson_inline document_stream::~document_stream() noexcept -{ - #ifdef SIMDJSON_THREADS_ENABLED - worker.reset(); - #endif -} - -inline size_t document_stream::size_in_bytes() const noexcept { - return len; -} - -inline size_t document_stream::truncated_bytes() const noexcept { - if(error == CAPACITY) { return len - batch_start; } - return parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] - parser->implementation->structural_indexes[parser->implementation->n_structural_indexes + 1]; -} - -simdjson_inline document_stream::iterator::iterator() noexcept - : stream{nullptr}, finished{true} { -} - -simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bool is_end) noexcept - : stream{_stream}, finished{is_end} { -} - -simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } - return simdjson_result(stream->doc, stream->error); -} - -simdjson_inline document_stream::iterator& document_stream::iterator::operator++() noexcept { - // If there is an error, then we want the iterator - // to be finished, no matter what. (E.g., we do not - // keep generating documents with errors, or go beyond - // a document with errors.) - // - // Users do not have to call "operator*()" when they use operator++, - // so we need to end the stream in the operator++ function. - // - // Note that setting finished = true is essential otherwise - // we would enter an infinite loop. - if (stream->error) { finished = true; } - // Note that stream->error() is guarded against error conditions - // (it will immediately return if stream->error casts to false). - // In effect, this next function does nothing when (stream->error) - // is true (hence the risk of an infinite loop). - stream->next(); - // If that was the last document, we're finished. - // It is the only type of error we do not want to appear - // in operator*. - if (stream->error == EMPTY) { finished = true; } - // If we had any other kind of error (not EMPTY) then we want - // to pass it along to the operator* and we cannot mark the result - // as "finished" just yet. - return *this; -} - -simdjson_inline bool document_stream::iterator::operator!=(const document_stream::iterator &other) const noexcept { - return finished != other.finished; -} - -simdjson_inline document_stream::iterator document_stream::begin() noexcept { - start(); - // If there are no documents, we're finished. - return iterator(this, error == EMPTY); -} - -simdjson_inline document_stream::iterator document_stream::end() noexcept { - return iterator(this, true); -} - -inline void document_stream::start() noexcept { - if (error) { return; } - error = parser->allocate(batch_size); - if (error) { return; } - // Always run the first stage 1 parse immediately - batch_start = 0; - error = run_stage1(*parser, batch_start); - while(error == EMPTY) { - // In exceptional cases, we may start with an empty block - batch_start = next_batch_start(); - if (batch_start >= len) { return; } - error = run_stage1(*parser, batch_start); - } - if (error) { return; } - doc_index = batch_start; - doc = document(json_iterator(&buf[batch_start], parser)); - doc.iter._streaming = true; - - #ifdef SIMDJSON_THREADS_ENABLED - if (use_thread && next_batch_start() < len) { - // Kick off the first thread on next batch if needed - error = stage1_thread_parser.allocate(batch_size); - if (error) { return; } - worker->start_thread(); - start_stage1_thread(); - if (error) { return; } - } - #endif // SIMDJSON_THREADS_ENABLED -} - -inline void document_stream::next() noexcept { - // We always enter at once once in an error condition. - if (error) { return; } - next_document(); - if (error) { return; } - auto cur_struct_index = doc.iter._root - parser->implementation->structural_indexes.get(); - doc_index = batch_start + parser->implementation->structural_indexes[cur_struct_index]; - - // Check if at end of structural indexes (i.e. at end of batch) - if(cur_struct_index >= static_cast(parser->implementation->n_structural_indexes)) { - error = EMPTY; - // Load another batch (if available) - while (error == EMPTY) { - batch_start = next_batch_start(); - if (batch_start >= len) { break; } - #ifdef SIMDJSON_THREADS_ENABLED - if(use_thread) { - load_from_stage1_thread(); - } else { - error = run_stage1(*parser, batch_start); - } - #else - error = run_stage1(*parser, batch_start); - #endif - /** - * Whenever we move to another window, we need to update all pointers to make - * it appear as if the input buffer started at the beginning of the window. - * - * Take this input: - * - * {"z":5} {"1":1,"2":2,"4":4} [7, 10, 9] [15, 11, 12, 13] [154, 110, 112, 1311] - * - * Say you process the following window... - * - * '{"z":5} {"1":1,"2":2,"4":4} [7, 10, 9]' - * - * When you do so, the json_iterator has a pointer at the beginning of the memory region - * (pointing at the beginning of '{"z"...'. - * - * When you move to the window that starts at... - * - * '[7, 10, 9] [15, 11, 12, 13] ... - * - * then it is not sufficient to just run stage 1. You also need to re-anchor the - * json_iterator so that it believes we are starting at '[7, 10, 9]...'. - * - * Under the DOM front-end, this gets done automatically because the parser owns - * the pointer the data, and when you call stage1 and then stage2 on the same - * parser, then stage2 will run on the pointer acquired by stage1. - * - * That is, stage1 calls "this->buf = _buf" so the parser remembers the buffer that - * we used. But json_iterator has no callback when stage1 is called on the parser. - * In fact, I think that the parser is unaware of json_iterator. - * - * - * So we need to re-anchor the json_iterator after each call to stage 1 so that - * all of the pointers are in sync. - */ - doc.iter = json_iterator(&buf[batch_start], parser); - doc.iter._streaming = true; - /** - * End of resync. - */ - - if (error) { continue; } // If the error was EMPTY, we may want to load another batch. - doc_index = batch_start; - } - } -} - -inline void document_stream::next_document() noexcept { - // Go to next place where depth=0 (document depth) - error = doc.iter.skip_child(0); - if (error) { return; } - // Always set depth=1 at the start of document - doc.iter._depth = 1; - // Resets the string buffer at the beginning, thus invalidating the strings. - doc.iter._string_buf_loc = parser->string_buf.get(); - doc.iter._root = doc.iter.position(); -} - -inline size_t document_stream::next_batch_start() const noexcept { - return batch_start + parser->implementation->structural_indexes[parser->implementation->n_structural_indexes]; -} - -inline error_code document_stream::run_stage1(ondemand::parser &p, size_t _batch_start) noexcept { - // This code only updates the structural index in the parser, it does not update any json_iterator - // instance. - size_t remaining = len - _batch_start; - if (remaining <= batch_size) { - return p.implementation->stage1(&buf[_batch_start], remaining, stage1_mode::streaming_final); - } else { - return p.implementation->stage1(&buf[_batch_start], batch_size, stage1_mode::streaming_partial); - } -} - -simdjson_inline size_t document_stream::iterator::current_index() const noexcept { - return stream->doc_index; -} - -simdjson_inline std::string_view document_stream::iterator::source() const noexcept { - auto depth = stream->doc.iter.depth(); - auto cur_struct_index = stream->doc.iter._root - stream->parser->implementation->structural_indexes.get(); - - // If at root, process the first token to determine if scalar value - if (stream->doc.iter.at_root()) { - switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) { - case '{': case '[': // Depth=1 already at start of document - break; - case '}': case ']': - depth--; - break; - default: // Scalar value document - // TODO: Remove any trailing whitespaces - // This returns a string spanning from start of value to the beginning of the next document (excluded) - return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[++cur_struct_index] - current_index() - 1); - } - cur_struct_index++; - } - - while (cur_struct_index <= static_cast(stream->parser->implementation->n_structural_indexes)) { - switch (stream->buf[stream->batch_start + stream->parser->implementation->structural_indexes[cur_struct_index]]) { - case '{': case '[': - depth++; - break; - case '}': case ']': - depth--; - break; - } - if (depth == 0) { break; } - cur_struct_index++; - } - - return std::string_view(reinterpret_cast(stream->buf) + current_index(), stream->parser->implementation->structural_indexes[cur_struct_index] - current_index() + stream->batch_start + 1);; -} - -inline error_code document_stream::iterator::error() const noexcept { - return stream->error; -} - -#ifdef SIMDJSON_THREADS_ENABLED - -inline void document_stream::load_from_stage1_thread() noexcept { - worker->finish(); - // Swap to the parser that was loaded up in the thread. Make sure the parser has - // enough memory to swap to, as well. - std::swap(stage1_thread_parser,*parser); - error = stage1_thread_error; - if (error) { return; } - - // If there's anything left, start the stage 1 thread! - if (next_batch_start() < len) { - start_stage1_thread(); - } -} - -inline void document_stream::start_stage1_thread() noexcept { - // we call the thread on a lambda that will update - // this->stage1_thread_error - // there is only one thread that may write to this value - // TODO this is NOT exception-safe. - this->stage1_thread_error = UNINITIALIZED; // In case something goes wrong, make sure it's an error - size_t _next_batch_start = this->next_batch_start(); - - worker->run(this, & this->stage1_thread_parser, _next_batch_start); -} - -#endif // SIMDJSON_THREADS_ENABLED - -} // namespace ondemand -} // namespace SIMDJSON_BUILTIN_IMPLEMENTATION -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result::simdjson_result( - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_stream &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} - -} -/* end file include/simdjson/generic/ondemand/document_stream-inl.h */ -/* begin file include/simdjson/generic/ondemand/serialization-inl.h */ - - -namespace simdjson { - -inline std::string_view trim(const std::string_view str) noexcept { - // We can almost surely do better by rolling our own find_first_not_of function. - size_t first = str.find_first_not_of(" \t\n\r"); - // If we have the empty string (just white space), then no trimming is possible, and - // we return the empty string_view. - if (std::string_view::npos == first) { return std::string_view(); } - size_t last = str.find_last_not_of(" \t\n\r"); - return str.substr(first, (last - first + 1)); -} - - -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); -} - -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_reference& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); -} - -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value& x) noexcept { - /** - * If we somehow receive a value that has already been consumed, - * then the following code could be in trouble. E.g., we create - * an array as needed, but if an array was already created, then - * it could be bad. - */ - using namespace SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand; - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::json_type t; - auto error = x.type().get(t); - if(error != SUCCESS) { return error; } - switch (t) - { - case json_type::array: - { - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array array; - error = x.get_array().get(array); - if(error) { return error; } - return to_json_string(array); - } - case json_type::object: - { - SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object object; - error = x.get_object().get(object); - if(error) { return error; } - return to_json_string(object); - } - default: - return trim(x.raw_json_token()); - } -} - -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); -} - -inline simdjson_result to_json_string(SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} -} // namespace simdjson - -namespace simdjson { namespace SIMDJSON_BUILTIN_IMPLEMENTATION { namespace ondemand { - -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value x) { - std::string_view v; - auto error = simdjson::to_json_string(x).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::value x) { - std::string_view v; - auto error = simdjson::to_json_string(x).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - return (out << error); - } -} -#endif - -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::array value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - return (out << error); - } -} -#endif - -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document& value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document_reference& value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::document& value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - return (out << error); - } -} -#endif - -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand::object value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - return (out << error); - } -} -#endif -}}} // namespace simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand -/* end file include/simdjson/generic/ondemand/serialization-inl.h */ -/* end file include/simdjson/generic/ondemand-inl.h */ - - -namespace simdjson { - /** - * Represents the best statically linked simdjson implementation that can be used by the compiling - * program. - * - * Detects what options the program is compiled against, and picks the minimum implementation that - * will work on any computer that can run the program. For example, if you compile with g++ - * -march=westmere, it will pick the westmere implementation. The haswell implementation will - * still be available, and can be selected at runtime, but the builtin implementation (and any - * code that uses it) will use westmere. - */ - namespace builtin = SIMDJSON_BUILTIN_IMPLEMENTATION; - /** - * @copydoc simdjson::SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand - */ - namespace ondemand = SIMDJSON_BUILTIN_IMPLEMENTATION::ondemand; - /** - * Function which returns a pointer to an implementation matching the "builtin" implementation. - * The builtin implementation is the best statically linked simdjson implementation that can be used by the compiling - * program. If you compile with g++ -march=haswell, this will return the haswell implementation. - * It is handy to be able to check what builtin was used: builtin_implementation()->name(). - */ - const implementation * builtin_implementation(); -} // namespace simdjson - -#endif // SIMDJSON_BUILTIN_H -/* end file include/simdjson/builtin.h */ - -#endif // SIMDJSON_H -/* end file include/simdjson.h */ diff --git a/csrc/wrapper.cpp b/csrc/wrapper.cpp deleted file mode 100644 index d45f1e4..0000000 --- a/csrc/wrapper.cpp +++ /dev/null @@ -1,431 +0,0 @@ -#include "simdjson-rust/csrc/wrapper.h" -#include "simdjson-rust/src/libsimdjson.rs.h" -#include - -namespace simdjson -{ - namespace ffi - { - // using simdjson::dom::element; - // using simdjson::dom::parser; - - std::unique_ptr parser_new(size_t max_capacity) - { - return std::make_unique(max_capacity); - } - - ElementResult parser_load(parser &p, rust::Str path) - { - element value; - error_code error; - const std::string &cpath = std::string(path); - p.load(cpath).tie(value, error); - - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - ElementResult parser_parse(parser &p, rust::Str s) - { - // element value; - auto value = std::make_unique(); - error_code error; - const std::string &cs = std::string(s); - p.parse(cs).tie(*value, error); - return ElementResult{ - // .value = std::make_unique(value), - std::move(value), - int(error), - }; - } - - ElementResult parser_parse_padded(parser &p, const padded_string &s) - { - element value; - error_code error; - p.parse(s).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - std::unique_ptr padded_string_from_string(rust::Str s) - { - const std::string &cs = std::string(s); - return std::make_unique(cs); - } - - // // uint8_t tape_ref_type(const tape_ref &tr) - // // { - // // auto tape_type = tr.tape_ref_type(); - // // return static_cast(tape_type); - // // } - - // // uint64_t tape_ref_next_tape_value(const tape_ref &tr) - // // { - // // auto value = tr.next_tape_value(); - // // return value; - // // } - - StringResult element_get_string(const element &elm) - { - const char *value; - error_code error; - elm.get().tie(value, error); - - return StringResult{ - rust::String(value), - int(error), - }; - } - - ArrayResult element_get_array(const element &elm) - { - array value; - error_code error; - elm.get().tie(value, error); - return ArrayResult{ - std::make_unique(value), - int(error), - }; - } - - ObjectResult element_get_object(const element &elm) - { - object value; - error_code error; - elm.get().tie(value, error); - return ObjectResult{ - std::make_unique(value), - int(error), - }; - } - - // NumberResult element_get_number(const element &elm) - // { - // uint64_t value; - // error_code error; - // elm.get().tie(value, error); - // return NumberResult{ - // .value = value, - // .code = int(error), - // }; - // } - - U64Result element_get_u64(const element &elm) - { - uint64_t value; - error_code error; - elm.get().tie(value, error); - return U64Result{ - value, - int(error), - }; - } - - I64Result element_get_i64(const element &elm) - { - int64_t value; - error_code error; - elm.get().tie(value, error); - return I64Result{ - value, - int(error), - }; - } - - F64Result element_get_f64(const element &elm) - { - double value; - error_code error; - elm.get().tie(value, error); - return F64Result{ - value, - int(error), - }; - } - - BoolResult element_get_bool(const element &elm) - { - bool value; - error_code error; - elm.get().tie(value, error); - return BoolResult{ - value, - int(error), - }; - } - - bool element_is_null(const element &elm) - { - return elm.is_null(); - } - - ElementResult element_at_pointer(const element &elm, rust::Str s) - { - element value; - error_code error; - auto json_pointer = std::string_view(s.data(), s.size()); - elm.at_pointer(json_pointer).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - ElementResult element_at_index(const element &elm, size_t index) - { - element value; - error_code error; - elm.at(index).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - ElementResult element_at_key(const element &elm, rust::Str s) - { - element value; - error_code error; - auto key = std::string_view(s.data(), s.size()); - elm.at_key(key).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - uint8_t element_get_type(const element &elm) - { - return (uint8_t)elm.type(); - } - - ElementResult array_at_pointer(const array &arr, rust::Str s) - { - element value; - error_code error; - auto json_pointer = std::string_view(s.data(), s.size()); - arr.at_pointer(json_pointer).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - ElementResult array_at(const array &arr, size_t index) - { - element value; - error_code error; - arr.at(index).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - ElementResult object_at_pointer(const object &obj, rust::Str s) - { - element value; - error_code error; - auto json_pointer = std::string_view(s.data(), s.size()); - obj.at_pointer(json_pointer).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - ElementResult object_at_key(const object &obj, rust::Str s) - { - element value; - error_code error; - auto key = std::string_view(s.data(), s.size()); - obj.at_key(key).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - ElementResult object_at_key_case_insensitive(const object &obj, rust::Str s) - { - element value; - error_code error; - auto key = std::string_view(s.data(), s.size()); - obj.at_key_case_insensitive(key).tie(value, error); - return ElementResult{ - std::make_unique(value), - int(error), - }; - } - - std::unique_ptr array_get_iterator(const array &arr) - { - auto iter = ArrayIterator{ - arr.begin(), - arr.end(), - }; - return std::make_unique(iter); - } - - std::unique_ptr array_iterator_next(ArrayIterator &iter) - { - // if (iter.begin != iter.end) - // { - // element elm = **(iter.begin); - // ++(*(iter.begin)); - // return std::make_unique(elm); - // } - // else - // { - // return nullptr; - // } - if (iter.begin != iter.end) - { - element elm = *iter.begin; - ++(iter.begin); - return std::make_unique(elm); - } - else - { - return nullptr; - } - } - - std::unique_ptr object_get_iterator(const object &obj) - { - auto iter = ObjectIterator{ - obj.begin(), - obj.end(), - }; - return std::make_unique(iter); - } - - void object_iterator_next(ObjectIterator &iter) - { - // if (iter.begin != iter.end) - // { - // key_value_pair kvp = *iter.begin; - // // auto out = KeyValuePair{ - // // .key = rust::String(kvp.key.data()), - // // .value = std::make_unique(kvp.value), - // // }; - // ++(iter.begin); - // // return out; - // } - // else - // { - - // } - ++(iter.begin); - } - - bool object_iterator_has_next(const ObjectIterator &iter) - { - return iter.begin != iter.end; - } - - rust::String object_iterator_key(const ObjectIterator &iter) - { - return rust::String(iter.begin.key().data()); - } - std::unique_ptr object_iterator_value(const ObjectIterator &iter) - { - return std::make_unique(iter.begin.value()); - } - - rust::String element_minify(const element &elm) - { - auto s = std::string(minify(elm)); - return rust::String(s); - } - - rust::String object_minify(const object &obj) - { - auto s = std::string(minify(obj)); - return rust::String(s); - } - - rust::String array_minify(const array &arr) - { - auto s = std::string(minify(arr)); - return rust::String(s); - } - - DocumentStreamResult parser_load_many(parser &p, rust::Str path, size_t batch_size) - { - auto cpath = std::string(path); - auto stream = std::make_unique(); - auto error = p.load_many(cpath, batch_size).get(*stream); - return DocumentStreamResult{ - error ? nullptr : std::move(stream), - int(error), - }; - } - - DocumentStreamResult parser_parse_many(parser &p, rust::Str s, size_t batch_size) - { - auto stream = std::make_unique(); - auto error = p.parse_many(s.data(), s.length(), batch_size).get(*stream); - - return DocumentStreamResult{ - error ? nullptr : std::move(stream), - int(error), - }; - } - - DocumentStreamResult parser_parse_many_padded(parser &p, const padded_string &s, size_t batch_size) - { - auto stream = std::make_unique(); - auto error = p.parse_many(s, batch_size).get(*stream); - - return DocumentStreamResult{ - error ? nullptr : std::move(stream), - int(error), - }; - } - - std::unique_ptr document_stream_get_iterator(document_stream &stream) - { - auto iter = DocumentStreamIterator{ - stream.begin(), - stream.end(), - }; - return std::make_unique(iter); - } - - ElementResult document_stream_iterator_deref(DocumentStreamIterator &iter) - { - if (iter.begin != iter.end) - { - element value; - error_code error; - (*iter.begin).tie(value, error); - return ElementResult{ - error ? nullptr : std::make_unique(value), - int(error), - }; - } - else - { - return ElementResult{ - nullptr, - 0, - }; - } - } - - void document_stream_iterator_next(DocumentStreamIterator &iter) - { - if (iter.begin != iter.end) - { - ++iter.begin; - } - } - - } // namespace ffi -} // namespace simdjson diff --git a/csrc/wrapper.h b/csrc/wrapper.h deleted file mode 100644 index 35dd1d9..0000000 --- a/csrc/wrapper.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include "rust/cxx.h" -#include "simdjson.h" - -namespace simdjson -{ - namespace ffi - { - - using simdjson::padded_string; - // // using simdjson::simdjson_result; - using simdjson::error_code; - // using simdjson::dom::array; - // using simdjson::dom::element; - // using simdjson::dom::object; - // using simdjson::dom::parser; - // using simdjson::internal::tape_ref; - - using namespace simdjson::dom; - - using array_iterator = simdjson::dom::array::iterator; - using object_iterator = simdjson::dom::object::iterator; - using document_stream_iterator = simdjson::dom::document_stream::iterator; - - // using simdjson::dom::parser; - // using simdjson::dom::element; - - struct ElementResult; - struct StringResult; - struct ArrayResult; - struct ObjectResult; - // struct NumberResult; - struct BoolResult; - // struct ArrayIterator; - // struct ObjectIterator; - struct KeyValuePair; - struct U64Result; - struct I64Result; - struct F64Result; - struct DocumentStreamResult; - - struct ArrayIterator - { - array_iterator begin; - array_iterator end; - }; - - struct ObjectIterator - { - object_iterator begin; - object_iterator end; - }; - - struct DocumentStreamIterator - { - document_stream_iterator begin; - document_stream_iterator end; - }; - - std::unique_ptr parser_new(size_t max_capacity); - ElementResult parser_load(parser &p, rust::Str path); - ElementResult parser_parse(parser &p, rust::Str s); - ElementResult parser_parse_padded(parser &p, const padded_string &s); - - std::unique_ptr padded_string_from_string(rust::Str s); - - // // uint8_t tape_ref_type(const tape_ref &tr); - - // // uint64_t tape_ref_next_tape_value(const tape_ref &tr); - - StringResult element_get_string(const element &elm); - ArrayResult element_get_array(const element &elm); - ObjectResult element_get_object(const element &elm); - U64Result element_get_u64(const element &elm); - I64Result element_get_i64(const element &elm); - F64Result element_get_f64(const element &elm); - BoolResult element_get_bool(const element &elm); - bool element_is_null(const element &elm); - ElementResult element_at_pointer(const element &elm, rust::Str s); - ElementResult element_at_index(const element &elm, size_t index); - ElementResult element_at_key(const element &elm, rust::Str s); - uint8_t element_get_type(const element &elm); - - ElementResult array_at_pointer(const array &arr, rust::Str s); - ElementResult array_at(const array &arr, size_t index); - - ElementResult object_at_pointer(const object &obj, rust::Str s); - ElementResult object_at_key(const object &obj, rust::Str s); - ElementResult object_at_key_case_insensitive(const object &obj, rust::Str s); - - std::unique_ptr array_get_iterator(const array &arr); - std::unique_ptr array_iterator_next(ArrayIterator &iter); - - std::unique_ptr object_get_iterator(const object &obj); - void object_iterator_next(ObjectIterator &iter); - bool object_iterator_has_next(const ObjectIterator &iter); - rust::String object_iterator_key(const ObjectIterator &iter); - std::unique_ptr object_iterator_value(const ObjectIterator &iter); - - // For display - rust::String element_minify(const element &elm); - rust::String object_minify(const object &obj); - rust::String array_minify(const array &arr); - - DocumentStreamResult parser_load_many(parser &p, rust::Str path, size_t batch_size); - DocumentStreamResult parser_parse_many(parser &p, rust::Str s, size_t batch_size); - DocumentStreamResult parser_parse_many_padded(parser &p, const padded_string &s, size_t batch_size); - - // For load many and parse many - std::unique_ptr document_stream_get_iterator(document_stream &stream); - ElementResult document_stream_iterator_deref(DocumentStreamIterator &iter); - void document_stream_iterator_next(DocumentStreamIterator &iter); - - } // namespace ffi -} // namespace simdjson diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..1cf48c5 --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,2 @@ +pub const SIMDJSON_PADDING: usize = 64; +pub const SIMDJSON_MAXSIZE_BYTES: usize = 0xFFFFFFFF; diff --git a/src/lib.rs b/src/lib.rs index 473b147..5afaef9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,9 +11,10 @@ mod macros; +pub mod constants; // pub mod dom; pub mod error; - +pub mod ondemand; // pub mod libsimdjson; pub mod padded_string; // pub mod serde; diff --git a/src/macros.rs b/src/macros.rs index 0da7941..a05a3f7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,4 +1,13 @@ macro_rules! impl_drop { + ($name:ident < $($lt:lifetime),+ > , $free_fn:expr) => { + impl<$($lt),+> Drop for $name<$($lt),+> { + fn drop(&mut self) { + unsafe { + $free_fn(self.ptr.as_ptr()); + } + } + } + }; ($name:ty, $free_fn:expr) => { impl Drop for $name { fn drop(&mut self) { @@ -11,6 +20,17 @@ macro_rules! impl_drop { } macro_rules! map_result { + (primitive, $func_call:expr, $get_code:expr, $get_value:expr) => { + unsafe { + let ptr = $func_call; + let code = $get_code(ptr); + if code == 0 { + Ok($get_value(ptr)) + } else { + Err(crate::error::SimdJsonError::from(code)) + } + } + }; ($func_call:expr, $get_code:expr, $get_value:expr) => { unsafe { let ptr = $func_call; diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs new file mode 100644 index 0000000..ab33928 --- /dev/null +++ b/src/ondemand/array.rs @@ -0,0 +1,24 @@ +use simdjson_sys as ffi; +use std::{marker::PhantomData, ptr::NonNull}; + +use crate::macros::impl_drop; + +use super::parser::Parser; + +pub struct Array<'p, 's> { + ptr: NonNull, + _parser: PhantomData<&'p mut Parser>, + _padded_string: PhantomData<&'s String>, +} + +impl<'p, 's> Array<'p, 's> { + pub fn new(ptr: NonNull) -> Self { + Self { + ptr, + _parser: PhantomData, + _padded_string: PhantomData, + } + } +} + +impl_drop!(Array<'p, 's>, ffi::SJ_OD_array_free); diff --git a/src/ondemand/document.rs b/src/ondemand/document.rs new file mode 100644 index 0000000..5f77c4f --- /dev/null +++ b/src/ondemand/document.rs @@ -0,0 +1,90 @@ +use std::{marker::PhantomData, ptr::NonNull}; + +use simdjson_sys as ffi; + +use crate::{ + error::Result, + macros::{impl_drop, map_result}, +}; + +use super::{array::Array, object::Object, parser::Parser, value::Value}; + +pub struct Document<'p, 's> { + ptr: NonNull, + _parser: PhantomData<&'p mut Parser>, + _padded_string: PhantomData<&'s String>, +} +impl<'p, 's> Document<'p, 's> { + pub fn new(ptr: NonNull) -> Self { + Self { + ptr, + _parser: PhantomData, + _padded_string: PhantomData, + } + } + + pub fn get_uint64(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_document_get_uint64(self.ptr.as_mut()), + ffi::uint64_t_result_error, + ffi::uint64_t_result_value_unsafe + ) + } + + pub fn get_int64(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_document_get_int64(self.ptr.as_mut()), + ffi::int64_t_result_error, + ffi::int64_t_result_value_unsafe + ) + } + + pub fn get_bool(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_document_get_bool(self.ptr.as_mut()), + ffi::bool_result_error, + ffi::bool_result_value_unsafe + ) + } + + pub fn get_double(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_document_get_double(self.ptr.as_mut()), + ffi::double_result_error, + ffi::double_result_value_unsafe + ) + } + + pub fn get_value(&mut self) -> Result> { + map_result!( + ffi::SJ_OD_document_get_value(self.ptr.as_mut()), + ffi::SJ_OD_value_result_error, + ffi::SJ_OD_value_result_value_unsafe + ) + .map(Value::new) + } + + pub fn get_array(&mut self) -> Result> { + map_result!( + ffi::SJ_OD_document_get_array(self.ptr.as_mut()), + ffi::SJ_OD_array_result_error, + ffi::SJ_OD_array_result_value_unsafe + ) + .map(Array::new) + } + + pub fn get_object(&mut self) -> Result> { + map_result!( + ffi::SJ_OD_document_get_object(self.ptr.as_mut()), + ffi::SJ_OD_object_result_error, + ffi::SJ_OD_object_result_value_unsafe + ) + .map(Object::new) + } +} + +impl_drop!(Document<'p, 's>, ffi::SJ_OD_document_free); diff --git a/src/ondemand/field.rs b/src/ondemand/field.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/ondemand/mod.rs b/src/ondemand/mod.rs new file mode 100644 index 0000000..f139cce --- /dev/null +++ b/src/ondemand/mod.rs @@ -0,0 +1,6 @@ +pub mod array; +pub mod document; +pub mod field; +pub mod object; +pub mod parser; +pub mod value; diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs new file mode 100644 index 0000000..76e8c32 --- /dev/null +++ b/src/ondemand/object.rs @@ -0,0 +1,24 @@ +use simdjson_sys as ffi; +use std::{marker::PhantomData, ptr::NonNull}; + +use crate::macros::impl_drop; + +use super::parser::Parser; + +pub struct Object<'p, 's> { + ptr: NonNull, + _parser: PhantomData<&'p mut Parser>, + _padded_string: PhantomData<&'s String>, +} + +impl<'p, 's> Object<'p, 's> { + pub fn new(ptr: NonNull) -> Self { + Self { + ptr, + _parser: PhantomData, + _padded_string: PhantomData, + } + } +} + +impl_drop!(Object<'p, 's>, ffi::SJ_OD_object_free); diff --git a/src/ondemand/parser.rs b/src/ondemand/parser.rs new file mode 100644 index 0000000..2e3a948 --- /dev/null +++ b/src/ondemand/parser.rs @@ -0,0 +1,66 @@ +use std::{marker::PhantomData, ptr::NonNull}; + +use simdjson_sys as ffi; + +use crate::{ + constants::SIMDJSON_MAXSIZE_BYTES, + error::Result, + macros::{impl_drop, map_result}, +}; + +use super::document::Document; + +pub struct Parser { + ptr: NonNull, +} + +impl Default for Parser { + fn default() -> Self { + Parser::new(SIMDJSON_MAXSIZE_BYTES) + } +} + +impl Parser { + pub fn new(max_capacity: usize) -> Self { + let ptr = unsafe { NonNull::new_unchecked(ffi::SJ_OD_parser_new(max_capacity)) }; + Self { ptr } + } + + pub fn iterate<'p, 's>(&'p mut self, padded_string: &'s String) -> Result> { + map_result!( + ffi::SJ_OD_parser_iterate_padded_string_view( + self.ptr.as_mut(), + padded_string.as_ptr().cast(), + padded_string.len(), + padded_string.capacity() + ), + ffi::SJ_OD_document_result_error, + ffi::SJ_OD_document_result_value_unsafe + ) + .map(Document::new) + } +} + +impl_drop!(Parser, ffi::SJ_OD_parser_free); + +#[cfg(test)] +mod tests { + use crate::padded_string::make_padded_string; + + use super::*; + + #[test] + fn test_new() { + let mut parser = Parser::default(); + let ps = make_padded_string("[1,2,3]"); + let mut doc = parser.iterate(&ps).unwrap(); + doc.get_array().unwrap(); + drop(doc); + let ps2 = make_padded_string("1"); + let mut doc2 = parser.iterate(&ps2).unwrap(); + let v = doc2.get_int64().unwrap(); + assert_eq!(v, 1); + let v = doc2.get_uint64().unwrap(); + assert_eq!(v, 1); + } +} diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs new file mode 100644 index 0000000..3744de0 --- /dev/null +++ b/src/ondemand/value.rs @@ -0,0 +1,24 @@ +use simdjson_sys as ffi; +use std::{marker::PhantomData, ptr::NonNull}; + +use crate::macros::impl_drop; + +use super::parser::Parser; + +pub struct Value<'p, 's> { + ptr: NonNull, + _parser: PhantomData<&'p mut Parser>, + _padded_string: PhantomData<&'s String>, +} + +impl<'p, 's> Value<'p, 's> { + pub fn new(ptr: NonNull) -> Self { + Self { + ptr, + _parser: PhantomData, + _padded_string: PhantomData, + } + } +} + +impl_drop!(Value<'p, 's>, ffi::SJ_OD_value_free); diff --git a/src/padded_string.rs b/src/padded_string.rs index db7b542..2463d0f 100644 --- a/src/padded_string.rs +++ b/src/padded_string.rs @@ -3,6 +3,7 @@ use std::{os::unix::prelude::OsStrExt, path::Path, ptr::NonNull}; use simdjson_sys as ffi; use crate::{ + constants::SIMDJSON_PADDING, error::Result, macros::{impl_drop, map_result}, }; @@ -46,6 +47,12 @@ impl PaddedString { impl_drop!(PaddedString, ffi::SJ_padded_string_free); +pub fn make_padded_string(s: &str) -> String { + let mut ps = String::with_capacity(s.len() + SIMDJSON_PADDING); + ps.push_str(s); + ps +} + #[cfg(test)] mod tests { use super::*; From 54f081d01c9e7acfce85badf5bc75a349fb20a23 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 11:33:19 +0800 Subject: [PATCH 13/31] add value methods --- src/ondemand/parser.rs | 1 + src/ondemand/value.rs | 58 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/ondemand/parser.rs b/src/ondemand/parser.rs index 2e3a948..66563e3 100644 --- a/src/ondemand/parser.rs +++ b/src/ondemand/parser.rs @@ -55,6 +55,7 @@ mod tests { let ps = make_padded_string("[1,2,3]"); let mut doc = parser.iterate(&ps).unwrap(); doc.get_array().unwrap(); + // doc.get_value().unwrap(); drop(doc); let ps2 = make_padded_string("1"); let mut doc2 = parser.iterate(&ps2).unwrap(); diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs index 3744de0..c95defa 100644 --- a/src/ondemand/value.rs +++ b/src/ondemand/value.rs @@ -1,9 +1,11 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; -use crate::macros::impl_drop; +use crate::macros::{impl_drop, map_result}; use super::parser::Parser; +use super::{array::Array, object::Object}; +use crate::error::Result; pub struct Value<'p, 's> { ptr: NonNull, @@ -19,6 +21,60 @@ impl<'p, 's> Value<'p, 's> { _padded_string: PhantomData, } } + + pub fn get_uint64(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_value_get_uint64(self.ptr.as_mut()), + ffi::uint64_t_result_error, + ffi::uint64_t_result_value_unsafe + ) + } + + pub fn get_int64(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_value_get_int64(self.ptr.as_mut()), + ffi::int64_t_result_error, + ffi::int64_t_result_value_unsafe + ) + } + + pub fn get_bool(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_value_get_bool(self.ptr.as_mut()), + ffi::bool_result_error, + ffi::bool_result_value_unsafe + ) + } + + pub fn get_double(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_value_get_double(self.ptr.as_mut()), + ffi::double_result_error, + ffi::double_result_value_unsafe + ) + } + + pub fn get_array(&mut self) -> Result> { + map_result!( + ffi::SJ_OD_value_get_array(self.ptr.as_mut()), + ffi::SJ_OD_array_result_error, + ffi::SJ_OD_array_result_value_unsafe + ) + .map(Array::new) + } + + pub fn get_object(&mut self) -> Result> { + map_result!( + ffi::SJ_OD_value_get_object(self.ptr.as_mut()), + ffi::SJ_OD_object_result_error, + ffi::SJ_OD_object_result_value_unsafe + ) + .map(Object::new) + } } impl_drop!(Value<'p, 's>, ffi::SJ_OD_value_free); From 8eb36b3adfef1826d6656ccb24e969683b1e999a Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 11:42:01 +0800 Subject: [PATCH 14/31] save --- Cargo.toml | 1 - src/ondemand/array_iterator.rs | 0 src/ondemand/mod.rs | 2 ++ src/ondemand/object_iterator.rs | 0 4 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 src/ondemand/array_iterator.rs create mode 100644 src/ondemand/object_iterator.rs diff --git a/Cargo.toml b/Cargo.toml index ed479f3..9abfe95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ members = ["simdjson-sys"] [dependencies] thiserror = "1.0" -# anyhow = "1.0" simdjson-sys = { path = "simdjson-sys" } # serde compatibilty diff --git a/src/ondemand/array_iterator.rs b/src/ondemand/array_iterator.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/ondemand/mod.rs b/src/ondemand/mod.rs index f139cce..63fc86d 100644 --- a/src/ondemand/mod.rs +++ b/src/ondemand/mod.rs @@ -1,6 +1,8 @@ pub mod array; +pub mod array_iterator; pub mod document; pub mod field; pub mod object; +pub mod object_iterator; pub mod parser; pub mod value; diff --git a/src/ondemand/object_iterator.rs b/src/ondemand/object_iterator.rs new file mode 100644 index 0000000..e69de29 From af1c5d12e2a964aa70c53c07d2c1d79d52c0e6b0 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 12:04:12 +0800 Subject: [PATCH 15/31] add load_padded_string --- Cargo.toml | 1 - build.rs | 1 - examples/quickstart.rs | 6 ++++-- src/ondemand/array.rs | 23 ++++++++++++++++++++++- src/ondemand/parser.rs | 2 +- src/padded_string.rs | 10 +++++++++- 6 files changed, 36 insertions(+), 7 deletions(-) delete mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index 9abfe95..20c2240 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ name = "simdjson-rust" version = "0.3.0" authors = ["SunDoge <384813529@qq.com>"] edition = "2021" -links = "simdjson-rust" license = "Apache-2.0" diff --git a/build.rs b/build.rs deleted file mode 100644 index f328e4d..0000000 --- a/build.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() {} diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 8922c49..94dbf37 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -1,4 +1,4 @@ -use simdjson_rust::{error::Result, padded_string::PaddedString}; +use simdjson_rust::{error::Result, ondemand::parser::Parser, padded_string::load_padded_string}; fn main() -> Result<()> { // let mut parser = dom::Parser::default(); @@ -11,7 +11,9 @@ fn main() -> Result<()> { // .get_u64()? // ); - let ps = PaddedString::load("json-examples/twitter.json")?; + let ps = load_padded_string("json-examples/twitter.json").unwrap(); + let mut parser = Parser::default(); + let tweets = parser.iterate(&ps)?; Ok(()) } diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs index ab33928..c9e0ba0 100644 --- a/src/ondemand/array.rs +++ b/src/ondemand/array.rs @@ -1,7 +1,10 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; -use crate::macros::impl_drop; +use crate::{ + error::Result, + macros::{impl_drop, map_result}, +}; use super::parser::Parser; @@ -19,6 +22,24 @@ impl<'p, 's> Array<'p, 's> { _padded_string: PhantomData, } } + + pub fn count_elements(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_array_count_elements(self.ptr.as_mut()), + ffi::size_t_result_error, + ffi::size_t_result_value_unsafe + ) + } + + pub fn is_empty(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_array_is_empty(self.ptr.as_mut()), + ffi::bool_result_error, + ffi::bool_result_value_unsafe + ) + } } impl_drop!(Array<'p, 's>, ffi::SJ_OD_array_free); diff --git a/src/ondemand/parser.rs b/src/ondemand/parser.rs index 66563e3..7c890bf 100644 --- a/src/ondemand/parser.rs +++ b/src/ondemand/parser.rs @@ -1,4 +1,4 @@ -use std::{marker::PhantomData, ptr::NonNull}; +use std::ptr::NonNull; use simdjson_sys as ffi; diff --git a/src/padded_string.rs b/src/padded_string.rs index 2463d0f..0a8cf75 100644 --- a/src/padded_string.rs +++ b/src/padded_string.rs @@ -1,4 +1,4 @@ -use std::{os::unix::prelude::OsStrExt, path::Path, ptr::NonNull}; +use std::{io::Read, os::unix::prelude::OsStrExt, path::Path, ptr::NonNull}; use simdjson_sys as ffi; @@ -53,6 +53,14 @@ pub fn make_padded_string(s: &str) -> String { ps } +pub fn load_padded_string>(path: P) -> std::io::Result { + let mut file = std::fs::File::open(path)?; + let len = file.metadata()?.len() as usize; + let mut buf = String::with_capacity(len + SIMDJSON_PADDING); + file.read_to_string(&mut buf)?; + Ok(buf) +} + #[cfg(test)] mod tests { use super::*; From cf49f1b45225becddef2d382e355f6480f465dd9 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 12:29:47 +0800 Subject: [PATCH 16/31] fix lifetime --- simdjson-sys/Cargo.toml | 6 ++++++ src/ondemand/array.rs | 13 ++++++------- src/ondemand/document.rs | 6 +++--- src/ondemand/object.rs | 13 ++++++------- src/ondemand/value.rs | 17 ++++++++--------- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/simdjson-sys/Cargo.toml b/simdjson-sys/Cargo.toml index c74b0f1..b1df930 100644 --- a/simdjson-sys/Cargo.toml +++ b/simdjson-sys/Cargo.toml @@ -5,6 +5,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +include = [ + "../simdjson/singleheader/simdjson.h", + "../simdjson/singleheader/simdjson.cpp", +] + + [dependencies] [build-dependencies] diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs index c9e0ba0..4af7750 100644 --- a/src/ondemand/array.rs +++ b/src/ondemand/array.rs @@ -1,6 +1,7 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; +use super::document::Document; use crate::{ error::Result, macros::{impl_drop, map_result}, @@ -8,18 +9,16 @@ use crate::{ use super::parser::Parser; -pub struct Array<'p, 's> { +pub struct Array<'d> { ptr: NonNull, - _parser: PhantomData<&'p mut Parser>, - _padded_string: PhantomData<&'s String>, + _document: PhantomData<&'d mut Document<'d, 'd>>, } -impl<'p, 's> Array<'p, 's> { +impl<'d> Array<'d> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _parser: PhantomData, - _padded_string: PhantomData, + _document: PhantomData, } } @@ -42,4 +41,4 @@ impl<'p, 's> Array<'p, 's> { } } -impl_drop!(Array<'p, 's>, ffi::SJ_OD_array_free); +impl_drop!(Array<'d>, ffi::SJ_OD_array_free); diff --git a/src/ondemand/document.rs b/src/ondemand/document.rs index 5f77c4f..d160206 100644 --- a/src/ondemand/document.rs +++ b/src/ondemand/document.rs @@ -59,7 +59,7 @@ impl<'p, 's> Document<'p, 's> { ) } - pub fn get_value(&mut self) -> Result> { + pub fn get_value(&mut self) -> Result> { map_result!( ffi::SJ_OD_document_get_value(self.ptr.as_mut()), ffi::SJ_OD_value_result_error, @@ -68,7 +68,7 @@ impl<'p, 's> Document<'p, 's> { .map(Value::new) } - pub fn get_array(&mut self) -> Result> { + pub fn get_array(&mut self) -> Result> { map_result!( ffi::SJ_OD_document_get_array(self.ptr.as_mut()), ffi::SJ_OD_array_result_error, @@ -77,7 +77,7 @@ impl<'p, 's> Document<'p, 's> { .map(Array::new) } - pub fn get_object(&mut self) -> Result> { + pub fn get_object(&mut self) -> Result> { map_result!( ffi::SJ_OD_document_get_object(self.ptr.as_mut()), ffi::SJ_OD_object_result_error, diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index 76e8c32..67439f0 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -3,22 +3,21 @@ use std::{marker::PhantomData, ptr::NonNull}; use crate::macros::impl_drop; +use super::document::Document; use super::parser::Parser; -pub struct Object<'p, 's> { +pub struct Object<'d> { ptr: NonNull, - _parser: PhantomData<&'p mut Parser>, - _padded_string: PhantomData<&'s String>, + _document: PhantomData<&'d mut Document<'d, 'd>>, } -impl<'p, 's> Object<'p, 's> { +impl<'d> Object<'d> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _parser: PhantomData, - _padded_string: PhantomData, + _document: PhantomData, } } } -impl_drop!(Object<'p, 's>, ffi::SJ_OD_object_free); +impl_drop!(Object<'d>, ffi::SJ_OD_object_free); diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs index c95defa..281e0a5 100644 --- a/src/ondemand/value.rs +++ b/src/ondemand/value.rs @@ -3,22 +3,21 @@ use std::{marker::PhantomData, ptr::NonNull}; use crate::macros::{impl_drop, map_result}; +use super::document::Document; use super::parser::Parser; use super::{array::Array, object::Object}; use crate::error::Result; -pub struct Value<'p, 's> { +pub struct Value<'d> { ptr: NonNull, - _parser: PhantomData<&'p mut Parser>, - _padded_string: PhantomData<&'s String>, + _document: PhantomData<&'d mut Document<'d, 'd>>, } -impl<'p, 's> Value<'p, 's> { +impl<'d> Value<'d> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _parser: PhantomData, - _padded_string: PhantomData, + _document: PhantomData, } } @@ -58,7 +57,7 @@ impl<'p, 's> Value<'p, 's> { ) } - pub fn get_array(&mut self) -> Result> { + pub fn get_array(&mut self) -> Result> { map_result!( ffi::SJ_OD_value_get_array(self.ptr.as_mut()), ffi::SJ_OD_array_result_error, @@ -67,7 +66,7 @@ impl<'p, 's> Value<'p, 's> { .map(Array::new) } - pub fn get_object(&mut self) -> Result> { + pub fn get_object(&mut self) -> Result> { map_result!( ffi::SJ_OD_value_get_object(self.ptr.as_mut()), ffi::SJ_OD_object_result_error, @@ -77,4 +76,4 @@ impl<'p, 's> Value<'p, 's> { } } -impl_drop!(Value<'p, 's>, ffi::SJ_OD_value_free); +impl_drop!(Value<'d>, ffi::SJ_OD_value_free); From 1f4035b0d9753d0556337f5a6b4f5d31f26fa50b Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 13:16:56 +0800 Subject: [PATCH 17/31] save --- simdjson-sys/src/simdjson_c_api.cpp | 8 ++++++++ simdjson-sys/src/simdjson_c_api.h | 7 +++++++ src/ondemand/array.rs | 29 ++++++++++++++++++++++++++++- src/ondemand/array_iterator.rs | 23 +++++++++++++++++++++++ src/ondemand/value.rs | 1 - 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index ddde148..2f3b17e 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -158,8 +158,16 @@ size_t STD_string_view_size(STD_string_view *sv) { IMPL_GET(SJ_OD_array, ondemand::array, size_t, count_elements) IMPL_GET(SJ_OD_array, ondemand::array, bool, is_empty) IMPL_GET(SJ_OD_array, ondemand::array, bool, reset) +IMPL_GET(SJ_OD_array, ondemand::array, SJ_OD_array_iterator, begin) +IMPL_GET(SJ_OD_array, ondemand::array, SJ_OD_array_iterator, end) SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index) { auto result = reinterpret_cast(array)->at(index); return object_to_pointer(std::move(result)); } + +// ondemand::array_iterator +SJ_OD_value_result* SJ_OD_array_iterator_get(SJ_OD_array_iterator* self) { + auto ptr = reinterpret_cast(self); + return object_to_pointer(**ptr); +} diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index 5140675..2d1d963 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -106,8 +106,15 @@ size_t STD_string_view_size(STD_string_view *sv); DEFINE_GET(SJ_OD_array, size_t, count_elements) DEFINE_GET(SJ_OD_array, bool, is_empty) DEFINE_GET(SJ_OD_array, bool, reset) +DEFINE_GET(SJ_OD_array, SJ_OD_array_iterator, begin) +DEFINE_GET(SJ_OD_array, SJ_OD_array_iterator, end) SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index); +// ondemand::array_iterator +DEFINE_GET(SJ_OD_array_iterator, SJ_OD_value, get) +bool SJ_OD_array_iterator_not_equal(SJ_OD_array_iterator* rhs); +void SJ_OD_array_iterator_step(SJ_OD_array_iterator* self); + #ifdef __cplusplus } #endif diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs index 4af7750..85c822f 100644 --- a/src/ondemand/array.rs +++ b/src/ondemand/array.rs @@ -1,7 +1,7 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; -use super::document::Document; +use super::{document::Document, value::Value}; use crate::{ error::Result, macros::{impl_drop, map_result}, @@ -39,6 +39,33 @@ impl<'d> Array<'d> { ffi::bool_result_value_unsafe ) } + + pub fn at(&mut self, index: usize) -> Result { + map_result!( + ffi::SJ_OD_array_at(self.ptr.as_mut(), index), + ffi::SJ_OD_value_result_error, + ffi::SJ_OD_value_result_value_unsafe + ) + .map(Value::new) + } + + pub fn reset(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_array_reset(self.ptr.as_mut()), + ffi::bool_result_error, + ffi::bool_result_value_unsafe + ) + } + + fn begin(&mut self) { + + } + + fn end(&self) { + + } + } impl_drop!(Array<'d>, ffi::SJ_OD_array_free); diff --git a/src/ondemand/array_iterator.rs b/src/ondemand/array_iterator.rs index e69de29..3c3b3c6 100644 --- a/src/ondemand/array_iterator.rs +++ b/src/ondemand/array_iterator.rs @@ -0,0 +1,23 @@ +use simdjson_sys as ffi; +use std::{marker::PhantomData, ptr::NonNull}; + +use super::array::Array; + +pub struct ArrayIterator<'a> { + begin: NonNull, + end: NonNull, + _array: PhantomData<&'a mut Array<'a>>, +} + +impl<'a> ArrayIterator<'a> { + pub fn current(&self) {} +} + +impl<'a> Drop for ArrayIterator<'a> { + fn drop(&mut self) { + unsafe { + ffi::SJ_OD_array_iterator_free(self.begin.as_mut()); + ffi::SJ_OD_array_iterator_free(self.end.as_mut()); + } + } +} diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs index 281e0a5..dee1bfe 100644 --- a/src/ondemand/value.rs +++ b/src/ondemand/value.rs @@ -4,7 +4,6 @@ use std::{marker::PhantomData, ptr::NonNull}; use crate::macros::{impl_drop, map_result}; use super::document::Document; -use super::parser::Parser; use super::{array::Array, object::Object}; use crate::error::Result; From 224745fd3f7a49a4dda2b7de302b4987000efe42 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 15:04:47 +0800 Subject: [PATCH 18/31] fix array iterator --- examples/issue_20.rs | 26 ++++++++++ simdjson-sys/src/simdjson_c_api.cpp | 14 +++++ simdjson-sys/src/simdjson_c_api.h | 5 +- src/ondemand/array.rs | 31 ++++++----- src/ondemand/array_iterator.rs | 79 ++++++++++++++++++++++++++--- src/ondemand/document.rs | 20 ++++---- src/ondemand/object.rs | 27 +++++++--- src/ondemand/parser.rs | 2 +- src/ondemand/value.rs | 14 ++--- 9 files changed, 174 insertions(+), 44 deletions(-) create mode 100644 examples/issue_20.rs diff --git a/examples/issue_20.rs b/examples/issue_20.rs new file mode 100644 index 0000000..b83d1e6 --- /dev/null +++ b/examples/issue_20.rs @@ -0,0 +1,26 @@ +use simdjson_rust::{error::Result, ondemand::parser::Parser, padded_string::make_padded_string}; + +fn main() -> Result<()> { + 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}}]} +"#; + let mut parser = Parser::default(); + let padded_string = make_padded_string(data); + let mut doc = parser.iterate(&padded_string)?; + + let mut arr = doc.get_object()?.at_pointer("/ingredients")?.get_array()?; + + for value in arr.iter()? { + dbg!(value.is_ok()); + // let mut object = value?.get_object()?; + + // for field in object.iter()? { + // let mut field = field?; + + // let mut value = field.value(); + // let key = field.unescaped_key()?; + // println!("key: {} | value: {}", key, value.get_object()?.raw_json()?); + // } + } + + Ok(()) +} diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index 2f3b17e..aa94134 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -171,3 +171,17 @@ SJ_OD_value_result* SJ_OD_array_iterator_get(SJ_OD_array_iterator* self) { auto ptr = reinterpret_cast(self); return object_to_pointer(**ptr); } +bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator* lhs, const SJ_OD_array_iterator* rhs) { + return *reinterpret_cast(lhs) != *reinterpret_cast(rhs); +} +void SJ_OD_array_iterator_step(SJ_OD_array_iterator* self) { + auto ptr = reinterpret_cast(self); + ++(*ptr); +} + + +// ondemand::object +SJ_OD_value_result* SJ_OD_object_at_pointer(SJ_OD_object* self, const char *s, size_t len) { + auto result = reinterpret_cast(self)->at_pointer(std::string_view(s, len)); + return object_to_pointer(std::move(result)); +} diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index 2d1d963..06160d3 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -112,9 +112,12 @@ SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index); // ondemand::array_iterator DEFINE_GET(SJ_OD_array_iterator, SJ_OD_value, get) -bool SJ_OD_array_iterator_not_equal(SJ_OD_array_iterator* rhs); +bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator* lhs,const SJ_OD_array_iterator* rhs); void SJ_OD_array_iterator_step(SJ_OD_array_iterator* self); +// ondemand::object +SJ_OD_value_result* SJ_OD_object_at_pointer(SJ_OD_object* self, const char *s, size_t len); + #ifdef __cplusplus } #endif diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs index 85c822f..da9347d 100644 --- a/src/ondemand/array.rs +++ b/src/ondemand/array.rs @@ -1,7 +1,7 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; -use super::{document::Document, value::Value}; +use super::{array_iterator::ArrayIterator, document::Document, value::Value}; use crate::{ error::Result, macros::{impl_drop, map_result}, @@ -9,16 +9,16 @@ use crate::{ use super::parser::Parser; -pub struct Array<'d> { +pub struct Array { ptr: NonNull, - _document: PhantomData<&'d mut Document<'d, 'd>>, + // _document: PhantomData<&'d mut Document<'d, 'd>>, } -impl<'d> Array<'d> { +impl Array { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _document: PhantomData, + // _document: PhantomData, } } @@ -58,14 +58,19 @@ impl<'d> Array<'d> { ) } - fn begin(&mut self) { - - } - - fn end(&self) { - + pub fn iter(&mut self) -> Result { + let begin = map_result!( + ffi::SJ_OD_array_begin(self.ptr.as_mut()), + ffi::SJ_OD_array_iterator_result_error, + ffi::SJ_OD_array_iterator_result_value_unsafe + )?; + let end = map_result!( + ffi::SJ_OD_array_end(self.ptr.as_mut()), + ffi::SJ_OD_array_iterator_result_error, + ffi::SJ_OD_array_iterator_result_value_unsafe + )?; + Ok(ArrayIterator::new(begin, end)) } - } -impl_drop!(Array<'d>, ffi::SJ_OD_array_free); +impl_drop!(Array, ffi::SJ_OD_array_free); diff --git a/src/ondemand/array_iterator.rs b/src/ondemand/array_iterator.rs index 3c3b3c6..15fbac1 100644 --- a/src/ondemand/array_iterator.rs +++ b/src/ondemand/array_iterator.rs @@ -1,19 +1,49 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; -use super::array::Array; +use crate::{error::Result, macros::map_result}; -pub struct ArrayIterator<'a> { +use super::{array::Array, value::Value}; + +pub struct ArrayIterator { begin: NonNull, end: NonNull, - _array: PhantomData<&'a mut Array<'a>>, + running: bool, + // _array: PhantomData<&'a mut Array<'a>>, } -impl<'a> ArrayIterator<'a> { - pub fn current(&self) {} +impl ArrayIterator { + pub fn new( + begin: NonNull, + end: NonNull, + ) -> Self { + Self { + begin, + end, + running: false, + // _array: PhantomData, + } + } + + pub fn get(&mut self) -> Result { + map_result!( + ffi::SJ_OD_array_iterator_get(self.begin.as_mut()), + ffi::SJ_OD_value_result_error, + ffi::SJ_OD_value_result_value_unsafe + ) + .map(Value::new) + } + + pub fn not_equal(&self) -> bool { + unsafe { ffi::SJ_OD_array_iterator_not_equal(self.begin.as_ref(), self.end.as_ref()) } + } + + pub fn step(&mut self) { + unsafe { ffi::SJ_OD_array_iterator_step(self.begin.as_mut()) } + } } -impl<'a> Drop for ArrayIterator<'a> { +impl Drop for ArrayIterator { fn drop(&mut self) { unsafe { ffi::SJ_OD_array_iterator_free(self.begin.as_mut()); @@ -21,3 +51,40 @@ impl<'a> Drop for ArrayIterator<'a> { } } } + +impl Iterator for ArrayIterator { + type Item = Result; + + fn next(&mut self) -> Option { + if self.running { + self.step(); + } + + if self.not_equal() { + self.running = true; + Some(self.get()) + } else { + self.running = false; + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ondemand::parser::Parser, padded_string::make_padded_string}; + + use super::*; + + #[test] + fn test_iter() { + let mut parser = Parser::default(); + let ps = make_padded_string("[1,2,3]"); + let mut doc = parser.iterate(&ps).unwrap(); + let mut arr = doc.get_array().unwrap(); + for (v, num) in arr.iter().unwrap().zip([1u64, 2, 3]) { + let res = v.unwrap().get_uint64().unwrap(); + assert_eq!(res, num); + } + } +} diff --git a/src/ondemand/document.rs b/src/ondemand/document.rs index d160206..36f7276 100644 --- a/src/ondemand/document.rs +++ b/src/ondemand/document.rs @@ -9,17 +9,17 @@ use crate::{ use super::{array::Array, object::Object, parser::Parser, value::Value}; -pub struct Document<'p, 's> { +pub struct Document { ptr: NonNull, - _parser: PhantomData<&'p mut Parser>, - _padded_string: PhantomData<&'s String>, + // _parser: PhantomData<&'p mut Parser>, + // _padded_string: PhantomData<&'s String>, } -impl<'p, 's> Document<'p, 's> { +impl Document { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _parser: PhantomData, - _padded_string: PhantomData, + // _parser: PhantomData, + // _padded_string: PhantomData, } } @@ -59,7 +59,7 @@ impl<'p, 's> Document<'p, 's> { ) } - pub fn get_value(&mut self) -> Result> { + pub fn get_value(&mut self) -> Result { map_result!( ffi::SJ_OD_document_get_value(self.ptr.as_mut()), ffi::SJ_OD_value_result_error, @@ -68,7 +68,7 @@ impl<'p, 's> Document<'p, 's> { .map(Value::new) } - pub fn get_array(&mut self) -> Result> { + pub fn get_array(&mut self) -> Result { map_result!( ffi::SJ_OD_document_get_array(self.ptr.as_mut()), ffi::SJ_OD_array_result_error, @@ -77,7 +77,7 @@ impl<'p, 's> Document<'p, 's> { .map(Array::new) } - pub fn get_object(&mut self) -> Result> { + pub fn get_object(&mut self) -> Result { map_result!( ffi::SJ_OD_document_get_object(self.ptr.as_mut()), ffi::SJ_OD_object_result_error, @@ -87,4 +87,4 @@ impl<'p, 's> Document<'p, 's> { } } -impl_drop!(Document<'p, 's>, ffi::SJ_OD_document_free); +impl_drop!(Document, ffi::SJ_OD_document_free); diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index 67439f0..c1c2707 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -1,23 +1,38 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; -use crate::macros::impl_drop; +use crate::macros::{impl_drop, map_result}; use super::document::Document; use super::parser::Parser; +use super::value::Value; +use crate::error::Result; -pub struct Object<'d> { +pub struct Object { ptr: NonNull, - _document: PhantomData<&'d mut Document<'d, 'd>>, + // _document: PhantomData<&'d mut Document<'d, 'd>>, } -impl<'d> Object<'d> { +impl Object { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _document: PhantomData, + // _document: PhantomData, } } + + pub fn at_pointer(&mut self, json_pointer: &str) -> Result { + map_result!( + ffi::SJ_OD_object_at_pointer( + self.ptr.as_mut(), + json_pointer.as_ptr().cast(), + json_pointer.len() + ), + ffi::SJ_OD_value_result_error, + ffi::SJ_OD_value_result_value_unsafe + ) + .map(Value::new) + } } -impl_drop!(Object<'d>, ffi::SJ_OD_object_free); +impl_drop!(Object, ffi::SJ_OD_object_free); diff --git a/src/ondemand/parser.rs b/src/ondemand/parser.rs index 7c890bf..fdfa812 100644 --- a/src/ondemand/parser.rs +++ b/src/ondemand/parser.rs @@ -26,7 +26,7 @@ impl Parser { Self { ptr } } - pub fn iterate<'p, 's>(&'p mut self, padded_string: &'s String) -> Result> { + pub fn iterate(&mut self, padded_string: &String) -> Result { map_result!( ffi::SJ_OD_parser_iterate_padded_string_view( self.ptr.as_mut(), diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs index dee1bfe..de62db1 100644 --- a/src/ondemand/value.rs +++ b/src/ondemand/value.rs @@ -7,16 +7,16 @@ use super::document::Document; use super::{array::Array, object::Object}; use crate::error::Result; -pub struct Value<'d> { +pub struct Value { ptr: NonNull, - _document: PhantomData<&'d mut Document<'d, 'd>>, + // _document: PhantomData<&'d mut Document<'d, 'd>>, } -impl<'d> Value<'d> { +impl Value { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _document: PhantomData, + // _document: PhantomData, } } @@ -56,7 +56,7 @@ impl<'d> Value<'d> { ) } - pub fn get_array(&mut self) -> Result> { + pub fn get_array(&mut self) -> Result { map_result!( ffi::SJ_OD_value_get_array(self.ptr.as_mut()), ffi::SJ_OD_array_result_error, @@ -65,7 +65,7 @@ impl<'d> Value<'d> { .map(Array::new) } - pub fn get_object(&mut self) -> Result> { + pub fn get_object(&mut self) -> Result { map_result!( ffi::SJ_OD_value_get_object(self.ptr.as_mut()), ffi::SJ_OD_object_result_error, @@ -75,4 +75,4 @@ impl<'d> Value<'d> { } } -impl_drop!(Value<'d>, ffi::SJ_OD_value_free); +impl_drop!(Value, ffi::SJ_OD_value_free); From af2a42d90f5f49ee66bf11191f6dca8444bcb282 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 15:53:58 +0800 Subject: [PATCH 19/31] fix #20 --- examples/issue_20.rs | 16 +++---- simdjson-sys/src/simdjson_c_api.cpp | 31 +++++++++++++ simdjson-sys/src/simdjson_c_api.h | 13 ++++++ src/lib.rs | 1 + src/ondemand/field.rs | 57 +++++++++++++++++++++++ src/ondemand/mod.rs | 1 + src/ondemand/object.rs | 25 ++++++++++ src/ondemand/object_iterator.rs | 71 +++++++++++++++++++++++++++++ src/utils.rs | 15 ++++++ 9 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 src/utils.rs diff --git a/examples/issue_20.rs b/examples/issue_20.rs index b83d1e6..ed34151 100644 --- a/examples/issue_20.rs +++ b/examples/issue_20.rs @@ -10,16 +10,16 @@ fn main() -> Result<()> { let mut arr = doc.get_object()?.at_pointer("/ingredients")?.get_array()?; for value in arr.iter()? { - dbg!(value.is_ok()); - // let mut object = value?.get_object()?; + let mut object = value?.get_object()?; - // for field in object.iter()? { - // let mut field = field?; + for field in object.iter()? { + let mut field = field?; - // let mut value = field.value(); - // let key = field.unescaped_key()?; - // println!("key: {} | value: {}", key, value.get_object()?.raw_json()?); - // } + + let key = field.unescaped_key(false)?.to_owned(); + let mut value = field.take_value(); + println!("key: {} | value: {}", key, value.get_object()?.raw_json()?); + } } Ok(()) diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index aa94134..23d03a6 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -181,7 +181,38 @@ void SJ_OD_array_iterator_step(SJ_OD_array_iterator* self) { // ondemand::object +IMPL_GET(SJ_OD_object, ondemand::object, SJ_OD_object_iterator, begin) +IMPL_GET(SJ_OD_object, ondemand::object, SJ_OD_object_iterator, end) +IMPL_GET(SJ_OD_object, ondemand::object, STD_string_view, raw_json) SJ_OD_value_result* SJ_OD_object_at_pointer(SJ_OD_object* self, const char *s, size_t len) { auto result = reinterpret_cast(self)->at_pointer(std::string_view(s, len)); return object_to_pointer(std::move(result)); } + +// ondemand::object_iterator +SJ_OD_field_result* SJ_OD_object_iterator_get(SJ_OD_object_iterator* self) { + auto ptr = reinterpret_cast(self); + return object_to_pointer(**ptr); +} +bool SJ_OD_object_iterator_not_equal(const SJ_OD_object_iterator* lhs, const SJ_OD_object_iterator* rhs) { + return *reinterpret_cast(lhs) != *reinterpret_cast(rhs); +} +void SJ_OD_object_iterator_step(SJ_OD_object_iterator* self) { + auto ptr = reinterpret_cast(self); + ++(*ptr); +} + +// ondemand::field +STD_string_view_result* SJ_OD_field_unescaped_key(SJ_OD_field* self, bool allow_replacement) { + auto result = reinterpret_cast(self)->unescaped_key(allow_replacement); + return object_to_pointer(std::move(result)); +} +SJ_OD_value* SJ_OD_field_value(SJ_OD_field* self) { + ondemand::value& value = reinterpret_cast(self)->value(); + return reinterpret_cast(&value); +} +SJ_OD_value* SJ_OD_field_take_value(SJ_OD_field* self) { + auto field = reinterpret_cast(self); + auto value = std::move(*field).value(); + return object_to_pointer(std::move(value)); +} diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index 06160d3..bc04dd1 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -116,8 +116,21 @@ bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator* lhs,const SJ_OD_ void SJ_OD_array_iterator_step(SJ_OD_array_iterator* self); // ondemand::object +DEFINE_GET(SJ_OD_object, SJ_OD_object_iterator, begin) +DEFINE_GET(SJ_OD_object, SJ_OD_object_iterator, end) +DEFINE_GET(SJ_OD_object, STD_string_view, raw_json) SJ_OD_value_result* SJ_OD_object_at_pointer(SJ_OD_object* self, const char *s, size_t len); +// ondemand::object_iterator +DEFINE_GET(SJ_OD_object_iterator, SJ_OD_field, get) +bool SJ_OD_object_iterator_not_equal(const SJ_OD_object_iterator* lhs,const SJ_OD_object_iterator* rhs); +void SJ_OD_object_iterator_step(SJ_OD_object_iterator* self); + +// ondemand::field +STD_string_view_result* SJ_OD_field_unescaped_key(SJ_OD_field* self, bool allow_replacement); +SJ_OD_value* SJ_OD_field_value(SJ_OD_field* self); +SJ_OD_value* SJ_OD_field_take_value(SJ_OD_field* self); + #ifdef __cplusplus } #endif diff --git a/src/lib.rs b/src/lib.rs index 5afaef9..d7fe674 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ pub mod ondemand; // pub mod libsimdjson; pub mod padded_string; // pub mod serde; +pub mod utils; #[cfg(test)] mod tests { diff --git a/src/ondemand/field.rs b/src/ondemand/field.rs index e69de29..a1553c5 100644 --- a/src/ondemand/field.rs +++ b/src/ondemand/field.rs @@ -0,0 +1,57 @@ +use simdjson_sys as ffi; +use std::ptr::NonNull; + +use crate::error::Result; +use crate::macros::{impl_drop, map_result}; +use crate::utils::string_view_to_str; + +use super::value::Value; + +pub struct Field { + ptr: NonNull, +} + +impl Field { + pub fn new(ptr: NonNull) -> Self { + Self { ptr } + } + + pub fn unescaped_key(&mut self, allow_replacement: bool) -> Result<&str> { + let sv = map_result!( + ffi::SJ_OD_field_unescaped_key(self.ptr.as_mut(), allow_replacement), + ffi::STD_string_view_result_error, + ffi::STD_string_view_result_value_unsafe + )?; + // let s = unsafe { + // let s = std::slice::from_raw_parts( + // ffi::STD_string_view_data(sv.as_ptr()).cast(), + // ffi::STD_string_view_size(sv.as_ptr()), + // ); + // std::str::from_utf8_unchecked(s) + // }; + // unsafe { ffi::STD_string_view_free(sv.as_ptr()) }; + + let s = string_view_to_str(sv); + Ok(s) + } + + // Double free error. + // pub fn value(&mut self) -> Value { + // let ptr = unsafe { + // let ptr = ffi::SJ_OD_field_value(self.ptr.as_mut()); + // NonNull::new_unchecked(ptr) + // }; + // Value::new(ptr) + // } + + pub fn take_value(self) -> Value { + let ptr = unsafe { + let ptr = ffi::SJ_OD_field_take_value(self.ptr.as_ptr()); + NonNull::new_unchecked(ptr) + }; + + Value::new(ptr) + } +} + +impl_drop!(Field, ffi::SJ_OD_field_free); diff --git a/src/ondemand/mod.rs b/src/ondemand/mod.rs index 63fc86d..8250d67 100644 --- a/src/ondemand/mod.rs +++ b/src/ondemand/mod.rs @@ -5,4 +5,5 @@ pub mod field; pub mod object; pub mod object_iterator; pub mod parser; + pub mod value; diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index c1c2707..bceed18 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -2,8 +2,10 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; use crate::macros::{impl_drop, map_result}; +use crate::utils::string_view_to_str; use super::document::Document; +use super::object_iterator::ObjectIterator; use super::parser::Parser; use super::value::Value; use crate::error::Result; @@ -33,6 +35,29 @@ impl Object { ) .map(Value::new) } + + pub fn iter(&mut self) -> Result { + let begin = map_result!( + ffi::SJ_OD_object_begin(self.ptr.as_mut()), + ffi::SJ_OD_object_iterator_result_error, + ffi::SJ_OD_object_iterator_result_value_unsafe + )?; + let end = map_result!( + ffi::SJ_OD_object_end(self.ptr.as_mut()), + ffi::SJ_OD_object_iterator_result_error, + ffi::SJ_OD_object_iterator_result_value_unsafe + )?; + Ok(ObjectIterator::new(begin, end)) + } + + pub fn raw_json(&mut self) -> Result<&str> { + let sv = map_result!( + ffi::SJ_OD_object_raw_json(self.ptr.as_mut()), + ffi::STD_string_view_result_error, + ffi::STD_string_view_result_value_unsafe + )?; + Ok(string_view_to_str(sv)) + } } impl_drop!(Object, ffi::SJ_OD_object_free); diff --git a/src/ondemand/object_iterator.rs b/src/ondemand/object_iterator.rs index e69de29..9face69 100644 --- a/src/ondemand/object_iterator.rs +++ b/src/ondemand/object_iterator.rs @@ -0,0 +1,71 @@ +use simdjson_sys as ffi; +use std::{marker::PhantomData, ptr::NonNull}; + +use crate::{error::Result, macros::map_result}; + +use super::{field::Field, value::Value}; + +pub struct ObjectIterator { + begin: NonNull, + end: NonNull, + running: bool, + // _array: PhantomData<&'a mut Array<'a>>, +} + +impl ObjectIterator { + pub fn new( + begin: NonNull, + end: NonNull, + ) -> Self { + Self { + begin, + end, + running: false, + // _array: PhantomData, + } + } + + pub fn get(&mut self) -> Result { + map_result!( + ffi::SJ_OD_object_iterator_get(self.begin.as_mut()), + ffi::SJ_OD_field_result_error, + ffi::SJ_OD_field_result_value_unsafe + ) + .map(Field::new) + } + + pub fn not_equal(&self) -> bool { + unsafe { ffi::SJ_OD_object_iterator_not_equal(self.begin.as_ref(), self.end.as_ref()) } + } + + pub fn step(&mut self) { + unsafe { ffi::SJ_OD_object_iterator_step(self.begin.as_mut()) } + } +} + +impl Drop for ObjectIterator { + fn drop(&mut self) { + unsafe { + ffi::SJ_OD_object_iterator_free(self.begin.as_mut()); + ffi::SJ_OD_object_iterator_free(self.end.as_mut()); + } + } +} + +impl Iterator for ObjectIterator { + type Item = Result; + + fn next(&mut self) -> Option { + if self.running { + self.step(); + } + + if self.not_equal() { + self.running = true; + Some(self.get()) + } else { + self.running = false; + None + } + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..678f2b7 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,15 @@ +use simdjson_sys as ffi; +use std::ptr::NonNull; + + +pub fn string_view_to_str<'a>(sv: NonNull) -> &'a str { + let s = unsafe { + let s = std::slice::from_raw_parts( + ffi::STD_string_view_data(sv.as_ptr()).cast(), + ffi::STD_string_view_size(sv.as_ptr()), + ); + std::str::from_utf8_unchecked(s) + }; + unsafe { ffi::STD_string_view_free(sv.as_ptr()) }; + s +} From 0ece6d283be72f6874d8da9f60613e60d14b9eb9 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 16:59:46 +0800 Subject: [PATCH 20/31] rm submodule --- .gitmodules | 3 --- simdjson | 1 - 2 files changed, 4 deletions(-) delete mode 160000 simdjson diff --git a/.gitmodules b/.gitmodules index fa29b8a..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "simdjson"] - path = simdjson - url = https://github.com/simdjson/simdjson.git diff --git a/simdjson b/simdjson deleted file mode 160000 index bf849e3..0000000 --- a/simdjson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bf849e36191d4cf2442f4af57a794103df3e2979 From 0a00efe450a1e50500f9ea2eca629dc2db8064ae Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 17:24:51 +0800 Subject: [PATCH 21/31] update submodule --- .gitmodules | 3 +++ simdjson-sys/simdjson | 1 + 2 files changed, 4 insertions(+) create mode 160000 simdjson-sys/simdjson diff --git a/.gitmodules b/.gitmodules index e69de29..07040bf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "simdjson-sys/simdjson"] + path = simdjson-sys/simdjson + url = https://github.com/simdjson/simdjson.git diff --git a/simdjson-sys/simdjson b/simdjson-sys/simdjson new file mode 160000 index 0000000..bf849e3 --- /dev/null +++ b/simdjson-sys/simdjson @@ -0,0 +1 @@ +Subproject commit bf849e36191d4cf2442f4af57a794103df3e2979 From f6cced8f658e704b024d77e7b5011bdeb7b78c17 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 17:25:09 +0800 Subject: [PATCH 22/31] update cargo toml --- Cargo.toml | 1 + examples/quickstart.rs | 2 +- json-examples/twitter.json | 15482 ----------------------------------- simdjson-sys/Cargo.toml | 7 +- simdjson-sys/build.rs | 11 +- 5 files changed, 8 insertions(+), 15495 deletions(-) delete mode 100644 json-examples/twitter.json diff --git a/Cargo.toml b/Cargo.toml index 20c2240..bd259ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.3.0" authors = ["SunDoge <384813529@qq.com>"] edition = "2021" license = "Apache-2.0" +exclude = [".github/"] [workspace] diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 94dbf37..8363152 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -11,7 +11,7 @@ fn main() -> Result<()> { // .get_u64()? // ); - let ps = load_padded_string("json-examples/twitter.json").unwrap(); + let ps = load_padded_string("simdjson-sys/simdjson/jsonexamples/twitter.json").unwrap(); let mut parser = Parser::default(); let tweets = parser.iterate(&ps)?; diff --git a/json-examples/twitter.json b/json-examples/twitter.json deleted file mode 100644 index 71d1966..0000000 --- a/json-examples/twitter.json +++ /dev/null @@ -1,15482 +0,0 @@ -{ - "statuses": [ - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:15 +0000 2014", - "id": 505874924095815700, - "id_str": "505874924095815681", - "text": "@aym0566x \n\n名前:前田あゆみ\n第一印象:なんか怖っ!\n今の印象:とりあえずキモい。噛み合わない\n好きなところ:ぶすでキモいとこ😋✨✨\n思い出:んーーー、ありすぎ😊❤️\nLINE交換できる?:あぁ……ごめん✋\nトプ画をみて:照れますがな😘✨\n一言:お前は一生もんのダチ💖", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": 866260188, - "in_reply_to_user_id_str": "866260188", - "in_reply_to_screen_name": "aym0566x", - "user": { - "id": 1186275104, - "id_str": "1186275104", - "name": "AYUMI", - "screen_name": "ayuu0123", - "location": "", - "description": "元野球部マネージャー❤︎…最高の夏をありがとう…❤︎", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 262, - "friends_count": 252, - "listed_count": 0, - "created_at": "Sat Feb 16 13:40:25 +0000 2013", - "favourites_count": 235, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 1769, - "lang": "en", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/497760886795153410/LDjAwR_y_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/497760886795153410/LDjAwR_y_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1186275104/1409318784", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "aym0566x", - "name": "前田あゆみ", - "id": 866260188, - "id_str": "866260188", - "indices": [ - 0, - 9 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:14 +0000 2014", - "id": 505874922023837700, - "id_str": "505874922023837696", - "text": "RT @KATANA77: えっそれは・・・(一同) http://t.co/PkCJAcSuYK", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 903487807, - "id_str": "903487807", - "name": "RT&ファボ魔のむっつんさっm", - "screen_name": "yuttari1998", - "location": "関西 ↓詳しいプロ↓", - "description": "無言フォローはあまり好みません ゲームと動画が好きですシモ野郎ですがよろしく…最近はMGSとブレイブルー、音ゲーをプレイしてます", - "url": "http://t.co/Yg9e1Fl8wd", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/Yg9e1Fl8wd", - "expanded_url": "http://twpf.jp/yuttari1998", - "display_url": "twpf.jp/yuttari1998", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 95, - "friends_count": 158, - "listed_count": 1, - "created_at": "Thu Oct 25 08:27:13 +0000 2012", - "favourites_count": 3652, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 10276, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/500268849275494400/AoXHZ7Ij_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/500268849275494400/AoXHZ7Ij_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/903487807/1409062272", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sat Aug 30 23:49:35 +0000 2014", - "id": 505864943636197400, - "id_str": "505864943636197376", - "text": "えっそれは・・・(一同) http://t.co/PkCJAcSuYK", - "source": "Twitter Web Client", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 77915997, - "id_str": "77915997", - "name": "(有)刀", - "screen_name": "KATANA77", - "location": "", - "description": "プリキュア好きのサラリーマンです。好きなプリキュアシリーズはハートキャッチ、最愛のキャラクターは月影ゆりさんです。 http://t.co/QMLJeFmfMTご質問、お問い合わせはこちら http://t.co/LU8T7vmU3h", - "url": null, - "entities": { - "description": { - "urls": [ - { - "url": "http://t.co/QMLJeFmfMT", - "expanded_url": "http://www.pixiv.net/member.php?id=4776", - "display_url": "pixiv.net/member.php?id=…", - "indices": [ - 58, - 80 - ] - }, - { - "url": "http://t.co/LU8T7vmU3h", - "expanded_url": "http://ask.fm/KATANA77", - "display_url": "ask.fm/KATANA77", - "indices": [ - 95, - 117 - ] - } - ] - } - }, - "protected": false, - "followers_count": 1095, - "friends_count": 740, - "listed_count": 50, - "created_at": "Mon Sep 28 03:41:27 +0000 2009", - "favourites_count": 3741, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": true, - "verified": false, - "statuses_count": 19059, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/808597451/45b82f887085d32bd4b87dfc348fe22a.png", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/808597451/45b82f887085d32bd4b87dfc348fe22a.png", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/480210114964504577/MjVIEMS4_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/480210114964504577/MjVIEMS4_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/77915997/1404661392", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "FFFFFF", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 82, - "favorite_count": 42, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [], - "media": [ - { - "id": 505864942575034400, - "id_str": "505864942575034369", - "indices": [ - 13, - 35 - ], - "media_url": "http://pbs.twimg.com/media/BwUxfC6CIAEr-Ye.jpg", - "media_url_https": "https://pbs.twimg.com/media/BwUxfC6CIAEr-Ye.jpg", - "url": "http://t.co/PkCJAcSuYK", - "display_url": "pic.twitter.com/PkCJAcSuYK", - "expanded_url": "http://twitter.com/KATANA77/status/505864943636197376/photo/1", - "type": "photo", - "sizes": { - "medium": { - "w": 600, - "h": 338, - "resize": "fit" - }, - "small": { - "w": 340, - "h": 192, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "large": { - "w": 765, - "h": 432, - "resize": "fit" - } - } - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - "retweet_count": 82, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "KATANA77", - "name": "(有)刀", - "id": 77915997, - "id_str": "77915997", - "indices": [ - 3, - 12 - ] - } - ], - "media": [ - { - "id": 505864942575034400, - "id_str": "505864942575034369", - "indices": [ - 27, - 49 - ], - "media_url": "http://pbs.twimg.com/media/BwUxfC6CIAEr-Ye.jpg", - "media_url_https": "https://pbs.twimg.com/media/BwUxfC6CIAEr-Ye.jpg", - "url": "http://t.co/PkCJAcSuYK", - "display_url": "pic.twitter.com/PkCJAcSuYK", - "expanded_url": "http://twitter.com/KATANA77/status/505864943636197376/photo/1", - "type": "photo", - "sizes": { - "medium": { - "w": 600, - "h": 338, - "resize": "fit" - }, - "small": { - "w": 340, - "h": 192, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "large": { - "w": 765, - "h": 432, - "resize": "fit" - } - }, - "source_status_id": 505864943636197400, - "source_status_id_str": "505864943636197376" - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:14 +0000 2014", - "id": 505874920140591100, - "id_str": "505874920140591104", - "text": "@longhairxMIURA 朝一ライカス辛目だよw", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": 505874728897085440, - "in_reply_to_status_id_str": "505874728897085440", - "in_reply_to_user_id": 114188950, - "in_reply_to_user_id_str": "114188950", - "in_reply_to_screen_name": "longhairxMIURA", - "user": { - "id": 114786346, - "id_str": "114786346", - "name": "PROTECT-T", - "screen_name": "ttm_protect", - "location": "静岡県長泉町", - "description": "24 / XXX / @andprotector / @lifefocus0545 potato design works", - "url": "http://t.co/5EclbQiRX4", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/5EclbQiRX4", - "expanded_url": "http://ap.furtherplatonix.net/index.html", - "display_url": "ap.furtherplatonix.net/index.html", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 1387, - "friends_count": 903, - "listed_count": 25, - "created_at": "Tue Feb 16 16:13:41 +0000 2010", - "favourites_count": 492, - "utc_offset": 32400, - "time_zone": "Osaka", - "geo_enabled": false, - "verified": false, - "statuses_count": 12679, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/481360383253295104/4B9Rcfys_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/481360383253295104/4B9Rcfys_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/114786346/1403600232", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "longhairxMIURA", - "name": "miura desu", - "id": 114188950, - "id_str": "114188950", - "indices": [ - 0, - 15 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:14 +0000 2014", - "id": 505874919020699650, - "id_str": "505874919020699648", - "text": "RT @omo_kko: ラウワン脱出→友達が家に連んで帰ってって言うから友達ん家に乗せて帰る(1度も行ったことない田舎道)→友達おろして迷子→500メートルくらい続く変な一本道進む→墓地で行き止まりでUターン出来ずバックで500メートル元のところまで進まないといけない←今ここ", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 392585658, - "id_str": "392585658", - "name": "原稿", - "screen_name": "chibu4267", - "location": "キミの部屋の燃えるゴミ箱", - "description": "RTしてTLに濁流を起こすからフォローしない方が良いよ 言ってることもつまらないし 詳細→http://t.co/ANSFlYXERJ 相方@1life_5106_hshd 葛西教徒その壱", - "url": "http://t.co/JTFjV89eaN", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/JTFjV89eaN", - "expanded_url": "http://www.pixiv.net/member.php?id=1778417", - "display_url": "pixiv.net/member.php?id=…", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [ - { - "url": "http://t.co/ANSFlYXERJ", - "expanded_url": "http://twpf.jp/chibu4267", - "display_url": "twpf.jp/chibu4267", - "indices": [ - 45, - 67 - ] - } - ] - } - }, - "protected": false, - "followers_count": 1324, - "friends_count": 1165, - "listed_count": 99, - "created_at": "Mon Oct 17 08:23:46 +0000 2011", - "favourites_count": 9542, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": true, - "verified": false, - "statuses_count": 369420, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/453106940822814720/PcJIZv43.png", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/453106940822814720/PcJIZv43.png", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/505731759216943107/pzhnkMEg_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/505731759216943107/pzhnkMEg_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/392585658/1362383911", - "profile_link_color": "5EB9FF", - "profile_sidebar_border_color": "FFFFFF", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sat Aug 30 16:51:09 +0000 2014", - "id": 505759640164892700, - "id_str": "505759640164892673", - "text": "ラウワン脱出→友達が家に連んで帰ってって言うから友達ん家に乗せて帰る(1度も行ったことない田舎道)→友達おろして迷子→500メートルくらい続く変な一本道進む→墓地で行き止まりでUターン出来ずバックで500メートル元のところまで進まないといけない←今ここ", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 309565423, - "id_str": "309565423", - "name": "おもっこ", - "screen_name": "omo_kko", - "location": "", - "description": "ぱんすと", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 730, - "friends_count": 200, - "listed_count": 23, - "created_at": "Thu Jun 02 09:15:51 +0000 2011", - "favourites_count": 5441, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": true, - "verified": false, - "statuses_count": 30012, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/499126939378929664/GLWpIKTW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/499126939378929664/GLWpIKTW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/309565423/1409418370", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 67, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "omo_kko", - "name": "おもっこ", - "id": 309565423, - "id_str": "309565423", - "indices": [ - 3, - 11 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:13 +0000 2014", - "id": 505874918198624260, - "id_str": "505874918198624256", - "text": "RT @thsc782_407: #LEDカツカツ選手権\n漢字一文字ぶんのスペースに「ハウステンボス」を収める狂気 http://t.co/vmrreDMziI", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 753161754, - "id_str": "753161754", - "name": "ねこねこみかん*", - "screen_name": "nekonekomikan", - "location": "ソーダ水のあふれるビンの中", - "description": "猫×6、大学・高校・旦那各1と暮らしています。猫、子供、日常思った事をつぶやいています/今年の目標:読書、庭の手入れ、ランニング、手芸/猫*花*写真*詩*林ももこさん*鉄道など好きな方をフォローさせていただいています。よろしくお願いします♬", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 217, - "friends_count": 258, - "listed_count": 8, - "created_at": "Sun Aug 12 14:00:47 +0000 2012", - "favourites_count": 7650, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": false, - "verified": false, - "statuses_count": 20621, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/470627990271848448/m83uy6Vc_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/470627990271848448/m83uy6Vc_normal.jpeg", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Fri Feb 28 16:04:13 +0000 2014", - "id": 439430848190742500, - "id_str": "439430848190742528", - "text": "#LEDカツカツ選手権\n漢字一文字ぶんのスペースに「ハウステンボス」を収める狂気 http://t.co/vmrreDMziI", - "source": "Twitter Web Client", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 82900665, - "id_str": "82900665", - "name": "[90]青葉台 芦 (第二粟屋) 屋", - "screen_name": "thsc782_407", - "location": "かんましき", - "description": "湯の街の元勃酩姦なんちゃら大 赤い犬の犬(外資系) 肥後で緑ナンバー屋さん勤め\nくだらないことしかつぶやかないし、いちいち訳のわからない記号を連呼するので相当邪魔になると思います。害はないと思います。のりものの画像とかたくさん上げます。さみしい。車輪のついたものならだいたい好き。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 587, - "friends_count": 623, - "listed_count": 30, - "created_at": "Fri Oct 16 15:13:32 +0000 2009", - "favourites_count": 1405, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": true, - "verified": false, - "statuses_count": 60427, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "352726", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/154137819/__813-1103.jpg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/154137819/__813-1103.jpg", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/493760276676620289/32oLiTtT_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/493760276676620289/32oLiTtT_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/82900665/1398865798", - "profile_link_color": "D02B55", - "profile_sidebar_border_color": "829D5E", - "profile_sidebar_fill_color": "99CC33", - "profile_text_color": "3E4415", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 3291, - "favorite_count": 1526, - "entities": { - "hashtags": [ - { - "text": "LEDカツカツ選手権", - "indices": [ - 0, - 11 - ] - } - ], - "symbols": [], - "urls": [], - "user_mentions": [], - "media": [ - { - "id": 439430848194936800, - "id_str": "439430848194936832", - "indices": [ - 41, - 63 - ], - "media_url": "http://pbs.twimg.com/media/BhksBzoCAAAJeDS.jpg", - "media_url_https": "https://pbs.twimg.com/media/BhksBzoCAAAJeDS.jpg", - "url": "http://t.co/vmrreDMziI", - "display_url": "pic.twitter.com/vmrreDMziI", - "expanded_url": "http://twitter.com/thsc782_407/status/439430848190742528/photo/1", - "type": "photo", - "sizes": { - "medium": { - "w": 600, - "h": 450, - "resize": "fit" - }, - "large": { - "w": 1024, - "h": 768, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "small": { - "w": 340, - "h": 255, - "resize": "fit" - } - } - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - "retweet_count": 3291, - "favorite_count": 0, - "entities": { - "hashtags": [ - { - "text": "LEDカツカツ選手権", - "indices": [ - 17, - 28 - ] - } - ], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "thsc782_407", - "name": "[90]青葉台 芦 (第二粟屋) 屋", - "id": 82900665, - "id_str": "82900665", - "indices": [ - 3, - 15 - ] - } - ], - "media": [ - { - "id": 439430848194936800, - "id_str": "439430848194936832", - "indices": [ - 58, - 80 - ], - "media_url": "http://pbs.twimg.com/media/BhksBzoCAAAJeDS.jpg", - "media_url_https": "https://pbs.twimg.com/media/BhksBzoCAAAJeDS.jpg", - "url": "http://t.co/vmrreDMziI", - "display_url": "pic.twitter.com/vmrreDMziI", - "expanded_url": "http://twitter.com/thsc782_407/status/439430848190742528/photo/1", - "type": "photo", - "sizes": { - "medium": { - "w": 600, - "h": 450, - "resize": "fit" - }, - "large": { - "w": 1024, - "h": 768, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "small": { - "w": 340, - "h": 255, - "resize": "fit" - } - }, - "source_status_id": 439430848190742500, - "source_status_id_str": "439430848190742528" - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:13 +0000 2014", - "id": 505874918039228400, - "id_str": "505874918039228416", - "text": "【金一地区太鼓台】川関と小山の見分けがつかない", - "source": "twittbot.net", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2530194984, - "id_str": "2530194984", - "name": "川之江中高生あるある", - "screen_name": "kw_aru", - "location": "DMにてネタ提供待ってますよ", - "description": "川之江中高生の川之江中高生による川之江中高生のためのあるあるアカウントです。タイムリーなネタはお気に入りにあります。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 113, - "friends_count": 157, - "listed_count": 0, - "created_at": "Wed May 28 15:01:43 +0000 2014", - "favourites_count": 30, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 4472, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/471668359314948097/XbIyXiZK_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/471668359314948097/XbIyXiZK_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2530194984/1401289473", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:13 +0000 2014", - "id": 505874915338104800, - "id_str": "505874915338104833", - "text": "おはようございますん♪ SSDSのDVDが朝一で届いた〜(≧∇≦)", - "source": "TweetList!", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 428179337, - "id_str": "428179337", - "name": "サラ", - "screen_name": "sala_mgn", - "location": "東京都", - "description": "bot遊びと実況が主目的の趣味アカウント。成人済♀。時々TLお騒がせします。リフォ率低いですがF/Bご自由に。スパムはブロック![HOT]K[アニメ]タイバニ/K/薄桜鬼/トライガン/進撃[小説]冲方丁/森博嗣[漫画]内藤泰弘/高河ゆん[他]声優/演劇 ※@sano_bot1二代目管理人", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 104, - "friends_count": 421, - "listed_count": 2, - "created_at": "Sun Dec 04 12:51:18 +0000 2011", - "favourites_count": 3257, - "utc_offset": -36000, - "time_zone": "Hawaii", - "geo_enabled": false, - "verified": false, - "statuses_count": 25303, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "1A1B1F", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/601682567/put73jtg48ytjylq00if.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/601682567/put73jtg48ytjylq00if.jpeg", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/3350624721/755920942e4f512e6ba489df7eb1147e_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/3350624721/755920942e4f512e6ba489df7eb1147e_normal.jpeg", - "profile_link_color": "2FC2EF", - "profile_sidebar_border_color": "181A1E", - "profile_sidebar_fill_color": "252429", - "profile_text_color": "666666", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:13 +0000 2014", - "id": 505874914897690600, - "id_str": "505874914897690624", - "text": "@ran_kirazuki そのようなお言葉を頂けるとは……!この雨太郎、誠心誠意を持って姉御の足の指の第一関節を崇め奉りとうございます", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": 505874276692406300, - "in_reply_to_status_id_str": "505874276692406272", - "in_reply_to_user_id": 531544559, - "in_reply_to_user_id_str": "531544559", - "in_reply_to_screen_name": "ran_kirazuki", - "user": { - "id": 2364828518, - "id_str": "2364828518", - "name": "雨", - "screen_name": "tear_dice", - "location": "変態/日常/創作/室町/たまに版権", - "description": "アイコンは兄さんから!", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 28, - "friends_count": 28, - "listed_count": 0, - "created_at": "Fri Feb 28 00:28:40 +0000 2014", - "favourites_count": 109, - "utc_offset": 32400, - "time_zone": "Seoul", - "geo_enabled": false, - "verified": false, - "statuses_count": 193, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "000000", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/504434510675443713/lvW7ad5b.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/504434510675443713/lvW7ad5b.jpeg", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/505170142284640256/rnW4XeEJ_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/505170142284640256/rnW4XeEJ_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2364828518/1409087198", - "profile_link_color": "0D31BF", - "profile_sidebar_border_color": "000000", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "ran_kirazuki", - "name": "蘭ぴよの日常", - "id": 531544559, - "id_str": "531544559", - "indices": [ - 0, - 13 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:13 +0000 2014", - "id": 505874914591514600, - "id_str": "505874914591514626", - "text": "RT @AFmbsk: @samao21718 \n呼び方☞まおちゃん\n呼ばれ方☞あーちゃん\n第一印象☞平野から?!\n今の印象☞おとなっぽい!!\nLINE交換☞もってるん\\( ˆoˆ )/\nトプ画について☞楽しそうでいーな😳\n家族にするなら☞おねぇちゃん\n最後に一言☞全然会えない…", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2179759316, - "id_str": "2179759316", - "name": "まお", - "screen_name": "samao21718", - "location": "埼玉 UK留学してました✈", - "description": "゚.*97line おさらに貢いでる系女子*.゜ DISH// ✯ 佐野悠斗 ✯ 読モ ✯ WEGO ✯ 嵐 I met @OTYOfficial in the London ;)", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 111, - "friends_count": 121, - "listed_count": 0, - "created_at": "Thu Nov 07 09:47:41 +0000 2013", - "favourites_count": 321, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 1777, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501535615351926784/c5AAh6Sz_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501535615351926784/c5AAh6Sz_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2179759316/1407640217", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sat Aug 30 14:59:49 +0000 2014", - "id": 505731620456771600, - "id_str": "505731620456771584", - "text": "@samao21718 \n呼び方☞まおちゃん\n呼ばれ方☞あーちゃん\n第一印象☞平野から?!\n今の印象☞おとなっぽい!!\nLINE交換☞もってるん\\( ˆoˆ )/\nトプ画について☞楽しそうでいーな😳\n家族にするなら☞おねぇちゃん\n最後に一言☞全然会えないねー今度会えたらいいな!", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": 2179759316, - "in_reply_to_user_id_str": "2179759316", - "in_reply_to_screen_name": "samao21718", - "user": { - "id": 1680668713, - "id_str": "1680668713", - "name": "★Shiiiii!☆", - "screen_name": "AFmbsk", - "location": "埼玉", - "description": "2310*basketball#41*UVERworld*Pooh☪Bell +.。*弱さを知って強くなれ*゚", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 429, - "friends_count": 434, - "listed_count": 0, - "created_at": "Sun Aug 18 12:45:00 +0000 2013", - "favourites_count": 2488, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 6352, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/504643170886365185/JN_dlwUd_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/504643170886365185/JN_dlwUd_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1680668713/1408805886", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 1, - "favorite_count": 1, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "samao21718", - "name": "まお", - "id": 2179759316, - "id_str": "2179759316", - "indices": [ - 0, - 11 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 1, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "AFmbsk", - "name": "★Shiiiii!☆", - "id": 1680668713, - "id_str": "1680668713", - "indices": [ - 3, - 10 - ] - }, - { - "screen_name": "samao21718", - "name": "まお", - "id": 2179759316, - "id_str": "2179759316", - "indices": [ - 12, - 23 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:10 +0000 2014", - "id": 505874905712189440, - "id_str": "505874905712189440", - "text": "一、常に身一つ簡素にして、美食を好んではならない", - "source": "twittbot.net", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1330420010, - "id_str": "1330420010", - "name": "獨行道bot", - "screen_name": "dokkodo_bot", - "location": "", - "description": "宮本武蔵の自誓書、「獨行道」に記された二十一箇条をランダムにつぶやくbotです。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 4, - "friends_count": 5, - "listed_count": 1, - "created_at": "Sat Apr 06 01:19:55 +0000 2013", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 9639, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/3482551671/d9e749f7658b523bdd50b7584ed4ba6a_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/3482551671/d9e749f7658b523bdd50b7584ed4ba6a_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1330420010/1365212335", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:10 +0000 2014", - "id": 505874903094939650, - "id_str": "505874903094939648", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "モテモテ大作戦★男子編", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2714526565, - "id_str": "2714526565", - "name": "モテモテ大作戦★男子編", - "screen_name": "mote_danshi1", - "location": "", - "description": "やっぱりモテモテ男子になりたい!自分を磨くヒントをみつけたい!応援してくれる人は RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 664, - "friends_count": 1835, - "listed_count": 0, - "created_at": "Thu Aug 07 12:59:59 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 597, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/497368689386086400/7hqdKMzG_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/497368689386086400/7hqdKMzG_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714526565/1407416898", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:10 +0000 2014", - "id": 505874902390276100, - "id_str": "505874902390276096", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "心に響くアツい名言集", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2699261263, - "id_str": "2699261263", - "name": "心に響くアツい名言集", - "screen_name": "kokoro_meigen11", - "location": "", - "description": "人生の格言は、人の心や人生を瞬時にに動かしてしまうことがある。\r\nそんな言葉の重みを味わおう。\r\n面白かったらRT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 183, - "friends_count": 1126, - "listed_count": 0, - "created_at": "Fri Aug 01 22:00:00 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 749, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/495328654126112768/1rKnNuWK_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/495328654126112768/1rKnNuWK_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2699261263/1406930543", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:10 +0000 2014", - "id": 505874902247677950, - "id_str": "505874902247677954", - "text": "RT @POTENZA_SUPERGT: ありがとうございます!“@8CBR8: @POTENZA_SUPERGT 13時半ごろ一雨きそうですが、無事全車決勝レース完走出来ること祈ってます! http://t.co/FzTyFnt9xH”", - "source": "jigtwi", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1021030416, - "id_str": "1021030416", - "name": "narur", - "screen_name": "narur2", - "location": "晴れの国なのに何故か開幕戦では雨や雪や冰や霰が降る✨", - "description": "F1.GP2.Superformula.SuperGT.F3...\nスーパーGTが大好き♡車が好き!新幹線も好き!飛行機も好き!こっそり別アカです(๑´ㅂ`๑)♡*.+゜", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 257, - "friends_count": 237, - "listed_count": 2, - "created_at": "Wed Dec 19 01:14:41 +0000 2012", - "favourites_count": 547, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 55417, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/462180217574789121/1Jf6m_2L.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/462180217574789121/1Jf6m_2L.jpeg", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/444312241395863552/FKl40ebQ_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/444312241395863552/FKl40ebQ_normal.jpeg", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:05:11 +0000 2014", - "id": 505868866686169100, - "id_str": "505868866686169089", - "text": "ありがとうございます!“@8CBR8: @POTENZA_SUPERGT 13時半ごろ一雨きそうですが、無事全車決勝レース完走出来ること祈ってます! http://t.co/FzTyFnt9xH”", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": 505868690588303360, - "in_reply_to_status_id_str": "505868690588303360", - "in_reply_to_user_id": 333344408, - "in_reply_to_user_id_str": "333344408", - "in_reply_to_screen_name": "8CBR8", - "user": { - "id": 359324738, - "id_str": "359324738", - "name": "POTENZA_SUPERGT", - "screen_name": "POTENZA_SUPERGT", - "location": "", - "description": "ブリヂストンのスポーツタイヤ「POTENZA」のアカウントです。レースやタイヤの事などをつぶやきます。今シーズンも「チャンピオンタイヤの称号は譲らない」をキャッチコピーに、タイヤ供給チームを全力でサポートしていきますので、応援よろしくお願いします!なお、返信ができない場合もありますので、ご了承よろしくお願い致します。", - "url": "http://t.co/LruVPk5x4K", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/LruVPk5x4K", - "expanded_url": "http://www.bridgestone.co.jp/sc/potenza/", - "display_url": "bridgestone.co.jp/sc/potenza/", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 9612, - "friends_count": 308, - "listed_count": 373, - "created_at": "Sun Aug 21 11:33:38 +0000 2011", - "favourites_count": 26, - "utc_offset": -36000, - "time_zone": "Hawaii", - "geo_enabled": true, - "verified": false, - "statuses_count": 10032, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "131516", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/1507885396/TW_image_normal.jpg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/1507885396/TW_image_normal.jpg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/359324738/1402546267", - "profile_link_color": "FF2424", - "profile_sidebar_border_color": "EEEEEE", - "profile_sidebar_fill_color": "EFEFEF", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 7, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "8CBR8", - "name": "CBR Rider #17 KEIHIN", - "id": 333344408, - "id_str": "333344408", - "indices": [ - 12, - 18 - ] - }, - { - "screen_name": "POTENZA_SUPERGT", - "name": "POTENZA_SUPERGT", - "id": 359324738, - "id_str": "359324738", - "indices": [ - 20, - 36 - ] - } - ], - "media": [ - { - "id": 505868690252779500, - "id_str": "505868690252779521", - "indices": [ - 75, - 97 - ], - "media_url": "http://pbs.twimg.com/media/BwU05MGCUAEY6Wu.jpg", - "media_url_https": "https://pbs.twimg.com/media/BwU05MGCUAEY6Wu.jpg", - "url": "http://t.co/FzTyFnt9xH", - "display_url": "pic.twitter.com/FzTyFnt9xH", - "expanded_url": "http://twitter.com/8CBR8/status/505868690588303360/photo/1", - "type": "photo", - "sizes": { - "medium": { - "w": 600, - "h": 399, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "large": { - "w": 1024, - "h": 682, - "resize": "fit" - }, - "small": { - "w": 340, - "h": 226, - "resize": "fit" - } - }, - "source_status_id": 505868690588303360, - "source_status_id_str": "505868690588303360" - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - "retweet_count": 7, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "POTENZA_SUPERGT", - "name": "POTENZA_SUPERGT", - "id": 359324738, - "id_str": "359324738", - "indices": [ - 3, - 19 - ] - }, - { - "screen_name": "8CBR8", - "name": "CBR Rider #17 KEIHIN", - "id": 333344408, - "id_str": "333344408", - "indices": [ - 33, - 39 - ] - }, - { - "screen_name": "POTENZA_SUPERGT", - "name": "POTENZA_SUPERGT", - "id": 359324738, - "id_str": "359324738", - "indices": [ - 41, - 57 - ] - } - ], - "media": [ - { - "id": 505868690252779500, - "id_str": "505868690252779521", - "indices": [ - 96, - 118 - ], - "media_url": "http://pbs.twimg.com/media/BwU05MGCUAEY6Wu.jpg", - "media_url_https": "https://pbs.twimg.com/media/BwU05MGCUAEY6Wu.jpg", - "url": "http://t.co/FzTyFnt9xH", - "display_url": "pic.twitter.com/FzTyFnt9xH", - "expanded_url": "http://twitter.com/8CBR8/status/505868690588303360/photo/1", - "type": "photo", - "sizes": { - "medium": { - "w": 600, - "h": 399, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "large": { - "w": 1024, - "h": 682, - "resize": "fit" - }, - "small": { - "w": 340, - "h": 226, - "resize": "fit" - } - }, - "source_status_id": 505868690588303360, - "source_status_id_str": "505868690588303360" - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:09 +0000 2014", - "id": 505874901689851900, - "id_str": "505874901689851904", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "ここだけの本音★男子編", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2762136439, - "id_str": "2762136439", - "name": "ここだけの本音★男子編", - "screen_name": "danshi_honne1", - "location": "", - "description": "思ってるけど言えない!でもホントは言いたいこと、実はいっぱいあるんです! \r\nそんな男子の本音を、つぶやきます。 \r\nその気持わかるって人は RT & フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 101, - "friends_count": 985, - "listed_count": 0, - "created_at": "Sun Aug 24 11:11:30 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 209, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503500282840354816/CEv8UMay_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503500282840354816/CEv8UMay_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762136439/1408878822", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:09 +0000 2014", - "id": 505874900939046900, - "id_str": "505874900939046912", - "text": "RT @UARROW_Y: ようかい体操第一を踊る国見英 http://t.co/SXoYWH98as", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2454426158, - "id_str": "2454426158", - "name": "ぴかりん", - "screen_name": "gncnToktTtksg", - "location": "", - "description": "銀魂/黒バス/進撃/ハイキュー/BLEACH/うたプリ/鈴木達央さん/神谷浩史さん 気軽にフォローしてください(^∇^)✨", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 1274, - "friends_count": 1320, - "listed_count": 17, - "created_at": "Sun Apr 20 07:48:53 +0000 2014", - "favourites_count": 2314, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 5868, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/457788684146716672/KCOy0S75_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/457788684146716672/KCOy0S75_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2454426158/1409371302", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:45 +0000 2014", - "id": 505871779949051900, - "id_str": "505871779949051904", - "text": "ようかい体操第一を踊る国見英 http://t.co/SXoYWH98as", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1261662588, - "id_str": "1261662588", - "name": "ゆう矢", - "screen_name": "UARROW_Y", - "location": "つくり出そう国影の波 広げよう国影の輪", - "description": "HQ!! 成人済腐女子。日常ツイート多いです。赤葦京治夢豚クソツイ含みます注意。フォローをお考えの際はプロフご一読お願い致します。FRBお気軽に", - "url": "http://t.co/LFX2XOzb0l", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/LFX2XOzb0l", - "expanded_url": "http://twpf.jp/UARROW_Y", - "display_url": "twpf.jp/UARROW_Y", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 265, - "friends_count": 124, - "listed_count": 12, - "created_at": "Tue Mar 12 10:42:17 +0000 2013", - "favourites_count": 6762, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": true, - "verified": false, - "statuses_count": 55946, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/502095104618663937/IzuPYx3E_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/502095104618663937/IzuPYx3E_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1261662588/1408618604", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 29, - "favorite_count": 54, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/SXoYWH98as", - "expanded_url": "http://twitter.com/UARROW_Y/status/505871779949051904/photo/1", - "display_url": "pic.twitter.com/SXoYWH98as", - "indices": [ - 15, - 37 - ] - } - ], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - "retweet_count": 29, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/SXoYWH98as", - "expanded_url": "http://twitter.com/UARROW_Y/status/505871779949051904/photo/1", - "display_url": "pic.twitter.com/SXoYWH98as", - "indices": [ - 29, - 51 - ] - } - ], - "user_mentions": [ - { - "screen_name": "UARROW_Y", - "name": "ゆう矢", - "id": 1261662588, - "id_str": "1261662588", - "indices": [ - 3, - 12 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:09 +0000 2014", - "id": 505874900561580000, - "id_str": "505874900561580032", - "text": "今日は一高と三桜(・θ・)\n光梨ちゃんに会えないかな〜", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1366375976, - "id_str": "1366375976", - "name": "ゆいの", - "screen_name": "yuino1006", - "location": "", - "description": "さんおう 男バスマネ2ねん(^ω^)", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 270, - "friends_count": 260, - "listed_count": 0, - "created_at": "Sat Apr 20 07:02:08 +0000 2013", - "favourites_count": 1384, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": false, - "verified": false, - "statuses_count": 5202, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/505354401448349696/nxVFEQQ4_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/505354401448349696/nxVFEQQ4_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1366375976/1399989379", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:09 +0000 2014", - "id": 505874899324248060, - "id_str": "505874899324248064", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "共感★絶対あるあるww", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2704420069, - "id_str": "2704420069", - "name": "共感★絶対あるあるww", - "screen_name": "kyoukan_aru", - "location": "", - "description": "みんなにもわかってもらえる、あるあるを見つけたい。\r\n面白かったらRT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 857, - "friends_count": 1873, - "listed_count": 0, - "created_at": "Sun Aug 03 15:50:40 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 682, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/495960812670836737/1LqkoyvU_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/495960812670836737/1LqkoyvU_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2704420069/1407081298", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:09 +0000 2014", - "id": 505874898493796350, - "id_str": "505874898493796352", - "text": "RT @assam_house: 泉田新潟県知事は、東電の申請書提出を容認させられただけで、再稼働に必要な「同意」はまだ与えていません。今まで柏崎刈羽の再稼働を抑え続けてきた知事に、もう一踏ん張りをお願いする意見を送って下さい。全国の皆様、お願いします!\nhttp://t.co…", - "source": "jigtwi for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 960765968, - "id_str": "960765968", - "name": "さち", - "screen_name": "sachitaka_dears", - "location": "宮城県", - "description": "動物関連のアカウントです。サブアカウント@sachi_dears (さち ❷) もあります。『心あるものは皆、愛し愛されるために生まれてきた。そして愛情を感じながら生を全うするべきなんだ』", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 3212, - "friends_count": 3528, - "listed_count": 91, - "created_at": "Tue Nov 20 16:30:53 +0000 2012", - "favourites_count": 3180, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": false, - "verified": false, - "statuses_count": 146935, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/3659653229/5b698df67f5d105400e9077f5ea50e91_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/3659653229/5b698df67f5d105400e9077f5ea50e91_normal.png", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Tue Aug 19 11:00:53 +0000 2014", - "id": 501685228427964400, - "id_str": "501685228427964417", - "text": "泉田新潟県知事は、東電の申請書提出を容認させられただけで、再稼働に必要な「同意」はまだ与えていません。今まで柏崎刈羽の再稼働を抑え続けてきた知事に、もう一踏ん張りをお願いする意見を送って下さい。全国の皆様、お願いします!\nhttp://t.co/9oH5cgpy1q", - "source": "twittbot.net", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1104771276, - "id_str": "1104771276", - "name": "アッサム山中(殺処分ゼロに一票)", - "screen_name": "assam_house", - "location": "新潟県柏崎市", - "description": "アッサム山中の趣味用アカ。当分の間、選挙啓発用としても使っていきます。このアカウントがアッサム山中本人のものである事は @assam_yamanaka のプロフでご確認下さい。\r\n公選法に係る表示\r\n庶民新党 #脱原発 http://t.co/96UqoCo0oU\r\nonestep.revival@gmail.com", - "url": "http://t.co/AEOCATaNZc", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/AEOCATaNZc", - "expanded_url": "http://www.assam-house.net/", - "display_url": "assam-house.net", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [ - { - "url": "http://t.co/96UqoCo0oU", - "expanded_url": "http://blog.assam-house.net/datsu-genpatsu/index.html", - "display_url": "blog.assam-house.net/datsu-genpatsu…", - "indices": [ - 110, - 132 - ] - } - ] - } - }, - "protected": false, - "followers_count": 2977, - "friends_count": 3127, - "listed_count": 64, - "created_at": "Sat Jan 19 22:10:13 +0000 2013", - "favourites_count": 343, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": false, - "verified": false, - "statuses_count": 18021, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/378800000067217575/e0a85b440429ff50430a41200327dcb8_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000067217575/e0a85b440429ff50430a41200327dcb8_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1104771276/1408948288", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 2, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/9oH5cgpy1q", - "expanded_url": "http://www.pref.niigata.lg.jp/kouhou/info.html", - "display_url": "pref.niigata.lg.jp/kouhou/info.ht…", - "indices": [ - 111, - 133 - ] - } - ], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - "retweet_count": 2, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/9oH5cgpy1q", - "expanded_url": "http://www.pref.niigata.lg.jp/kouhou/info.html", - "display_url": "pref.niigata.lg.jp/kouhou/info.ht…", - "indices": [ - 139, - 140 - ] - } - ], - "user_mentions": [ - { - "screen_name": "assam_house", - "name": "アッサム山中(殺処分ゼロに一票)", - "id": 1104771276, - "id_str": "1104771276", - "indices": [ - 3, - 15 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:09 +0000 2014", - "id": 505874898468630500, - "id_str": "505874898468630528", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "おしゃれ★ペアルック", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2708607692, - "id_str": "2708607692", - "name": "おしゃれ★ペアルック", - "screen_name": "osyare_pea", - "location": "", - "description": "ラブラブ度がアップする、素敵なペアルックを見つけて紹介します♪ 気に入ったら RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 129, - "friends_count": 1934, - "listed_count": 0, - "created_at": "Tue Aug 05 07:09:31 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 641, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/496554257676382208/Zgg0bmNu_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/496554257676382208/Zgg0bmNu_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2708607692/1407222776", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:08 +0000 2014", - "id": 505874897633951740, - "id_str": "505874897633951745", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "LOVE ♥ ラブライブ", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745389137, - "id_str": "2745389137", - "name": "LOVE ♥ ラブライブ", - "screen_name": "love_live55", - "location": "", - "description": "とにかく「ラブライブが好きで~す♥」 \r\nラブライブファンには、たまらない内容ばかり集めています♪ \r\n気に入ったら RT & 相互フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 251, - "friends_count": 969, - "listed_count": 0, - "created_at": "Tue Aug 19 15:45:40 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 348, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501757482448850944/x2uPpqRx_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501757482448850944/x2uPpqRx_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745389137/1408463342", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:08 +0000 2014", - "id": 505874896795086850, - "id_str": "505874896795086848", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "恋する♡ドレスシリーズ", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2726346560, - "id_str": "2726346560", - "name": "恋する♡ドレスシリーズ", - "screen_name": "koisurudoress", - "location": "", - "description": "どれもこれも、見ているだけで欲しくなっちゃう♪ \r\n特別な日に着る素敵なドレスを見つけたいです。 \r\n着てみたいと思ったら RT & フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 314, - "friends_count": 1900, - "listed_count": 0, - "created_at": "Tue Aug 12 14:10:35 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 471, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/499199619465621504/fg7sVusT_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/499199619465621504/fg7sVusT_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2726346560/1407853688", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:08 +0000 2014", - "id": 505874895964626940, - "id_str": "505874895964626944", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "胸キュン♥動物図鑑", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2759192574, - "id_str": "2759192574", - "name": "胸キュン♥動物図鑑", - "screen_name": "doubutuzukan", - "location": "", - "description": "ふとした表情に思わずキュンとしてしまう♪ \r\nそんな愛しの動物たちの写真を見つけます。 \r\n気に入ったら RT & フォローを、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 80, - "friends_count": 959, - "listed_count": 1, - "created_at": "Sat Aug 23 15:47:36 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 219, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503211559552688128/Ej_bixna_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503211559552688128/Ej_bixna_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2759192574/1408809101", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:08 +0000 2014", - "id": 505874895079608300, - "id_str": "505874895079608320", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "ディズニー★パラダイス", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2719228561, - "id_str": "2719228561", - "name": "ディズニー★パラダイス", - "screen_name": "disney_para", - "location": "", - "description": "ディズニーのかわいい画像、ニュース情報、あるあるなどをお届けします♪\r\nディズニーファンは RT & フォローもお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 331, - "friends_count": 1867, - "listed_count": 0, - "created_at": "Sat Aug 09 12:01:32 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 540, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498076922488696832/Ti2AEuOT_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498076922488696832/Ti2AEuOT_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2719228561/1407585841", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:08 +0000 2014", - "id": 505874894135898100, - "id_str": "505874894135898112", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "生々しい風刺画", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2714772727, - "id_str": "2714772727", - "name": "生々しい風刺画", - "screen_name": "nama_fuushi", - "location": "", - "description": "深い意味が込められた「生々しい風刺画」を見つけます。\r\n考えさせられたら RT & 相互フォローでみなさん、お願いします", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 298, - "friends_count": 1902, - "listed_count": 1, - "created_at": "Thu Aug 07 15:04:45 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 595, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/497398363352875011/tS-5FPJB_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/497398363352875011/tS-5FPJB_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714772727/1407424091", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:07 +0000 2014", - "id": 505874893347377150, - "id_str": "505874893347377152", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "嵐★大好きっ娘", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2721682579, - "id_str": "2721682579", - "name": "嵐★大好きっ娘", - "screen_name": "arashi_suki1", - "location": "", - "description": "なんだかんだ言って、やっぱり嵐が好きなんです♪\r\nいろいろ集めたいので、嵐好きな人に見てほしいです。\r\n気に入ったら RT & 相互フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 794, - "friends_count": 1913, - "listed_count": 2, - "created_at": "Sun Aug 10 13:43:56 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 504, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498465364733198336/RO6wupdc_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498465364733198336/RO6wupdc_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2721682579/1407678436", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:07 +0000 2014", - "id": 505874893154426900, - "id_str": "505874893154426881", - "text": "RT @Takashi_Shiina: テレビで「成人男性のカロリー摂取量は1900kcal」とか言ってて、それはいままさに私がダイエットのために必死でキープしようとしている量で、「それが普通なら人はいつ天一やココイチに行って大盛りを食えばいいんだ!」と思った。", - "source": "twicca", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 353516742, - "id_str": "353516742", - "name": "おしんこー@土曜西え41a", - "screen_name": "oshin_koko", - "location": "こたつ", - "description": "ROMって楽しんでいる部分もあり無言フォロー多めですすみません…。ツイート数多め・あらぶり多めなのでフォロー非推奨です。最近は早兵・兵部受け中心ですがBLNLなんでも好きです。地雷少ないため雑多に呟きます。腐・R18・ネタバレ有るのでご注意。他好きなジャンルはプロフ参照願います。 主催→@chounou_antholo", - "url": "http://t.co/mM1dG54NiO", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/mM1dG54NiO", - "expanded_url": "http://twpf.jp/oshin_koko", - "display_url": "twpf.jp/oshin_koko", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 479, - "friends_count": 510, - "listed_count": 43, - "created_at": "Fri Aug 12 05:53:13 +0000 2011", - "favourites_count": 3059, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": false, - "verified": false, - "statuses_count": 104086, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "000000", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/799871497/01583a031f83a45eba881c8acde729ee.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/799871497/01583a031f83a45eba881c8acde729ee.jpeg", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/484347196523835393/iHaYxm-2_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/484347196523835393/iHaYxm-2_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/353516742/1369039651", - "profile_link_color": "FF96B0", - "profile_sidebar_border_color": "FFFFFF", - "profile_sidebar_fill_color": "95E8EC", - "profile_text_color": "3C3940", - "profile_use_background_image": false, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sat Aug 30 09:58:30 +0000 2014", - "id": 505655792733650940, - "id_str": "505655792733650944", - "text": "テレビで「成人男性のカロリー摂取量は1900kcal」とか言ってて、それはいままさに私がダイエットのために必死でキープしようとしている量で、「それが普通なら人はいつ天一やココイチに行って大盛りを食えばいいんだ!」と思った。", - "source": "Janetter", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 126573583, - "id_str": "126573583", - "name": "椎名高志", - "screen_name": "Takashi_Shiina", - "location": "BABEL(超能力支援研究局)", - "description": "漫画家。週刊少年サンデーで『絶対可憐チルドレン』連載中。TVアニメ『THE UNLIMITED 兵部京介』公式サイト>http://t.co/jVqBoBEc", - "url": "http://t.co/K3Oi83wM3w", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/K3Oi83wM3w", - "expanded_url": "http://cnanews.asablo.jp/blog/", - "display_url": "cnanews.asablo.jp/blog/", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [ - { - "url": "http://t.co/jVqBoBEc", - "expanded_url": "http://unlimited-zc.jp/index.html", - "display_url": "unlimited-zc.jp/index.html", - "indices": [ - 59, - 79 - ] - } - ] - } - }, - "protected": false, - "followers_count": 110756, - "friends_count": 61, - "listed_count": 8159, - "created_at": "Fri Mar 26 08:54:51 +0000 2010", - "favourites_count": 25, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": false, - "verified": false, - "statuses_count": 27364, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "EDECE9", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme3/bg.gif", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme3/bg.gif", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/504597210772688896/Uvt4jgf5_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/504597210772688896/Uvt4jgf5_normal.png", - "profile_link_color": "088253", - "profile_sidebar_border_color": "D3D2CF", - "profile_sidebar_fill_color": "E3E2DE", - "profile_text_color": "634047", - "profile_use_background_image": false, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 221, - "favorite_count": 109, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 221, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "Takashi_Shiina", - "name": "椎名高志", - "id": 126573583, - "id_str": "126573583", - "indices": [ - 3, - 18 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:07 +0000 2014", - "id": 505874892567244800, - "id_str": "505874892567244801", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "下ネタ&笑変態雑学", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2762581922, - "id_str": "2762581922", - "name": "下ネタ&笑変態雑学", - "screen_name": "shimo_hentai", - "location": "", - "description": "普通の人には思いつかない、ちょっと変態チックな 笑える下ネタ雑学をお届けします。 \r\nおもしろかったら RT & フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 37, - "friends_count": 990, - "listed_count": 0, - "created_at": "Sun Aug 24 14:13:20 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 212, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503545991950114816/K9yQbh1Q_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503545991950114816/K9yQbh1Q_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762581922/1408889893", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:07 +0000 2014", - "id": 505874891778703360, - "id_str": "505874891778703360", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "超簡単★初心者英語", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2744544025, - "id_str": "2744544025", - "name": "超簡単★初心者英語", - "screen_name": "kantaneigo1", - "location": "", - "description": "すぐに使えるフレーズや簡単な会話を紹介します。 \r\n少しづつ練習して、どんどん使ってみよう☆ \r\n使ってみたいと思ったら RT & フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 147, - "friends_count": 970, - "listed_count": 1, - "created_at": "Tue Aug 19 10:11:48 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 345, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501676136321929216/4MLpyHe3_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501676136321929216/4MLpyHe3_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744544025/1408443928", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:07 +0000 2014", - "id": 505874891032121340, - "id_str": "505874891032121344", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "現代のハンドサイン", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2762816814, - "id_str": "2762816814", - "name": "現代のハンドサイン", - "screen_name": "ima_handsign", - "location": "", - "description": "イザという時や、困った時に、必ず役に立つハンドサインのオンパレードです♪ \r\n使ってみたくなったら RT & フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 95, - "friends_count": 996, - "listed_count": 0, - "created_at": "Sun Aug 24 15:33:58 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 210, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503566188253687809/7wtdp1AC_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503566188253687809/7wtdp1AC_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762816814/1408894540", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:07 +0000 2014", - "id": 505874890247782400, - "id_str": "505874890247782401", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "今日からアナタもイイ女♪", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2714167411, - "id_str": "2714167411", - "name": "今日からアナタもイイ女♪", - "screen_name": "anata_iionna", - "location": "", - "description": "みんなが知りたい イイ女の秘密を見つけます♪ いいな~と思ってくれた人は RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 390, - "friends_count": 1425, - "listed_count": 0, - "created_at": "Thu Aug 07 09:27:59 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 609, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/497314455655436288/dz7P3-fy_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/497314455655436288/dz7P3-fy_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714167411/1407404214", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:07 +0000 2014", - "id": 505874890218434560, - "id_str": "505874890218434560", - "text": "@kohecyan3 \n名前:上野滉平\n呼び方:うえの\n呼ばれ方:ずるかわ\n第一印象:過剰な俺イケメンですアピール\n今の印象:バーバリーの時計\n好きなところ:あの自信さ、笑いが絶えない\n一言:大学受かったの?応援してる〜(*^^*)!\n\n#RTした人にやる\nちょっとやってみる笑", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": 2591363659, - "in_reply_to_user_id_str": "2591363659", - "in_reply_to_screen_name": "kohecyan3", - "user": { - "id": 2613282517, - "id_str": "2613282517", - "name": "K", - "screen_name": "kawazurukenna", - "location": "", - "description": "# I surprise even my self", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 113, - "friends_count": 185, - "listed_count": 0, - "created_at": "Wed Jul 09 09:39:13 +0000 2014", - "favourites_count": 157, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 242, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/502436858135973888/PcUU0lov_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/502436858135973888/PcUU0lov_normal.jpeg", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [ - { - "text": "RTした人にやる", - "indices": [ - 119, - 128 - ] - } - ], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "kohecyan3", - "name": "上野滉平", - "id": 2591363659, - "id_str": "2591363659", - "indices": [ - 0, - 10 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:07 +0000 2014", - "id": 505874889392156700, - "id_str": "505874889392156672", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "IQ★力だめし", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2709308887, - "id_str": "2709308887", - "name": "IQ★力だめし", - "screen_name": "iq_tameshi", - "location": "", - "description": "解けると楽しい気分になれる問題を見つけて紹介します♪面白かったら RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 443, - "friends_count": 1851, - "listed_count": 1, - "created_at": "Tue Aug 05 13:14:30 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 664, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/496646485266558977/W_W--qV__normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/496646485266558977/W_W--qV__normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2709308887/1407244754", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:06 +0000 2014", - "id": 505874888817532900, - "id_str": "505874888817532928", - "text": "第一三軍から2個師団が北へ移動中らしい     この調子では満州に陸軍兵力があふれかえる", - "source": "如月克己", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1171299612, - "id_str": "1171299612", - "name": "如月 克己", - "screen_name": "kisaragi_katumi", - "location": "満州", - "description": "GパングのA型K月克己中尉の非公式botです。 主に七巻と八巻が中心の台詞をつぶやきます。 4/18.台詞追加しました/現在試運転中/現在軽い挨拶だけTL反応。/追加したい台詞や何おかしい所がありましたらDMやリプライで/フォロー返しは手動です/", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 65, - "friends_count": 63, - "listed_count": 0, - "created_at": "Tue Feb 12 08:21:38 +0000 2013", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 27219, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/3242847112/0ce536444c94cbec607229022d43a27a_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/3242847112/0ce536444c94cbec607229022d43a27a_normal.jpeg", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:06 +0000 2014", - "id": 505874888616181760, - "id_str": "505874888616181760", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "徳田有希★応援隊", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2766021865, - "id_str": "2766021865", - "name": "徳田有希★応援隊", - "screen_name": "tokuda_ouen1", - "location": "", - "description": "女子中高生に大人気ww いやされるイラストを紹介します。 \r\nみんなで RTして応援しよう~♪ \r\n「非公式アカウントです」", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 123, - "friends_count": 978, - "listed_count": 0, - "created_at": "Mon Aug 25 10:48:41 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 210, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503857235802333184/YS0sDN6q_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503857235802333184/YS0sDN6q_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2766021865/1408963998", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:06 +0000 2014", - "id": 505874887802511360, - "id_str": "505874887802511361", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "腐女子の☆部屋", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2744683982, - "id_str": "2744683982", - "name": "腐女子の☆部屋", - "screen_name": "fujyoshinoheya", - "location": "", - "description": "腐女子にしかわからないネタや、あるあるを見つけていきます。 \r\n他には、BL~萌えキュン系まで、腐のための画像を集めています♪ \r\n同じ境遇の人には、わかってもらえると思うので、気軽に RT & フォローお願いします☆", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 241, - "friends_count": 990, - "listed_count": 0, - "created_at": "Tue Aug 19 11:47:21 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 345, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501697365590306817/GLP_QH_b_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501697365590306817/GLP_QH_b_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744683982/1408448984", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:06 +0000 2014", - "id": 505874887009767400, - "id_str": "505874887009767424", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "萌え芸術★ラテアート", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2763178045, - "id_str": "2763178045", - "name": "萌え芸術★ラテアート", - "screen_name": "moe_rate", - "location": "", - "description": "ここまで来ると、もはや芸術!! 見てるだけで楽しい♪ \r\nそんなラテアートを、とことん探します。 \r\nスゴイと思ったら RT & フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 187, - "friends_count": 998, - "listed_count": 0, - "created_at": "Sun Aug 24 16:53:16 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 210, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503586151764992000/RC80it20_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503586151764992000/RC80it20_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2763178045/1408899447", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:06 +0000 2014", - "id": 505874886225448960, - "id_str": "505874886225448960", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "全部★ジャニーズ図鑑", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2724158970, - "id_str": "2724158970", - "name": "全部★ジャニーズ図鑑", - "screen_name": "zenbu_johnnys", - "location": "", - "description": "ジャニーズのカッコイイ画像、おもしろエピソードなどを発信します。\r\n「非公式アカウントです」\r\nジャニーズ好きな人は、是非 RT & フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 738, - "friends_count": 1838, - "listed_count": 0, - "created_at": "Mon Aug 11 15:50:08 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 556, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498859581057945600/ncMKwdvC_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498859581057945600/ncMKwdvC_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2724158970/1407772462", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:06 +0000 2014", - "id": 505874885810200600, - "id_str": "505874885810200576", - "text": "RT @naopisu_: 呼び方:\n呼ばれ方:\n第一印象:\n今の印象:\n好きなところ:\n家族にするなら:\n最後に一言:\n#RTした人にやる\n\nお腹痛くて寝れないからやるww\nだれでもどうぞ〜😏🙌", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2347898072, - "id_str": "2347898072", - "name": "にたにた", - "screen_name": "syo6660129", - "location": "", - "description": "", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 64, - "friends_count": 70, - "listed_count": 1, - "created_at": "Mon Feb 17 04:29:46 +0000 2014", - "favourites_count": 58, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 145, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/485603672118669314/73uh_xRS_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/485603672118669314/73uh_xRS_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2347898072/1396957619", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sat Aug 30 14:19:31 +0000 2014", - "id": 505721480261300200, - "id_str": "505721480261300224", - "text": "呼び方:\n呼ばれ方:\n第一印象:\n今の印象:\n好きなところ:\n家族にするなら:\n最後に一言:\n#RTした人にやる\n\nお腹痛くて寝れないからやるww\nだれでもどうぞ〜😏🙌", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 856045488, - "id_str": "856045488", - "name": "なおぴす", - "screen_name": "naopisu_", - "location": "Fujino 65th ⇢ Sagaso 12A(LJK", - "description": "\ もうすぐ18歳 “Only One”になる /", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 267, - "friends_count": 259, - "listed_count": 2, - "created_at": "Mon Oct 01 08:36:23 +0000 2012", - "favourites_count": 218, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 1790, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/496321592553525249/tuzX9ByR_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/496321592553525249/tuzX9ByR_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/856045488/1407118111", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 23, - "favorite_count": 1, - "entities": { - "hashtags": [ - { - "text": "RTした人にやる", - "indices": [ - 47, - 56 - ] - } - ], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 23, - "favorite_count": 0, - "entities": { - "hashtags": [ - { - "text": "RTした人にやる", - "indices": [ - 61, - 70 - ] - } - ], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "naopisu_", - "name": "なおぴす", - "id": 856045488, - "id_str": "856045488", - "indices": [ - 3, - 12 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:06 +0000 2014", - "id": 505874885474656260, - "id_str": "505874885474656256", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "爆笑★LINE あるある", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2709561589, - "id_str": "2709561589", - "name": "爆笑★LINE あるある", - "screen_name": "line_aru1", - "location": "", - "description": "思わず笑ってしまうLINEでのやりとりや、あるあるを見つけたいです♪面白かったら RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 496, - "friends_count": 1875, - "listed_count": 1, - "created_at": "Tue Aug 05 15:01:30 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 687, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/496673793939492867/p1BN4YaW_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/496673793939492867/p1BN4YaW_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2709561589/1407251270", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874884627410940, - "id_str": "505874884627410944", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "全力★ミサワ的w発言", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2734455415, - "id_str": "2734455415", - "name": "全力★ミサワ的w発言!!", - "screen_name": "misawahatugen", - "location": "", - "description": "ウザすぎて笑えるミサワ的名言や、おもしろミサワ画像を集めています。 \r\nミサワを知らない人でも、いきなりツボにハマっちゃう内容をお届けします。 \r\nウザいwと思ったら RT & 相互フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 144, - "friends_count": 1915, - "listed_count": 1, - "created_at": "Fri Aug 15 13:20:04 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 436, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/500271070834749444/HvengMe5_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/500271070834749444/HvengMe5_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2734455415/1408108944", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874883809521660, - "id_str": "505874883809521664", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "お宝ww有名人卒アル特集", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2708183557, - "id_str": "2708183557", - "name": "お宝ww有名人卒アル特集", - "screen_name": "otakara_sotuaru", - "location": "", - "description": "みんな昔は若かったんですね。今からは想像もつかない、あの有名人を見つけます。\r\n面白かったら RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 286, - "friends_count": 1938, - "listed_count": 0, - "created_at": "Tue Aug 05 03:26:54 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 650, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/496499121276985344/hC8RoebP_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/496499121276985344/hC8RoebP_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2708183557/1407318758", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874883322970100, - "id_str": "505874883322970112", - "text": "レッドクリフのキャラのこと女装ってくそわろたwww朝一で面白かった( ˘ω゜)笑", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1620730616, - "id_str": "1620730616", - "name": "ひーちゃん@橘芋健ぴ", - "screen_name": "2nd_8hkr", - "location": "北の大地.95年組 ☞ 9/28.10/2(5).12/28", - "description": "THE SECOND/劇団EXILE/EXILE/二代目JSB ☞KENCHI.AKIRA.青柳翔.小森隼.石井杏奈☜ Big Love ♡ Respect ..... ✍ MATSU Origin✧ .た ち ば な '' い も '' け ん い ち ろ う さ んTEAM NACS 安田.戸次 Liebe !", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 109, - "friends_count": 148, - "listed_count": 0, - "created_at": "Thu Jul 25 16:09:29 +0000 2013", - "favourites_count": 783, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 9541, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/458760951060123648/Cocoxi-2_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/458760951060123648/Cocoxi-2_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1620730616/1408681982", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874883067129860, - "id_str": "505874883067129857", - "text": "【状態良好】ペンタックス・デジタル一眼レフカメラ・K20D 入札数=38 現在価格=15000円 http://t.co/4WK1f6V2n6終了=2014年08月31日 20:47:53 #一眼レフ http://t.co/PcSaXzfHMW", - "source": "YahooAuction Degicame", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2278053589, - "id_str": "2278053589", - "name": "AuctionCamera", - "screen_name": "AuctionCamera", - "location": "", - "description": "Yahooオークションのデジカメカテゴリから商品を抽出するボットです。", - "url": "https://t.co/3sB1NDnd0m", - "entities": { - "url": { - "urls": [ - { - "url": "https://t.co/3sB1NDnd0m", - "expanded_url": "https://github.com/AKB428/YahooAuctionBot", - "display_url": "github.com/AKB428/YahooAu…", - "indices": [ - 0, - 23 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 5, - "friends_count": 24, - "listed_count": 0, - "created_at": "Sun Jan 05 20:10:56 +0000 2014", - "favourites_count": 1, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 199546, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/419927606146789376/vko-kd6Q_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/419927606146789376/vko-kd6Q_normal.jpeg", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [ - { - "text": "一眼レフ", - "indices": [ - 95, - 100 - ] - } - ], - "symbols": [], - "urls": [ - { - "url": "http://t.co/4WK1f6V2n6", - "expanded_url": "http://atq.ck.valuecommerce.com/servlet/atq/referral?sid=2219441&pid=877510753&vcptn=auct/p/RJH492.PLqoLQQx1Jy8U9LE-&vc_url=http://page8.auctions.yahoo.co.jp/jp/auction/h192024356", - "display_url": "atq.ck.valuecommerce.com/servlet/atq/re…", - "indices": [ - 49, - 71 - ] - } - ], - "user_mentions": [], - "media": [ - { - "id": 505874882828046340, - "id_str": "505874882828046336", - "indices": [ - 101, - 123 - ], - "media_url": "http://pbs.twimg.com/media/BwU6hpPCEAAxnpq.jpg", - "media_url_https": "https://pbs.twimg.com/media/BwU6hpPCEAAxnpq.jpg", - "url": "http://t.co/PcSaXzfHMW", - "display_url": "pic.twitter.com/PcSaXzfHMW", - "expanded_url": "http://twitter.com/AuctionCamera/status/505874883067129857/photo/1", - "type": "photo", - "sizes": { - "large": { - "w": 600, - "h": 450, - "resize": "fit" - }, - "medium": { - "w": 600, - "h": 450, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "small": { - "w": 340, - "h": 255, - "resize": "fit" - } - } - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874882995826700, - "id_str": "505874882995826689", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "ヤバすぎる!!ギネス世界記録", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2762405780, - "id_str": "2762405780", - "name": "ヤバすぎる!!ギネス世界記録", - "screen_name": "yabai_giness", - "location": "", - "description": "世の中には、まだまだ知られていないスゴイ記録があるんです! \r\nそんなギネス世界記録を見つけます☆ \r\nどんどん友達にも教えてあげてくださいねww \r\nヤバイと思ったら RT & フォローを、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 36, - "friends_count": 985, - "listed_count": 0, - "created_at": "Sun Aug 24 13:17:03 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 210, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503531782919045121/NiIC25wL_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503531782919045121/NiIC25wL_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762405780/1408886328", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874882870009860, - "id_str": "505874882870009856", - "text": "すごく面白い夢見た。魔法科高校通ってて(別に一科二科の区別ない)クラスメイトにヨセアツメ面子や赤僕の拓也がいて、学校対抗合唱コンクールが開催されたり会場入りの際他校の妨害工作受けたり、拓也が連れてきてた実が人質に取られたりとにかくてんこ盛りだった楽しかった赤僕読みたい手元にない", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 597357105, - "id_str": "597357105", - "name": "ふじよし", - "screen_name": "fuji_mark", - "location": "多摩動物公園", - "description": "成人腐女子", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 128, - "friends_count": 126, - "listed_count": 6, - "created_at": "Sat Jun 02 10:06:05 +0000 2012", - "favourites_count": 2842, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": false, - "verified": false, - "statuses_count": 10517, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "0099B9", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme4/bg.gif", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme4/bg.gif", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503553738569560065/D_JW2dCJ_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503553738569560065/D_JW2dCJ_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/597357105/1408864355", - "profile_link_color": "0099B9", - "profile_sidebar_border_color": "5ED4DC", - "profile_sidebar_fill_color": "95E8EC", - "profile_text_color": "3C3940", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874882228281340, - "id_str": "505874882228281345", - "text": "RT @oen_yakyu: ●継続試合(中京対崇徳)46回~ 9時~\n 〈ラジオ中継〉\n らじる★らじる→大阪放送局を選択→NHK-FM\n●決勝戦(三浦対中京or崇徳) 12時30分~\n 〈ラジオ中継〉\n らじる★らじる→大阪放送局を選択→NHK第一\n ※神奈川の方は普通のラ…", - "source": "twicca", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 18477566, - "id_str": "18477566", - "name": "Natit(なち)@そうだ、トップ行こう", - "screen_name": "natit_yso", - "location": "福岡市の端っこ", - "description": "ヤー・チャイカ。紫宝勢の末席くらいでQMAやってます。\r\n9/13(土)「九州杯」今年も宜しくお願いします!キーワードは「そうだ、トップ、行こう。」\r\nmore → http://t.co/ezuHyjF4Qy \r\n【旅の予定】9/20-22 関西 → 9/23-28 北海道ぐるり", - "url": "http://t.co/ll2yu78DGR", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/ll2yu78DGR", - "expanded_url": "http://qma-kyushu.sakura.ne.jp/", - "display_url": "qma-kyushu.sakura.ne.jp", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [ - { - "url": "http://t.co/ezuHyjF4Qy", - "expanded_url": "http://twpf.jp/natit_yso", - "display_url": "twpf.jp/natit_yso", - "indices": [ - 83, - 105 - ] - } - ] - } - }, - "protected": false, - "followers_count": 591, - "friends_count": 548, - "listed_count": 93, - "created_at": "Tue Dec 30 14:11:44 +0000 2008", - "favourites_count": 11676, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": false, - "verified": false, - "statuses_count": 130145, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "131516", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/1556202861/chibi-Leon_normal.jpg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/1556202861/chibi-Leon_normal.jpg", - "profile_link_color": "009999", - "profile_sidebar_border_color": "EEEEEE", - "profile_sidebar_fill_color": "EFEFEF", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sat Aug 30 23:12:39 +0000 2014", - "id": 505855649196953600, - "id_str": "505855649196953600", - "text": "●継続試合(中京対崇徳)46回~ 9時~\n 〈ラジオ中継〉\n らじる★らじる→大阪放送局を選択→NHK-FM\n●決勝戦(三浦対中京or崇徳) 12時30分~\n 〈ラジオ中継〉\n らじる★らじる→大阪放送局を選択→NHK第一\n ※神奈川の方は普通のラジオのNHK-FMでも", - "source": "Twitter Web Client", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2761692762, - "id_str": "2761692762", - "name": "三浦学苑軟式野球部応援団!", - "screen_name": "oen_yakyu", - "location": "", - "description": "兵庫県で開催される「もう一つの甲子園」こと全国高校軟式野球選手権大会に南関東ブロックから出場する三浦学苑軟式野球部を応援する非公式アカウントです。", - "url": "http://t.co/Cn1tPTsBGY", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/Cn1tPTsBGY", - "expanded_url": "http://www.miura.ed.jp/index.html", - "display_url": "miura.ed.jp/index.html", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 464, - "friends_count": 117, - "listed_count": 4, - "created_at": "Sun Aug 24 07:47:29 +0000 2014", - "favourites_count": 69, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 553, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/504299474445811712/zsxJUmL0_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/504299474445811712/zsxJUmL0_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2761692762/1409069337", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 7, - "favorite_count": 2, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 7, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "oen_yakyu", - "name": "三浦学苑軟式野球部応援団!", - "id": 2761692762, - "id_str": "2761692762", - "indices": [ - 3, - 13 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874882110824450, - "id_str": "505874882110824448", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "スマホに密封★アニメ画像", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2725976444, - "id_str": "2725976444", - "name": "スマホに密封★アニメ画像", - "screen_name": "sumahoanime", - "location": "", - "description": "なんともめずらしい、いろんなキャラがスマホに閉じ込められています。 \r\nあなたのスマホにマッチする画像が見つかるかも♪ \r\n気に入ったら是非 RT & フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 227, - "friends_count": 1918, - "listed_count": 0, - "created_at": "Tue Aug 12 11:27:54 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 527, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/499155646164393984/l5vSz5zu_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/499155646164393984/l5vSz5zu_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2725976444/1407843121", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:05 +0000 2014", - "id": 505874881297133600, - "id_str": "505874881297133568", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "アナタのそばの身近な危険", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2713926078, - "id_str": "2713926078", - "name": "アナタのそばの身近な危険", - "screen_name": "mijika_kiken", - "location": "", - "description": "知らないうちにやっている危険な行動を見つけて自分を守りましょう。 役に立つと思ったら RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 301, - "friends_count": 1871, - "listed_count": 0, - "created_at": "Thu Aug 07 07:12:50 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 644, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/497279579245907968/Ftvms_HR_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/497279579245907968/Ftvms_HR_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2713926078/1407395683", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:04 +0000 2014", - "id": 505874880294682600, - "id_str": "505874880294682624", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "人気者♥デイジー大好き", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2726199583, - "id_str": "2726199583", - "name": "人気者♥デイジー大好き", - "screen_name": "ninkimono_daosy", - "location": "", - "description": "デイジーの想いを、代わりにつぶやきます♪ \r\nデイジーのかわいい画像やグッズも大好きw \r\n可愛いと思ったら RT & フォローお願いします。 \r\n「非公式アカウントです」", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 190, - "friends_count": 474, - "listed_count": 0, - "created_at": "Tue Aug 12 12:58:33 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 469, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/499178622494576640/EzWKdR_p_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/499178622494576640/EzWKdR_p_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2726199583/1407848478", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:04 +0000 2014", - "id": 505874879392919550, - "id_str": "505874879392919552", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "幸せ話でフル充電しよう", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2721453846, - "id_str": "2721453846", - "name": "幸せ話でフル充電しようww", - "screen_name": "shiawasehanashi", - "location": "", - "description": "私が聞いて心に残った感動エピソードをお届けします。\r\n少しでも多くの人へ届けたいと思います。\r\nいいなと思ったら RT & フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 302, - "friends_count": 1886, - "listed_count": 0, - "created_at": "Sun Aug 10 12:16:25 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 508, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498444554916216832/ml8EiQka_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498444554916216832/ml8EiQka_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2721453846/1407673555", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:04 +0000 2014", - "id": 505874879103520800, - "id_str": "505874879103520768", - "text": "RT @Ang_Angel73: 逢坂「くっ…僕の秘められし右目が…!」\n一同「……………。」", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2571968509, - "id_str": "2571968509", - "name": "イイヒト", - "screen_name": "IwiAlohomora", - "location": "草葉の陰", - "description": "大人です。気軽に絡んでくれるとうれしいです! イラスト大好き!(≧∇≦) BF(仮)逢坂紘夢くんにお熱です! マンガも好き♡欲望のままにつぶやきますのでご注意を。雑食♡", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 156, - "friends_count": 165, - "listed_count": 14, - "created_at": "Tue Jun 17 01:18:34 +0000 2014", - "favourites_count": 11926, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 7234, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/504990074862178304/DoBvOb9c_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/504990074862178304/DoBvOb9c_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2571968509/1409106012", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:27:01 +0000 2014", - "id": 505874364596621300, - "id_str": "505874364596621313", - "text": "逢坂「くっ…僕の秘められし右目が…!」\n一同「……………。」", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1600750194, - "id_str": "1600750194", - "name": "臙脂", - "screen_name": "Ang_Angel73", - "location": "逢坂紘夢のそばに", - "description": "自由、気ままに。詳しくはツイプロ。アイコンはまめせろりちゃんからだよ☆~(ゝ。∂)", - "url": "http://t.co/kKCCwHTaph", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/kKCCwHTaph", - "expanded_url": "http://twpf.jp/Ang_Angel73", - "display_url": "twpf.jp/Ang_Angel73", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 155, - "friends_count": 154, - "listed_count": 10, - "created_at": "Wed Jul 17 11:44:31 +0000 2013", - "favourites_count": 2115, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": false, - "verified": false, - "statuses_count": 12342, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/378800000027871001/aa764602922050b22bf9ade3741367dc.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/378800000027871001/aa764602922050b22bf9ade3741367dc.jpeg", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/500293786287603713/Ywyh69eG_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/500293786287603713/Ywyh69eG_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1600750194/1403879183", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "FFFFFF", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 2, - "favorite_count": 2, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 2, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "Ang_Angel73", - "name": "臙脂", - "id": 1600750194, - "id_str": "1600750194", - "indices": [ - 3, - 15 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:04 +0000 2014", - "id": 505874877933314050, - "id_str": "505874877933314048", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "秘密の本音♥女子編", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2762237088, - "id_str": "2762237088", - "name": "秘密の本音♥女子編", - "screen_name": "honne_jyoshi1", - "location": "", - "description": "普段は言えない「お・ん・なの建前と本音」をつぶやきます。 気になる あの人の本音も、わかるかも!? \r\nわかるって人は RT & フォローを、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 123, - "friends_count": 988, - "listed_count": 0, - "created_at": "Sun Aug 24 12:27:07 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 211, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503519190364332032/BVjS_XBD_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503519190364332032/BVjS_XBD_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2762237088/1408883328", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:04 +0000 2014", - "id": 505874877148958700, - "id_str": "505874877148958721", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "美し過ぎる★色鉛筆アート", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2740047343, - "id_str": "2740047343", - "name": "美し過ぎる★色鉛筆アート", - "screen_name": "bi_iroenpitu", - "location": "", - "description": "ほんとにコレ色鉛筆なの~? \r\n本物と見間違える程のリアリティを御覧ください。 \r\n気に入ったら RT & 相互フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 321, - "friends_count": 1990, - "listed_count": 0, - "created_at": "Sun Aug 17 16:15:05 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 396, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501039950972739585/isigil4V_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501039950972739585/isigil4V_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2740047343/1408292283", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874876465295360, - "id_str": "505874876465295361", - "text": "【H15-9-4】道路を利用する利益は反射的利益であり、建築基準法に基づいて道路一の指定がなされている私道の敷地所有者に対し、通行妨害行為の排除を求める人格的権利を認めることはできない。→誤。", - "source": "twittbot.net", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1886570281, - "id_str": "1886570281", - "name": "行政法過去問", - "screen_name": "gyosei_goukaku", - "location": "", - "description": "行政書士の本試験問題の過去問(行政法分野)をランダムにつぶやきます。問題は随時追加中です。基本的に相互フォローします。※140字制限の都合上、表現は一部変えてあります。解説も文字数が可能であればなるべく…。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 1554, - "friends_count": 1772, - "listed_count": 12, - "created_at": "Fri Sep 20 13:24:29 +0000 2013", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 14565, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/378800000487791870/0e45e3c089c6b641cdd8d1b6f1ceb8a4_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000487791870/0e45e3c089c6b641cdd8d1b6f1ceb8a4_normal.jpeg", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874876318511100, - "id_str": "505874876318511104", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "K点越えの発想力!!", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2744863153, - "id_str": "2744863153", - "name": "K点越えの発想力!!", - "screen_name": "kgoehassou", - "location": "", - "description": "いったいどうやったら、その領域にたどりつけるのか!? \r\nそんな思わず笑ってしまう別世界の発想力をお届けします♪ \r\nおもしろかったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 76, - "friends_count": 957, - "listed_count": 0, - "created_at": "Tue Aug 19 13:00:08 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 341, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501715651686178816/Fgpe0B8M_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501715651686178816/Fgpe0B8M_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744863153/1408453328", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874875521581060, - "id_str": "505874875521581056", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "血液型の真実2", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2698625690, - "id_str": "2698625690", - "name": "血液型の真実", - "screen_name": "ketueki_sinjitu", - "location": "", - "description": "やっぱりそうだったのか~♪\r\n意外な、あの人の裏側を見つけます。\r\n面白かったらRT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 193, - "friends_count": 1785, - "listed_count": 1, - "created_at": "Fri Aug 01 16:11:40 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 769, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/495241446706790400/h_0DSFPG_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/495241446706790400/h_0DSFPG_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2698625690/1406911319", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874874712072200, - "id_str": "505874874712072192", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "やっぱり神が??を作る時", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2714868440, - "id_str": "2714868440", - "name": "やっぱり神が??を作る時", - "screen_name": "yahari_kamiga", - "location": "", - "description": "やっぱり今日も、神は何かを作ろうとしています 笑。 どうやって作っているのかわかったら RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 243, - "friends_count": 1907, - "listed_count": 0, - "created_at": "Thu Aug 07 16:12:33 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 590, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/497416102108884992/NRMEbKaT_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/497416102108884992/NRMEbKaT_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714868440/1407428237", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874874275864600, - "id_str": "505874874275864576", - "text": "RT @takuramix: 福島第一原発の構内地図がこちら。\nhttp://t.co/ZkU4TZCGPG\nどう見ても、1号機。\nRT @Lightworker19: 【大拡散】  福島第一原発 4号機 爆発動画 40秒~  http://t.co/lmlgp38fgZ", - "source": "ツイタマ", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 62525372, - "id_str": "62525372", - "name": "NANCY-MOON☆ひよこちゃん☆", - "screen_name": "nancy_moon_703", - "location": "JAPAN", - "description": "【無断転載禁止・コピペ禁止・非公式RT禁止】【必読!】⇒ http://t.co/nuUvfUVD 今現在活動中の東方神起YUNHO&CHANGMINの2人を全力で応援しています!!(^_-)-☆ ※東方神起及びYUNHO&CHANGMINを応援していない方・鍵付ユーザーのフォローお断り!", - "url": null, - "entities": { - "description": { - "urls": [ - { - "url": "http://t.co/nuUvfUVD", - "expanded_url": "http://goo.gl/SrGLb", - "display_url": "goo.gl/SrGLb", - "indices": [ - 29, - 49 - ] - } - ] - } - }, - "protected": false, - "followers_count": 270, - "friends_count": 328, - "listed_count": 4, - "created_at": "Mon Aug 03 14:22:24 +0000 2009", - "favourites_count": 3283, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": false, - "verified": false, - "statuses_count": 180310, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "642D8B", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/470849781397336064/ltM6EdFn.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/470849781397336064/ltM6EdFn.jpeg", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/3699005246/9ba2e306518d296b68b7cbfa5e4ce4e6_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/3699005246/9ba2e306518d296b68b7cbfa5e4ce4e6_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/62525372/1401094223", - "profile_link_color": "FF0000", - "profile_sidebar_border_color": "FFFFFF", - "profile_sidebar_fill_color": "F065A8", - "profile_text_color": "080808", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sat Aug 30 21:21:33 +0000 2014", - "id": 505827689660313600, - "id_str": "505827689660313600", - "text": "福島第一原発の構内地図がこちら。\nhttp://t.co/ZkU4TZCGPG\nどう見ても、1号機。\nRT @Lightworker19: 【大拡散】  福島第一原発 4号機 爆発動画 40秒~  http://t.co/lmlgp38fgZ", - "source": "TweetDeck", - "truncated": false, - "in_reply_to_status_id": 505774460910043140, - "in_reply_to_status_id_str": "505774460910043136", - "in_reply_to_user_id": 238157843, - "in_reply_to_user_id_str": "238157843", - "in_reply_to_screen_name": "Lightworker19", - "user": { - "id": 29599253, - "id_str": "29599253", - "name": "タクラミックス", - "screen_name": "takuramix", - "location": "i7", - "description": "私の機能一覧:歌う、演劇、ネットワークエンジニア、ライター、プログラマ、翻訳、シルバーアクセサリ、……何をやってる人かは良くわからない人なので、「機能」が欲しい人は私にがっかりするでしょう。私って人間に御用があるなら別ですが。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 5136, - "friends_count": 724, - "listed_count": 335, - "created_at": "Wed Apr 08 01:10:58 +0000 2009", - "favourites_count": 21363, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": false, - "verified": false, - "statuses_count": 70897, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/2049751947/takuramix1204_normal.jpg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/2049751947/takuramix1204_normal.jpg", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 1, - "favorite_count": 1, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/ZkU4TZCGPG", - "expanded_url": "http://www.tepco.co.jp/nu/fukushima-np/review/images/review1_01.gif", - "display_url": "tepco.co.jp/nu/fukushima-n…", - "indices": [ - 17, - 39 - ] - }, - { - "url": "http://t.co/lmlgp38fgZ", - "expanded_url": "http://youtu.be/gDXEhyuVSDk", - "display_url": "youtu.be/gDXEhyuVSDk", - "indices": [ - 99, - 121 - ] - } - ], - "user_mentions": [ - { - "screen_name": "Lightworker19", - "name": "Lightworker", - "id": 238157843, - "id_str": "238157843", - "indices": [ - 54, - 68 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - "retweet_count": 1, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/ZkU4TZCGPG", - "expanded_url": "http://www.tepco.co.jp/nu/fukushima-np/review/images/review1_01.gif", - "display_url": "tepco.co.jp/nu/fukushima-n…", - "indices": [ - 32, - 54 - ] - }, - { - "url": "http://t.co/lmlgp38fgZ", - "expanded_url": "http://youtu.be/gDXEhyuVSDk", - "display_url": "youtu.be/gDXEhyuVSDk", - "indices": [ - 114, - 136 - ] - } - ], - "user_mentions": [ - { - "screen_name": "takuramix", - "name": "タクラミックス", - "id": 29599253, - "id_str": "29599253", - "indices": [ - 3, - 13 - ] - }, - { - "screen_name": "Lightworker19", - "name": "Lightworker", - "id": 238157843, - "id_str": "238157843", - "indices": [ - 69, - 83 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874873961308160, - "id_str": "505874873961308160", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "やっぱりアナ雪が好き♥", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2714052962, - "id_str": "2714052962", - "name": "やっぱりアナ雪が好き♥", - "screen_name": "anayuki_suki", - "location": "", - "description": "なんだかんだ言ってもやっぱりアナ雪が好きなんですよね~♪ \r\n私も好きって人は RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 368, - "friends_count": 1826, - "listed_count": 1, - "created_at": "Thu Aug 07 08:29:13 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 670, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/497299646662705153/KMo3gkv7_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/497299646662705153/KMo3gkv7_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2714052962/1407400477", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "zh" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874873759977500, - "id_str": "505874873759977473", - "text": "四川盆地江淮等地将有强降雨 开学日多地将有雨:   中新网8月31日电 据中央气象台消息,江淮东部、四川盆地东北部等地今天(31日)又将迎来一场暴雨或大暴雨天气。明天9月1日,是中小学生开学的日子。预计明天,内蒙古中部、... http://t.co/toQgVlXPyH", - "source": "twitterfeed", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2281979863, - "id_str": "2281979863", - "name": "News 24h China", - "screen_name": "news24hchn", - "location": "", - "description": "", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 719, - "friends_count": 807, - "listed_count": 7, - "created_at": "Wed Jan 08 10:56:04 +0000 2014", - "favourites_count": 0, - "utc_offset": 7200, - "time_zone": "Amsterdam", - "geo_enabled": false, - "verified": false, - "statuses_count": 94782, - "lang": "it", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/452558963754561536/QPID3isM.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/452558963754561536/QPID3isM.jpeg", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/439031926569979904/SlBH9iMg_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/439031926569979904/SlBH9iMg_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2281979863/1393508427", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "FFFFFF", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/toQgVlXPyH", - "expanded_url": "http://news24h.allnews24h.com/FX54", - "display_url": "news24h.allnews24h.com/FX54", - "indices": [ - 114, - 136 - ] - } - ], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "zh" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874873248268300, - "id_str": "505874873248268288", - "text": "@Take3carnifex それは大変!一大事!命に関わります!\n是非うちに受診して下さい!", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": 505874353716600800, - "in_reply_to_status_id_str": "505874353716600832", - "in_reply_to_user_id": 535179785, - "in_reply_to_user_id_str": "535179785", - "in_reply_to_screen_name": "Take3carnifex", - "user": { - "id": 226897125, - "id_str": "226897125", - "name": "ひかり@hack", - "screen_name": "hikari_thirteen", - "location": "", - "description": "hackというバンドで、ギターを弾いています。 モンハンとポケモンが好き。 \nSPRING WATER リードギター(ヘルプ)\nROCK OUT レギュラーDJ", - "url": "http://t.co/SQLZnvjVxB", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/SQLZnvjVxB", - "expanded_url": "http://s.ameblo.jp/hikarihikarimay", - "display_url": "s.ameblo.jp/hikarihikarimay", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 296, - "friends_count": 348, - "listed_count": 3, - "created_at": "Wed Dec 15 10:51:51 +0000 2010", - "favourites_count": 33, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": false, - "verified": false, - "statuses_count": 3293, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "131516", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme14/bg.gif", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme14/bg.gif", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/378800000504584690/8ccba98eda8c0fd1d15a74e401f621d1_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000504584690/8ccba98eda8c0fd1d15a74e401f621d1_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/226897125/1385551752", - "profile_link_color": "009999", - "profile_sidebar_border_color": "EEEEEE", - "profile_sidebar_fill_color": "EFEFEF", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "Take3carnifex", - "name": "Take3", - "id": 535179785, - "id_str": "535179785", - "indices": [ - 0, - 14 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:03 +0000 2014", - "id": 505874873223110660, - "id_str": "505874873223110656", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "今どき女子高生の謎w", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2744236873, - "id_str": "2744236873", - "name": "今どき女子高生の謎w", - "screen_name": "imadokijoshiko", - "location": "", - "description": "思わず耳を疑う男性の方の夢を壊してしまう、\r\n女子高生達のディープな世界を見てください☆ \r\nおもしろいと思ったら RT & 相互フォローでお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 79, - "friends_count": 973, - "listed_count": 0, - "created_at": "Tue Aug 19 07:06:47 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 354, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501627015980535808/avWBgkDh_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501627015980535808/avWBgkDh_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744236873/1408432455", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874872463925250, - "id_str": "505874872463925248", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "私の理想の男性像", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2761782601, - "id_str": "2761782601", - "name": "私の理想の男性像", - "screen_name": "risou_dansei", - "location": "", - "description": "こんな男性♥ ほんとにいるのかしら!? \r\n「いたらいいのになぁ」っていう理想の男性像をを、私目線でつぶやきます。 \r\nいいなと思った人は RT & フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 69, - "friends_count": 974, - "listed_count": 0, - "created_at": "Sun Aug 24 08:03:32 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 208, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503452833719410688/tFU509Yk_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503452833719410688/tFU509Yk_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2761782601/1408867519", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874871713157100, - "id_str": "505874871713157120", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "激アツ★6秒動画", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2725690658, - "id_str": "2725690658", - "name": "激アツ★6秒動画", - "screen_name": "gekiatu_6byou", - "location": "", - "description": "話題の6秒動画! \r\n思わず「ほんとかよっ」てツッコんでしまう内容のオンパレード! \r\nおもしろかったら、是非 RT & フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 195, - "friends_count": 494, - "listed_count": 0, - "created_at": "Tue Aug 12 08:17:29 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 477, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/499107997444886528/3rl6FrIk_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/499107997444886528/3rl6FrIk_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2725690658/1407832963", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874871616671740, - "id_str": "505874871616671744", - "text": "爆笑ww珍解答集!\n先生のツメの甘さと生徒のセンスを感じる一問一答だとFBでも話題!!\nうどん天下一決定戦ウィンドウズ9三重高校竹内由恵アナ花火保険\nhttp://t.co/jRWJt8IrSB http://t.co/okrAoxSbt0", - "source": "笑える博物館", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2748747362, - "id_str": "2748747362", - "name": "笑える博物館", - "screen_name": "waraeru_kan", - "location": "", - "description": "", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 19, - "friends_count": 10, - "listed_count": 0, - "created_at": "Wed Aug 20 11:11:04 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 15137, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://abs.twimg.com/sticky/default_profile_images/default_profile_4_normal.png", - "profile_image_url_https": "https://abs.twimg.com/sticky/default_profile_images/default_profile_4_normal.png", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": true, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/jRWJt8IrSB", - "expanded_url": "http://bit.ly/1qBa1nl", - "display_url": "bit.ly/1qBa1nl", - "indices": [ - 75, - 97 - ] - } - ], - "user_mentions": [], - "media": [ - { - "id": 505874871344066560, - "id_str": "505874871344066560", - "indices": [ - 98, - 120 - ], - "media_url": "http://pbs.twimg.com/media/BwU6g-dCcAALxAW.png", - "media_url_https": "https://pbs.twimg.com/media/BwU6g-dCcAALxAW.png", - "url": "http://t.co/okrAoxSbt0", - "display_url": "pic.twitter.com/okrAoxSbt0", - "expanded_url": "http://twitter.com/waraeru_kan/status/505874871616671744/photo/1", - "type": "photo", - "sizes": { - "small": { - "w": 340, - "h": 425, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "large": { - "w": 600, - "h": 750, - "resize": "fit" - }, - "medium": { - "w": 600, - "h": 750, - "resize": "fit" - } - } - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874871268540400, - "id_str": "505874871268540416", - "text": "@nasan_arai \n名前→なーさん\n第一印象→誰。(´・_・`)\n今の印象→れいら♡\nLINE交換できる?→してる(「・ω・)「\n好きなところ→可愛い優しい優しい優しい\n最後に一言→なーさん好き〜(´・_・`)♡GEM現場おいでね(´・_・`)♡\n\n#ふぁぼした人にやる", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": 1717603286, - "in_reply_to_user_id_str": "1717603286", - "in_reply_to_screen_name": "nasan_arai", - "user": { - "id": 2417626784, - "id_str": "2417626784", - "name": "✩.ゆきଘ(*´꒳`)", - "screen_name": "Ymaaya_gem", - "location": "", - "description": "⁽⁽٩( ᐖ )۶⁾⁾ ❤︎ 武 田 舞 彩 ❤︎ ₍₍٩( ᐛ )۶₎₎", - "url": "http://t.co/wR0Qb76TbB", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/wR0Qb76TbB", - "expanded_url": "http://twpf.jp/Ymaaya_gem", - "display_url": "twpf.jp/Ymaaya_gem", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 198, - "friends_count": 245, - "listed_count": 1, - "created_at": "Sat Mar 29 16:03:06 +0000 2014", - "favourites_count": 3818, - "utc_offset": null, - "time_zone": null, - "geo_enabled": true, - "verified": false, - "statuses_count": 8056, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/505516858816987136/4gFGjHzu_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/505516858816987136/4gFGjHzu_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2417626784/1407764793", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [ - { - "text": "ふぁぼした人にやる", - "indices": [ - 128, - 138 - ] - } - ], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "nasan_arai", - "name": "なーさん", - "id": 1717603286, - "id_str": "1717603286", - "indices": [ - 0, - 11 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874871218225150, - "id_str": "505874871218225152", - "text": "\"ソードマスター\"剣聖カミイズミ (CV:緑川光)-「ソードマスター」のアスタリスク所持者\n第一師団団長にして「剣聖」の称号を持つ剣士。イデアの剣の師匠。 \n敵味方からも尊敬される一流の武人。", - "source": "twittbot.net", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1435517814, - "id_str": "1435517814", - "name": "俺、関係ないよ?", - "screen_name": "BDFF_LOVE", - "location": "ルクセンダルクorリングアベルさんの隣", - "description": "自分なりに生きる人、最後まであきらめないの。でも、フォローありがとう…。@ringo_BDFFLOVE ←は、妹です。時々、会話します。「現在BOTで、BDFFのこと呟くよ!」夜は、全滅 「BDFFプレイ中」詳しくは、ツイプロみてください!(絶対)", - "url": "http://t.co/5R4dzpbWX2", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/5R4dzpbWX2", - "expanded_url": "http://twpf.jp/BDFF_LOVE", - "display_url": "twpf.jp/BDFF_LOVE", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 1066, - "friends_count": 1799, - "listed_count": 6, - "created_at": "Fri May 17 12:33:23 +0000 2013", - "favourites_count": 1431, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": true, - "verified": false, - "statuses_count": 6333, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/505696320380612608/qvaxb_zx_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/505696320380612608/qvaxb_zx_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1435517814/1409401948", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874871130136600, - "id_str": "505874871130136576", - "text": "闇「リンと付き合うに当たって歳の差以外にもいろいろ壁があったんだよ。愛し隊の妨害とか風紀厨の生徒会長とか…」\n一号「リンちゃんを泣かせたらシメるかんね!」\n二号「リンちゃんにやましい事したら×す…」\n執行部「不純な交際は僕が取り締まろうじゃないか…」\n闇「(消される)」", - "source": "twittbot.net", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2386208737, - "id_str": "2386208737", - "name": "闇未来Bot", - "screen_name": "StxRinFbot", - "location": "DIVAルーム", - "description": "ProjectDIVAのモジュール・ストレンジダーク×鏡音リンFutureStyleの自己満足非公式Bot マセレン仕様。CP要素あります。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 7, - "friends_count": 2, - "listed_count": 0, - "created_at": "Thu Mar 13 02:58:09 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 4876, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/443948925351755776/6rmljL5C_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/443948925351755776/6rmljL5C_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2386208737/1396259004", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874870933016600, - "id_str": "505874870933016576", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "絶品!!スイーツ天国", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2725681663, - "id_str": "2725681663", - "name": "絶品!!スイーツ天国", - "screen_name": "suitestengoku", - "location": "", - "description": "美味しそうなスイーツって、見てるだけで幸せな気分になれますね♪\r\nそんな素敵なスイーツに出会いたいです。\r\n食べたいと思ったら是非 RT & フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 401, - "friends_count": 1877, - "listed_count": 1, - "created_at": "Tue Aug 12 07:43:52 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 554, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/499099533507178496/g5dNpArt_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/499099533507178496/g5dNpArt_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2725681663/1407829743", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874870148669440, - "id_str": "505874870148669440", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "電車厳禁!!おもしろ話", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2699667800, - "id_str": "2699667800", - "name": "電車厳禁!!おもしろ話w", - "screen_name": "dengeki_omoro", - "location": "", - "description": "日常のオモシロくて笑える場面を探します♪\r\n面白かったらRT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 461, - "friends_count": 1919, - "listed_count": 0, - "created_at": "Sat Aug 02 02:16:32 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 728, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/495400387961036800/BBMb_hcG_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/495400387961036800/BBMb_hcG_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2699667800/1406947654", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874869339189250, - "id_str": "505874869339189249", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "笑えるwwランキング2", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2695745652, - "id_str": "2695745652", - "name": "笑えるwwランキング", - "screen_name": "wara_runk", - "location": "", - "description": "知ってると使えるランキングを探そう。\r\n面白かったらRT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 314, - "friends_count": 1943, - "listed_count": 1, - "created_at": "Thu Jul 31 13:51:57 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 737, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/494844659856728064/xBQfnm5J_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/494844659856728064/xBQfnm5J_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2695745652/1406815103", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:02 +0000 2014", - "id": 505874868533854200, - "id_str": "505874868533854209", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "スニーカー大好き★図鑑", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2707963890, - "id_str": "2707963890", - "name": "スニーカー大好き★図鑑", - "screen_name": "sunikar_daisuki", - "location": "", - "description": "スニーカー好きを見つけて仲間になろう♪\r\n気に入ったら RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 394, - "friends_count": 1891, - "listed_count": 0, - "created_at": "Tue Aug 05 01:54:28 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 642, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/496474952631996416/f0C_u3_u_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/496474952631996416/f0C_u3_u_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2707963890/1407203869", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "zh" - }, - "created_at": "Sun Aug 31 00:29:01 +0000 2014", - "id": 505874867997380600, - "id_str": "505874867997380608", - "text": "\"@BelloTexto: ¿Quieres ser feliz? \n一\"No stalkees\" \n一\"No stalkees\" \n一\"No stalkees\" \n一\"No stalkees\" \n一\"No stalkees\" \n一\"No stalkees\".\"", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2249378935, - "id_str": "2249378935", - "name": "Maggie Becerril ", - "screen_name": "maggdesie", - "location": "", - "description": "cambiando la vida de las personas.", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 120, - "friends_count": 391, - "listed_count": 0, - "created_at": "Mon Dec 16 21:56:49 +0000 2013", - "favourites_count": 314, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 1657, - "lang": "es", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/505093371665604608/K0x_LV2y_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/505093371665604608/K0x_LV2y_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2249378935/1409258479", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "BelloTexto", - "name": "Indirectas... ✉", - "id": 833083404, - "id_str": "833083404", - "indices": [ - 1, - 12 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "zh" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:01 +0000 2014", - "id": 505874867720183800, - "id_str": "505874867720183808", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "ザ・異性の裏の顔", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2719746578, - "id_str": "2719746578", - "name": "ザ・異性の裏の顔", - "screen_name": "iseiuragao", - "location": "", - "description": "異性について少し学ぶことで、必然的にモテるようになる!? 相手を理解することで見えてくるもの「それは・・・●●」 いい内容だと思ったら RT & フォローもお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 238, - "friends_count": 1922, - "listed_count": 0, - "created_at": "Sat Aug 09 17:18:43 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 532, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498157077726900224/tW8q4di__normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498157077726900224/tW8q4di__normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2719746578/1407604947", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:01 +0000 2014", - "id": 505874866910687200, - "id_str": "505874866910687233", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "超w美女☆アルバム", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2744054334, - "id_str": "2744054334", - "name": "超w美女☆アルバム", - "screen_name": "bijyoalbum", - "location": "", - "description": "「おお~っ!いいね~」って、思わず言ってしまう、美女を見つけます☆ \r\nタイプだと思ったら RT & 相互フォローでお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 45, - "friends_count": 966, - "listed_count": 0, - "created_at": "Tue Aug 19 05:36:48 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 352, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501604413312491520/GP66eKWr_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501604413312491520/GP66eKWr_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744054334/1408426814", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:01 +0000 2014", - "id": 505874866105376800, - "id_str": "505874866105376769", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "男に見せない女子の裏生態", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2744261238, - "id_str": "2744261238", - "name": "男に見せない女子の裏生態", - "screen_name": "jyoshiuraseitai", - "location": "", - "description": "男の知らない女子ならではのあるある☆ \r\nそんな生々しい女子の生態をつぶやきます。 \r\nわかる~って人は RT & フォローでお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 203, - "friends_count": 967, - "listed_count": 0, - "created_at": "Tue Aug 19 08:01:28 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 348, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501641354804346880/Uh1-n1LD_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501641354804346880/Uh1-n1LD_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744261238/1408435630", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:01 +0000 2014", - "id": 505874865354584060, - "id_str": "505874865354584064", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "驚きの動物たちの生態", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2759403146, - "id_str": "2759403146", - "name": "驚きの動物たちの生態", - "screen_name": "soubutu_seitai", - "location": "", - "description": "「おお~っ」と 言われるような、動物の生態をツイートします♪ \r\n知っていると、あなたも人気者に!? \r\nおもしろかったら RT & フォローを、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 67, - "friends_count": 954, - "listed_count": 0, - "created_at": "Sat Aug 23 16:39:31 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 219, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503220468128567296/Z8mGDIBS_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503220468128567296/Z8mGDIBS_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2759403146/1408812130", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:01 +0000 2014", - "id": 505874864603820000, - "id_str": "505874864603820032", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "モテ女子★ファションの秘密", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2706659820, - "id_str": "2706659820", - "name": "モテ女子★ファションの秘密", - "screen_name": "mote_woman", - "location": "", - "description": "オシャレかわいい♥モテ度UPの注目アイテムを見つけます。\r\n気に入ったら RT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 217, - "friends_count": 1806, - "listed_count": 0, - "created_at": "Mon Aug 04 14:30:24 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 682, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/496303370936668161/s7xP8rTy_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/496303370936668161/s7xP8rTy_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2706659820/1407163059", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:00 +0000 2014", - "id": 505874863874007040, - "id_str": "505874863874007040", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "男女の違いを解明する", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2761896468, - "id_str": "2761896468", - "name": "男女の違いを解明する", - "screen_name": "danjyonotigai1", - "location": "", - "description": "意外と理解できていない男女それぞれの事情。 \r\n「えっ マジで!?」と驚くような、男女の習性をつぶやきます♪ ためになったら、是非 RT & フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 82, - "friends_count": 992, - "listed_count": 0, - "created_at": "Sun Aug 24 09:47:44 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 237, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503479057380413441/zDLu5Z9o_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503479057380413441/zDLu5Z9o_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2761896468/1408873803", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:00 +0000 2014", - "id": 505874862900924400, - "id_str": "505874862900924416", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "神レベル★極限の発想", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2744950735, - "id_str": "2744950735", - "name": "神レベル★極限の発想", - "screen_name": "kamihassou", - "location": "", - "description": "見ているだけで、本気がビシバシ伝わってきます! \r\n人生のヒントになるような、そんな究極の発想を集めています。 \r\nいいなと思ったら RT & 相互フォローで、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 84, - "friends_count": 992, - "listed_count": 0, - "created_at": "Tue Aug 19 13:36:05 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 343, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501725053189226496/xZNOTYz2_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501725053189226496/xZNOTYz2_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744950735/1408455571", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:00 +0000 2014", - "id": 505874862397591550, - "id_str": "505874862397591552", - "text": "@kaoritoxx そうよ!あたしはそう思うようにしておる。いま職場一やけとる気がする(°_°)!満喫幸せ焼け!!wあー、なるほどね!毎回そうだよね!ティアラちゃんみにいってるもんね♡五月と九月恐ろしい、、、\nハリポタエリアはいった??", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": 505838547308277760, - "in_reply_to_status_id_str": "505838547308277761", - "in_reply_to_user_id": 796000214, - "in_reply_to_user_id_str": "796000214", - "in_reply_to_screen_name": "kaoritoxx", - "user": { - "id": 2256249487, - "id_str": "2256249487", - "name": "はあちゃん@海賊同盟中", - "screen_name": "onepiece_24", - "location": "どえすえろぉたんの助手兼ね妹(願望)", - "description": "ONE PIECE愛しすぎて今年23ちゃい(歴14年目)ゾロ様に一途だったのにロー、このやろー。ロビンちゃんが幸せになればいい。ルフィは無条件にすき。ゾロビン、ローロビ、ルロビ♡usj、声優さん、コナン、進撃、クレしん、H x Hも好き♩", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 415, - "friends_count": 384, - "listed_count": 3, - "created_at": "Sat Dec 21 09:37:25 +0000 2013", - "favourites_count": 1603, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 9636, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501686340564418561/hMQFN4vD_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501686340564418561/hMQFN4vD_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2256249487/1399987924", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "kaoritoxx", - "name": "かおちゃん", - "id": 796000214, - "id_str": "796000214", - "indices": [ - 0, - 10 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:00 +0000 2014", - "id": 505874861973991400, - "id_str": "505874861973991424", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "恋愛仙人", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2698885082, - "id_str": "2698885082", - "name": "恋愛仙人", - "screen_name": "renai_sennin", - "location": "", - "description": "豊富でステキな恋愛経験を、シェアしましょう。\r\n面白かったらRT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 618, - "friends_count": 1847, - "listed_count": 1, - "created_at": "Fri Aug 01 18:09:38 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 726, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/495272204641132544/GNA18aOg_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/495272204641132544/GNA18aOg_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2698885082/1406917096", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:00 +0000 2014", - "id": 505874861881700350, - "id_str": "505874861881700353", - "text": "@itsukibot_ 一稀の俺のソーセージをペロペロする音はデカイ", - "source": "jigtwi", - "truncated": false, - "in_reply_to_status_id": 505871017428795400, - "in_reply_to_status_id_str": "505871017428795392", - "in_reply_to_user_id": 141170845, - "in_reply_to_user_id_str": "141170845", - "in_reply_to_screen_name": "itsukibot_", - "user": { - "id": 2184752048, - "id_str": "2184752048", - "name": "アンドー", - "screen_name": "55dakedayo", - "location": "", - "description": "", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 15, - "friends_count": 24, - "listed_count": 0, - "created_at": "Sat Nov 09 17:42:22 +0000 2013", - "favourites_count": 37249, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": false, - "verified": false, - "statuses_count": 21070, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://abs.twimg.com/sticky/default_profile_images/default_profile_3_normal.png", - "profile_image_url_https": "https://abs.twimg.com/sticky/default_profile_images/default_profile_3_normal.png", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": true, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "itsukibot_", - "name": "前田一稀", - "id": 141170845, - "id_str": "141170845", - "indices": [ - 0, - 11 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:00 +0000 2014", - "id": 505874861185437700, - "id_str": "505874861185437697", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "あの伝説の名ドラマ&名場面", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2706951979, - "id_str": "2706951979", - "name": "あの伝説の名ドラマ&名場面", - "screen_name": "densetunodorama", - "location": "", - "description": "誰にでも記憶に残る、ドラマの名場面があると思います。そんな感動のストーリーを、もう一度わかちあいたいです。\r\n「これ知ってる!」とか「あ~懐かしい」と思ったら RT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 300, - "friends_count": 1886, - "listed_count": 0, - "created_at": "Mon Aug 04 16:38:25 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 694, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/496335892152209408/fKzb8Nv3_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/496335892152209408/fKzb8Nv3_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2706951979/1407170704", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:29:00 +0000 2014", - "id": 505874860447260700, - "id_str": "505874860447260672", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "マジで食べたい♥ケーキ特集", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2724328646, - "id_str": "2724328646", - "name": "マジで食べたい♥ケーキ特集", - "screen_name": "tabetaicake1", - "location": "", - "description": "女性の目線から見た、美味しそうなケーキを探し求めています。\r\n見てるだけで、あれもコレも食べたくなっちゃう♪\r\n美味しそうだと思ったら、是非 RT & フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 158, - "friends_count": 1907, - "listed_count": 0, - "created_at": "Mon Aug 11 17:15:22 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 493, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498881289844293632/DAa9No9M_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498881289844293632/DAa9No9M_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2724328646/1407777704", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:59 +0000 2014", - "id": 505874859662925800, - "id_str": "505874859662925824", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "アディダス★マニア", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2704003662, - "id_str": "2704003662", - "name": "アディダス★マニア", - "screen_name": "adi_mania11", - "location": "", - "description": "素敵なアディダスのアイテムを見つけたいです♪\r\n気に入ってもらえたららRT & 相互フォローで みなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 340, - "friends_count": 1851, - "listed_count": 0, - "created_at": "Sun Aug 03 12:26:37 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 734, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/495911561781727235/06QAMVrR_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/495911561781727235/06QAMVrR_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2704003662/1407069046", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:59 +0000 2014", - "id": 505874858920513540, - "id_str": "505874858920513537", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "萌えペット大好き", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2719061228, - "id_str": "2719061228", - "name": "萌えペット大好き", - "screen_name": "moe_pet1", - "location": "", - "description": "かわいいペットを見るのが趣味です♥そんな私と一緒にいやされたい人いませんか?かわいいと思ったら RT & フォローもお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 289, - "friends_count": 1812, - "listed_count": 0, - "created_at": "Sat Aug 09 10:20:25 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 632, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498051549537386496/QizThq7N_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498051549537386496/QizThq7N_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2719061228/1407581287", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:59 +0000 2014", - "id": 505874858115219460, - "id_str": "505874858115219456", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "恋愛の教科書 ", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2744344514, - "id_str": "2744344514", - "name": "恋愛の教科書", - "screen_name": "renaikyoukasyo", - "location": "", - "description": "もっと早く知っとくべきだった~!知っていればもっと上手くいく♪ \r\n今すぐ役立つ恋愛についての雑学やマメ知識をお届けします。 \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 124, - "friends_count": 955, - "listed_count": 0, - "created_at": "Tue Aug 19 08:32:45 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 346, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501655512018997248/7SznYGWi_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501655512018997248/7SznYGWi_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2744344514/1408439001", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:59 +0000 2014", - "id": 505874857335074800, - "id_str": "505874857335074816", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "オモロすぎる★学生の日常", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2699365116, - "id_str": "2699365116", - "name": "オモロすぎる★学生の日常", - "screen_name": "omorogakusei", - "location": "", - "description": "楽しすぎる学生の日常を探していきます。\r\n面白かったらRT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 289, - "friends_count": 1156, - "listed_count": 2, - "created_at": "Fri Aug 01 23:35:18 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 770, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/495353473886478336/S-4B_RVl_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/495353473886478336/S-4B_RVl_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2699365116/1406936481", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:59 +0000 2014", - "id": 505874856605257700, - "id_str": "505874856605257728", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "憧れの★インテリア図鑑", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2721907602, - "id_str": "2721907602", - "name": "憧れの★インテリア図鑑", - "screen_name": "akogareinteria", - "location": "", - "description": "自分の住む部屋もこんなふうにしてみたい♪ \r\nそんな素敵なインテリアを、日々探していますw \r\nいいなと思ったら RT & 相互フォローお願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 298, - "friends_count": 1925, - "listed_count": 0, - "created_at": "Sun Aug 10 15:59:13 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 540, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498499374423343105/Wi_izHvT_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498499374423343105/Wi_izHvT_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2721907602/1407686543", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:59 +0000 2014", - "id": 505874856089378800, - "id_str": "505874856089378816", - "text": "天冥の標 VI 宿怨 PART1 / 小川 一水\nhttp://t.co/fXIgRt4ffH\n \n#キンドル #天冥の標VI宿怨PART1", - "source": "waromett", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1953404612, - "id_str": "1953404612", - "name": "わろめっと", - "screen_name": "waromett", - "location": "", - "description": "たのしいついーとしょうかい", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 16980, - "friends_count": 16983, - "listed_count": 18, - "created_at": "Fri Oct 11 05:49:57 +0000 2013", - "favourites_count": 3833, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": false, - "verified": false, - "statuses_count": 98655, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "352726", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme5/bg.gif", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme5/bg.gif", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/378800000578908101/14c4744c7aa34b1f8bbd942b78e59385_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000578908101/14c4744c7aa34b1f8bbd942b78e59385_normal.jpeg", - "profile_link_color": "D02B55", - "profile_sidebar_border_color": "829D5E", - "profile_sidebar_fill_color": "99CC33", - "profile_text_color": "3E4415", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [ - { - "text": "キンドル", - "indices": [ - 50, - 55 - ] - }, - { - "text": "天冥の標VI宿怨PART1", - "indices": [ - 56, - 70 - ] - } - ], - "symbols": [], - "urls": [ - { - "url": "http://t.co/fXIgRt4ffH", - "expanded_url": "http://j.mp/1kHBOym", - "display_url": "j.mp/1kHBOym", - "indices": [ - 25, - 47 - ] - } - ], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "zh" - }, - "created_at": "Sun Aug 31 00:28:58 +0000 2014", - "id": 505874855770599400, - "id_str": "505874855770599425", - "text": "四川盆地江淮等地将有强降雨 开学日多地将有雨:   中新网8月31日电 据中央气象台消息,江淮东部、四川盆地东北部等地今天(31日)又将迎来一场暴雨或大暴雨天气。明天9月1日,是中小学生开学的日子。预计明天,内蒙古中部、... http://t.co/RNdqIHmTby", - "source": "twitterfeed", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 402427654, - "id_str": "402427654", - "name": "中国新闻", - "screen_name": "zhongwenxinwen", - "location": "", - "description": "中国的新闻,世界的新闻。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 2429, - "friends_count": 15, - "listed_count": 29, - "created_at": "Tue Nov 01 01:56:43 +0000 2011", - "favourites_count": 0, - "utc_offset": -28800, - "time_zone": "Alaska", - "geo_enabled": false, - "verified": false, - "statuses_count": 84564, - "lang": "zh-cn", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "709397", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme6/bg.gif", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme6/bg.gif", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/2700523149/5597e347b2eb880425faef54287995f2_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/2700523149/5597e347b2eb880425faef54287995f2_normal.jpeg", - "profile_link_color": "FF3300", - "profile_sidebar_border_color": "86A4A6", - "profile_sidebar_fill_color": "A0C5C7", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/RNdqIHmTby", - "expanded_url": "http://bit.ly/1tOdNsI", - "display_url": "bit.ly/1tOdNsI", - "indices": [ - 114, - 136 - ] - } - ], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "zh" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:58 +0000 2014", - "id": 505874854877200400, - "id_str": "505874854877200384", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "LDH ★大好き応援団", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2700961603, - "id_str": "2700961603", - "name": "LDH ★大好き応援団", - "screen_name": "LDH_daisuki1", - "location": "", - "description": "LDHファンは、全員仲間です♪\r\n面白かったらRT & 相互フォローでみなさん、お願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 458, - "friends_count": 1895, - "listed_count": 0, - "created_at": "Sat Aug 02 14:23:46 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 735, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/495578007298252800/FOZflgYu_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/495578007298252800/FOZflgYu_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2700961603/1406989928", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:58 +0000 2014", - "id": 505874854147407900, - "id_str": "505874854147407872", - "text": "RT @shiawaseomamori: 一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるの…", - "source": "マジ!?怖いアニメ都市伝説", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2719489172, - "id_str": "2719489172", - "name": "マジ!?怖いアニメ都市伝説", - "screen_name": "anime_toshiden1", - "location": "", - "description": "あなたの知らない、怖すぎるアニメの都市伝説を集めています。\r\n「え~知らなかったよww]」って人は RT & フォローお願いします♪", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 377, - "friends_count": 1911, - "listed_count": 1, - "created_at": "Sat Aug 09 14:41:15 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 536, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/498118027322208258/h7XOTTSi_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/498118027322208258/h7XOTTSi_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2719489172/1407595513", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:06 +0000 2014", - "id": 505871615125491700, - "id_str": "505871615125491712", - "text": "一に止まると書いて、正しいという意味だなんて、この年になるまで知りませんでした。 人は生きていると、前へ前へという気持ちばかり急いて、どんどん大切なものを置き去りにしていくものでしょう。本当に正しいことというのは、一番初めの場所にあるのかもしれません。 by神様のカルテ、夏川草介", - "source": "幸せの☆お守り", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2745121514, - "id_str": "2745121514", - "name": "幸せの☆お守り", - "screen_name": "shiawaseomamori", - "location": "", - "description": "自分が幸せだと周りも幸せにできる! \r\nそんな人生を精一杯生きるために必要な言葉をお届けします♪ \r\nいいなと思ったら RT & 相互フォローで、お願いします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 213, - "friends_count": 991, - "listed_count": 0, - "created_at": "Tue Aug 19 14:45:19 +0000 2014", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 349, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/501742437606244354/scXy81ZW_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2745121514/1408459730", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 58, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "shiawaseomamori", - "name": "幸せの☆お守り", - "id": 2745121514, - "id_str": "2745121514", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:58 +0000 2014", - "id": 505874854134820860, - "id_str": "505874854134820864", - "text": "@vesperia1985 おはよー!\n今日までなのですよ…!!明日一生来なくていい", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": 505868030329364500, - "in_reply_to_status_id_str": "505868030329364480", - "in_reply_to_user_id": 2286548834, - "in_reply_to_user_id_str": "2286548834", - "in_reply_to_screen_name": "vesperia1985", - "user": { - "id": 2389045190, - "id_str": "2389045190", - "name": "りいこ", - "screen_name": "riiko_dq10", - "location": "", - "description": "サマーエルフです、りいこです。えるおくんラブです!随時ふれぼしゅ〜〜(っ˘ω˘c )*日常のどうでもいいことも呟いてますがよろしくね〜", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 67, - "friends_count": 69, - "listed_count": 0, - "created_at": "Fri Mar 14 13:02:27 +0000 2014", - "favourites_count": 120, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 324, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/503906346815610881/BfSrCoBr_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/503906346815610881/BfSrCoBr_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2389045190/1409232058", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "vesperia1985", - "name": "ユーリ", - "id": 2286548834, - "id_str": "2286548834", - "indices": [ - 0, - 13 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:58 +0000 2014", - "id": 505874853778685950, - "id_str": "505874853778685952", - "text": "【映画パンフレット】 永遠の0 (永遠のゼロ) 監督 山崎貴 キャスト 岡田准一、三浦春馬、井上真央東宝(2)11点の新品/中古品を見る: ¥ 500より\n(この商品の現在のランクに関する正式な情報については、アートフレーム... http://t.co/4hbyB1rbQ7", - "source": "IFTTT", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1319883571, - "id_str": "1319883571", - "name": "森林木工家具製作所", - "screen_name": "Furniturewood", - "location": "沖縄", - "description": "家具(かぐ、Furniture)は、家財道具のうち家の中に据え置いて利用する比較的大型の道具類、または元々家に作り付けられている比較的大型の道具類をさす。なお、日本の建築基準法上は、作り付け家具は、建築確認及び完了検査の対象となるが、後から置かれるものについては対象外である。", - "url": "http://t.co/V4oyL0xtZk", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/V4oyL0xtZk", - "expanded_url": "http://astore.amazon.co.jp/furniturewood-22", - "display_url": "astore.amazon.co.jp/furniturewood-…", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 677, - "friends_count": 743, - "listed_count": 1, - "created_at": "Mon Apr 01 07:55:14 +0000 2013", - "favourites_count": 0, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": false, - "verified": false, - "statuses_count": 17210, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/3460466135/c67d9df9b760787b9ed284fe80b1dd31_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/3460466135/c67d9df9b760787b9ed284fe80b1dd31_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1319883571/1364804982", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/4hbyB1rbQ7", - "expanded_url": "http://ift.tt/1kT55bk", - "display_url": "ift.tt/1kT55bk", - "indices": [ - 116, - 138 - ] - } - ], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:58 +0000 2014", - "id": 505874852754907140, - "id_str": "505874852754907136", - "text": "RT @siranuga_hotoke: ゴキブリは一世帯に平均して480匹いる。", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 413944345, - "id_str": "413944345", - "name": "泥酔イナバウアー", - "screen_name": "Natade_co_co_21", - "location": "", - "description": "君の瞳にうつる僕に乾杯。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 298, - "friends_count": 300, - "listed_count": 4, - "created_at": "Wed Nov 16 12:52:46 +0000 2011", - "favourites_count": 3125, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 12237, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "FFF04D", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/378800000115928444/9bf5fa13385cc80bfeb097e51af9862a.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/378800000115928444/9bf5fa13385cc80bfeb097e51af9862a.jpeg", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/500849752351600640/lMQqIzYj_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/500849752351600640/lMQqIzYj_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/413944345/1403511193", - "profile_link_color": "0099CC", - "profile_sidebar_border_color": "000000", - "profile_sidebar_fill_color": "F6FFD1", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sat Aug 30 23:24:23 +0000 2014", - "id": 505858599411666940, - "id_str": "505858599411666944", - "text": "ゴキブリは一世帯に平均して480匹いる。", - "source": "twittbot.net", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2243896200, - "id_str": "2243896200", - "name": "知らぬが仏bot", - "screen_name": "siranuga_hotoke", - "location": "奈良・京都辺り", - "description": "知らぬが仏な情報をお伝えします。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 3288, - "friends_count": 3482, - "listed_count": 7, - "created_at": "Fri Dec 13 13:16:35 +0000 2013", - "favourites_count": 0, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 1570, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/378800000866399372/ypy5NnPe_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000866399372/ypy5NnPe_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/2243896200/1386997755", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 1, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - "retweet_count": 1, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [], - "user_mentions": [ - { - "screen_name": "siranuga_hotoke", - "name": "知らぬが仏bot", - "id": 2243896200, - "id_str": "2243896200", - "indices": [ - 3, - 19 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:58 +0000 2014", - "id": 505874852603908100, - "id_str": "505874852603908096", - "text": "RT @UARROW_Y: ようかい体操第一を踊る国見英 http://t.co/SXoYWH98as", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 2463035136, - "id_str": "2463035136", - "name": "や", - "screen_name": "yae45", - "location": "", - "description": "きもちわるいことつぶやく用", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 4, - "friends_count": 30, - "listed_count": 0, - "created_at": "Fri Apr 25 10:49:20 +0000 2014", - "favourites_count": 827, - "utc_offset": 32400, - "time_zone": "Irkutsk", - "geo_enabled": false, - "verified": false, - "statuses_count": 390, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/505345820137234433/csFeRxPm_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/505345820137234433/csFeRxPm_normal.jpeg", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:16:45 +0000 2014", - "id": 505871779949051900, - "id_str": "505871779949051904", - "text": "ようかい体操第一を踊る国見英 http://t.co/SXoYWH98as", - "source": "Twitter for Android", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1261662588, - "id_str": "1261662588", - "name": "ゆう矢", - "screen_name": "UARROW_Y", - "location": "つくり出そう国影の波 広げよう国影の輪", - "description": "HQ!! 成人済腐女子。日常ツイート多いです。赤葦京治夢豚クソツイ含みます注意。フォローをお考えの際はプロフご一読お願い致します。FRBお気軽に", - "url": "http://t.co/LFX2XOzb0l", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/LFX2XOzb0l", - "expanded_url": "http://twpf.jp/UARROW_Y", - "display_url": "twpf.jp/UARROW_Y", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 265, - "friends_count": 124, - "listed_count": 12, - "created_at": "Tue Mar 12 10:42:17 +0000 2013", - "favourites_count": 6762, - "utc_offset": 32400, - "time_zone": "Tokyo", - "geo_enabled": true, - "verified": false, - "statuses_count": 55946, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/502095104618663937/IzuPYx3E_normal.png", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/502095104618663937/IzuPYx3E_normal.png", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1261662588/1408618604", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 29, - "favorite_count": 54, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/SXoYWH98as", - "expanded_url": "http://twitter.com/UARROW_Y/status/505871779949051904/photo/1", - "display_url": "pic.twitter.com/SXoYWH98as", - "indices": [ - 15, - 37 - ] - } - ], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - "retweet_count": 29, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/SXoYWH98as", - "expanded_url": "http://twitter.com/UARROW_Y/status/505871779949051904/photo/1", - "display_url": "pic.twitter.com/SXoYWH98as", - "indices": [ - 29, - 51 - ] - } - ], - "user_mentions": [ - { - "screen_name": "UARROW_Y", - "name": "ゆう矢", - "id": 1261662588, - "id_str": "1261662588", - "indices": [ - 3, - 12 - ] - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "zh" - }, - "created_at": "Sun Aug 31 00:28:57 +0000 2014", - "id": 505874848900341760, - "id_str": "505874848900341760", - "text": "RT @fightcensorship: 李克強總理的臉綠了!在前日南京青奧會閉幕式,觀眾席上一名貪玩韓國少年運動員,竟斗膽用激光筆射向中國總理李克強的臉。http://t.co/HLX9mHcQwe http://t.co/fVVOSML5s8", - "source": "Twitter for iPhone", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 889332218, - "id_str": "889332218", - "name": "民權初步", - "screen_name": "JoeyYoungkm", - "location": "km/cn", - "description": "经历了怎样的曲折才从追求“一致通过”发展到今天人们接受“过半数通过”,正是人们认识到对“一致”甚至是“基本一致”的追求本身就会变成一种独裁。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 313, - "friends_count": 46, - "listed_count": 0, - "created_at": "Thu Oct 18 17:21:17 +0000 2012", - "favourites_count": 24, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 15707, - "lang": "en", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "C0DEED", - "profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/378800000563062033/a7e8274752ce36a6cd5bad971ec7d416_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/378800000563062033/a7e8274752ce36a6cd5bad971ec7d416_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/889332218/1388896916", - "profile_link_color": "0084B4", - "profile_sidebar_border_color": "C0DEED", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": true, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweeted_status": { - "metadata": { - "result_type": "recent", - "iso_language_code": "zh" - }, - "created_at": "Sat Aug 30 23:56:27 +0000 2014", - "id": 505866670356070400, - "id_str": "505866670356070401", - "text": "李克強總理的臉綠了!在前日南京青奧會閉幕式,觀眾席上一名貪玩韓國少年運動員,竟斗膽用激光筆射向中國總理李克強的臉。http://t.co/HLX9mHcQwe http://t.co/fVVOSML5s8", - "source": "Twitter Web Client", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 67661086, - "id_str": "67661086", - "name": "※范强※法特姗瑟希蒲※", - "screen_name": "fightcensorship", - "location": "Middle of Nowhere", - "description": "被人指责“封建”、“落后”、“保守”的代表,当代红卫兵攻击对象。致力于言论自由,人权; 倡导资讯公开,反对网络封锁。既不是精英分子,也不是意见领袖,本推言论不代表任何国家、党派和组织,也不标榜伟大、光荣和正确。", - "url": null, - "entities": { - "description": { - "urls": [] - } - }, - "protected": false, - "followers_count": 7143, - "friends_count": 779, - "listed_count": 94, - "created_at": "Fri Aug 21 17:16:22 +0000 2009", - "favourites_count": 364, - "utc_offset": 28800, - "time_zone": "Singapore", - "geo_enabled": false, - "verified": false, - "statuses_count": 16751, - "lang": "en", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "FFFFFF", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/611138516/toeccqnahbpmr0sw9ybv.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/611138516/toeccqnahbpmr0sw9ybv.jpeg", - "profile_background_tile": true, - "profile_image_url": "http://pbs.twimg.com/profile_images/3253137427/3524557d21ef2c04871e985d4d136bdb_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/3253137427/3524557d21ef2c04871e985d4d136bdb_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/67661086/1385608347", - "profile_link_color": "ED1313", - "profile_sidebar_border_color": "FFFFFF", - "profile_sidebar_fill_color": "E0FF92", - "profile_text_color": "000000", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 4, - "favorite_count": 2, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/HLX9mHcQwe", - "expanded_url": "http://is.gd/H3OgTO", - "display_url": "is.gd/H3OgTO", - "indices": [ - 57, - 79 - ] - } - ], - "user_mentions": [], - "media": [ - { - "id": 505866668485386240, - "id_str": "505866668485386241", - "indices": [ - 80, - 102 - ], - "media_url": "http://pbs.twimg.com/media/BwUzDgbIIAEgvhD.jpg", - "media_url_https": "https://pbs.twimg.com/media/BwUzDgbIIAEgvhD.jpg", - "url": "http://t.co/fVVOSML5s8", - "display_url": "pic.twitter.com/fVVOSML5s8", - "expanded_url": "http://twitter.com/fightcensorship/status/505866670356070401/photo/1", - "type": "photo", - "sizes": { - "large": { - "w": 640, - "h": 554, - "resize": "fit" - }, - "medium": { - "w": 600, - "h": 519, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "small": { - "w": 340, - "h": 294, - "resize": "fit" - } - } - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "zh" - }, - "retweet_count": 4, - "favorite_count": 0, - "entities": { - "hashtags": [], - "symbols": [], - "urls": [ - { - "url": "http://t.co/HLX9mHcQwe", - "expanded_url": "http://is.gd/H3OgTO", - "display_url": "is.gd/H3OgTO", - "indices": [ - 78, - 100 - ] - } - ], - "user_mentions": [ - { - "screen_name": "fightcensorship", - "name": "※范强※法特姗瑟希蒲※", - "id": 67661086, - "id_str": "67661086", - "indices": [ - 3, - 19 - ] - } - ], - "media": [ - { - "id": 505866668485386240, - "id_str": "505866668485386241", - "indices": [ - 101, - 123 - ], - "media_url": "http://pbs.twimg.com/media/BwUzDgbIIAEgvhD.jpg", - "media_url_https": "https://pbs.twimg.com/media/BwUzDgbIIAEgvhD.jpg", - "url": "http://t.co/fVVOSML5s8", - "display_url": "pic.twitter.com/fVVOSML5s8", - "expanded_url": "http://twitter.com/fightcensorship/status/505866670356070401/photo/1", - "type": "photo", - "sizes": { - "large": { - "w": 640, - "h": 554, - "resize": "fit" - }, - "medium": { - "w": 600, - "h": 519, - "resize": "fit" - }, - "thumb": { - "w": 150, - "h": 150, - "resize": "crop" - }, - "small": { - "w": 340, - "h": 294, - "resize": "fit" - } - }, - "source_status_id": 505866670356070400, - "source_status_id_str": "505866670356070401" - } - ] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "zh" - }, - { - "metadata": { - "result_type": "recent", - "iso_language_code": "ja" - }, - "created_at": "Sun Aug 31 00:28:56 +0000 2014", - "id": 505874847260352500, - "id_str": "505874847260352513", - "text": "【マイリスト】【彩りりあ】妖怪体操第一 踊ってみた【反転】 http://t.co/PjL9if8OZC #sm24357625", - "source": "ニコニコ動画", - "truncated": false, - "in_reply_to_status_id": null, - "in_reply_to_status_id_str": null, - "in_reply_to_user_id": null, - "in_reply_to_user_id_str": null, - "in_reply_to_screen_name": null, - "user": { - "id": 1609789375, - "id_str": "1609789375", - "name": "食いしん坊前ちゃん", - "screen_name": "2no38mae", - "location": "ニノと二次元の間", - "description": "ニコ動で踊り手やってます!!応援本当に嬉しいですありがとうございます!! ぽっちゃりだけど前向きに頑張る腐女子です。嵐と弱虫ペダルが大好き!【お返事】りぷ(基本は)”○” DM (同業者様を除いて)”×” 動画の転載は絶対にやめてください。 ブログ→http://t.co/8E91tqoeKX  ", - "url": "http://t.co/ulD2e9mcwb", - "entities": { - "url": { - "urls": [ - { - "url": "http://t.co/ulD2e9mcwb", - "expanded_url": "http://www.nicovideo.jp/mylist/37917495", - "display_url": "nicovideo.jp/mylist/37917495", - "indices": [ - 0, - 22 - ] - } - ] - }, - "description": { - "urls": [ - { - "url": "http://t.co/8E91tqoeKX", - "expanded_url": "http://ameblo.jp/2no38mae/", - "display_url": "ameblo.jp/2no38mae/", - "indices": [ - 125, - 147 - ] - } - ] - } - }, - "protected": false, - "followers_count": 560, - "friends_count": 875, - "listed_count": 11, - "created_at": "Sun Jul 21 05:09:43 +0000 2013", - "favourites_count": 323, - "utc_offset": null, - "time_zone": null, - "geo_enabled": false, - "verified": false, - "statuses_count": 3759, - "lang": "ja", - "contributors_enabled": false, - "is_translator": false, - "is_translation_enabled": false, - "profile_background_color": "F2C6E4", - "profile_background_image_url": "http://pbs.twimg.com/profile_background_images/378800000029400927/114b242f5d838ec7cb098ea5db6df413.jpeg", - "profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/378800000029400927/114b242f5d838ec7cb098ea5db6df413.jpeg", - "profile_background_tile": false, - "profile_image_url": "http://pbs.twimg.com/profile_images/487853237723095041/LMBMGvOc_normal.jpeg", - "profile_image_url_https": "https://pbs.twimg.com/profile_images/487853237723095041/LMBMGvOc_normal.jpeg", - "profile_banner_url": "https://pbs.twimg.com/profile_banners/1609789375/1375752225", - "profile_link_color": "FF9EDD", - "profile_sidebar_border_color": "FFFFFF", - "profile_sidebar_fill_color": "DDEEF6", - "profile_text_color": "333333", - "profile_use_background_image": true, - "default_profile": false, - "default_profile_image": false, - "following": false, - "follow_request_sent": false, - "notifications": false - }, - "geo": null, - "coordinates": null, - "place": null, - "contributors": null, - "retweet_count": 0, - "favorite_count": 0, - "entities": { - "hashtags": [ - { - "text": "sm24357625", - "indices": [ - 53, - 64 - ] - } - ], - "symbols": [], - "urls": [ - { - "url": "http://t.co/PjL9if8OZC", - "expanded_url": "http://nico.ms/sm24357625", - "display_url": "nico.ms/sm24357625", - "indices": [ - 30, - 52 - ] - } - ], - "user_mentions": [] - }, - "favorited": false, - "retweeted": false, - "possibly_sensitive": false, - "lang": "ja" - } - ], - "search_metadata": { - "completed_in": 0.087, - "max_id": 505874924095815700, - "max_id_str": "505874924095815681", - "next_results": "?max_id=505874847260352512&q=%E4%B8%80&count=100&include_entities=1", - "query": "%E4%B8%80", - "refresh_url": "?since_id=505874924095815681&q=%E4%B8%80&include_entities=1", - "count": 100, - "since_id": 0, - "since_id_str": "0" - } -} diff --git a/simdjson-sys/Cargo.toml b/simdjson-sys/Cargo.toml index b1df930..697bca1 100644 --- a/simdjson-sys/Cargo.toml +++ b/simdjson-sys/Cargo.toml @@ -4,12 +4,7 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -include = [ - "../simdjson/singleheader/simdjson.h", - "../simdjson/singleheader/simdjson.cpp", -] - +exclude = ["simdjson/", "!simdjson/singleheader/simdjson.*"] [dependencies] diff --git a/simdjson-sys/build.rs b/simdjson-sys/build.rs index fcd5d13..b914408 100644 --- a/simdjson-sys/build.rs +++ b/simdjson-sys/build.rs @@ -5,9 +5,12 @@ fn main() { cc::Build::new() .cpp(true) .flag_if_supported("-std=c++17") - .include("../simdjson/singleheader") + .flag_if_supported("/std:c++17") + .flag_if_supported("-pthread") + .flag_if_supported("-O3") + .include("simdjson/singleheader") .file("src/simdjson_c_api.cpp") - .file("../simdjson/singleheader/simdjson.cpp") + .file("simdjson/singleheader/simdjson.cpp") .cargo_metadata(true) .compile("simdjson_c_api"); @@ -28,10 +31,6 @@ fn main() { // Write the bindings to the $OUT_DIR/bindings.rs file. let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - // println!( - // "cargo:rustc-link-lib=static={}", - // out_path.join("libsimdjson_c_api").display() - // ); bindings .write_to_file(out_path.join("bindings.rs")) From b0f3447ac2f0da0d210f4a6625f4f03ab4c55fd7 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 17:33:20 +0800 Subject: [PATCH 23/31] clean up --- Cargo.toml | 2 +- simdjson-sys/src/lib.rs | 14 --- src/lib.rs | 24 +---- src/libsimdjson.rs | 188 ---------------------------------------- src/padded_string.rs | 1 + 5 files changed, 5 insertions(+), 224 deletions(-) delete mode 100644 src/libsimdjson.rs diff --git a/Cargo.toml b/Cargo.toml index bd259ad..8cdf611 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.3.0" authors = ["SunDoge <384813529@qq.com>"] edition = "2021" license = "Apache-2.0" -exclude = [".github/"] +exclude = [".github/", "examples/"] [workspace] diff --git a/simdjson-sys/src/lib.rs b/simdjson-sys/src/lib.rs index 6347d78..a38a13a 100644 --- a/simdjson-sys/src/lib.rs +++ b/simdjson-sys/src/lib.rs @@ -3,17 +3,3 @@ #![allow(non_snake_case)] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - unsafe { - let s = "hello world"; - let ps = SJ_padded_string_new(s.as_ptr().cast(), s.len()); - SJ_padded_string_free(ps); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index d7fe674..a803912 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,30 +1,12 @@ -// pub mod error; -// pub mod json_ioutil; -// pub mod json_parser; -// pub mod parsed_json; -// pub mod parsed_json_iterator; - -// pub use error::SimdJsonError; -// pub use json_parser::{build_parsed_json, json_parse}; -// pub use parsed_json::{ParsedJson, DEFUALT_MAX_DEPTH}; -// pub use parsed_json_iterator::ParsedJsonIterator; - mod macros; pub mod constants; -// pub mod dom; pub mod error; pub mod ondemand; -// pub mod libsimdjson; pub mod padded_string; -// pub mod serde; pub mod utils; -#[cfg(test)] -mod tests { +// pub mod serde; - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +#[cfg(test)] +mod tests {} diff --git a/src/libsimdjson.rs b/src/libsimdjson.rs deleted file mode 100644 index a6e9648..0000000 --- a/src/libsimdjson.rs +++ /dev/null @@ -1,188 +0,0 @@ -#[cxx::bridge(namespace = simdjson::ffi)] -pub mod ffi { - - struct ElementResult { - value: UniquePtr, - code: i32, - } - - struct StringResult { - value: String, - code: i32, - } - - struct BoolResult { - value: bool, - code: i32, - } - - // struct NumberResult { - // value: u64, - // code: i32, - // } - struct U64Result { - value: u64, - code: i32, - } - - struct I64Result { - value: i64, - code: i32, - } - - struct F64Result { - value: f64, - code: i32, - } - - struct ArrayResult { - value: UniquePtr, - code: i32, - } - - struct ObjectResult { - value: UniquePtr, - code: i32, - } - - struct DocumentStreamResult { - value: UniquePtr, - code: i32, - } - - struct KeyValuePair { - key: String, - value: UniquePtr, - } - - // struct ArrayIterator { - // begin: UniquePtr, - // end: UniquePtr, - // } - - // struct ObjectIterator { - // begin: UniquePtr, - // end: UniquePtr, - // } - - unsafe extern "C++" { - include!("simdjson-rust/csrc/wrapper.h"); - type parser; - type element; - type padded_string; - // // type tape_ref; - type document_stream; - - type array; - type object; - - // type array_iterator; - // type object_iterator; - type ArrayIterator; - type ObjectIterator; - type DocumentStreamIterator; - - // type simdjson_result; - - fn parser_new(max_capacity: usize) -> UniquePtr; - fn parser_load(p: Pin<&mut parser>, path: &str) -> ElementResult; - fn parser_parse(p: Pin<&mut parser>, s: &str) -> ElementResult; - fn parser_parse_padded(p: Pin<&mut parser>, s: &padded_string) -> ElementResult; - - fn padded_string_from_string(s: &str) -> UniquePtr; - - // fn tape_ref_type(tr: &tape_ref) -> u8; - // fn tape_ref_next_tape_value(tr: &tape_ref) -> u64; - - fn element_get_string(elm: &element) -> StringResult; - fn element_get_array(elm: &element) -> ArrayResult; - fn element_get_object(elm: &element) -> ObjectResult; - // fn element_get_number(elm: &element) -> NumberResult; - fn element_get_u64(elm: &element) -> U64Result; - fn element_get_i64(elm: &element) -> I64Result; - fn element_get_f64(elm: &element) -> F64Result; - - fn element_get_bool(elm: &element) -> BoolResult; - fn element_is_null(elm: &element) -> bool; - fn element_at_pointer(elm: &element, json_pointer: &str) -> ElementResult; - fn element_at_index(elm: &element, index: usize) -> ElementResult; - fn element_at_key(elm: &element, key: &str) -> ElementResult; - - fn element_get_type(elm: &element) -> u8; - - fn array_at_pointer(arr: &array, json_pointer: &str) -> ElementResult; - fn array_at(arr: &array, index: usize) -> ElementResult; - - fn object_at_pointer(obj: &object, json_pointer: &str) -> ElementResult; - fn object_at_key(obj: &object, key: &str) -> ElementResult; - fn object_at_key_case_insensitive(obj: &object, key: &str) -> ElementResult; - - fn array_get_iterator(arr: &array) -> UniquePtr; - fn array_iterator_next(iter: Pin<&mut ArrayIterator>) -> UniquePtr; - - fn object_get_iterator(obj: &object) -> UniquePtr; - fn object_iterator_next(iter: Pin<&mut ObjectIterator>); - fn object_iterator_has_next(iter: &ObjectIterator) -> bool; - fn object_iterator_key(iter: &ObjectIterator) -> String; - fn object_iterator_value(iter: &ObjectIterator) -> UniquePtr; - - fn element_minify(elm: &element) -> String; - fn object_minify(obj: &object) -> String; - fn array_minify(arr: &array) -> String; - - fn parser_load_many( - p: Pin<&mut parser>, - path: &str, - batch_size: usize, - ) -> DocumentStreamResult; - fn parser_parse_many<'a>( - p: Pin<&'a mut parser>, - s: &'a str, - batch_size: usize, - ) -> DocumentStreamResult; - fn parser_parse_many_padded<'a>( - p: Pin<&'a mut parser>, - s: &'a padded_string, - batch_size: usize, - ) -> DocumentStreamResult; - - fn document_stream_get_iterator( - stream: Pin<&mut document_stream>, - ) -> UniquePtr; - fn document_stream_iterator_deref(iter: Pin<&mut DocumentStreamIterator>) -> ElementResult; - fn document_stream_iterator_next(iter: Pin<&mut DocumentStreamIterator>) -> (); - } -} - -pub const SIMDJSON_MAXSIZE_BYTES: usize = 0xFFFFFFFF; -pub const DEFAULT_BATCH_SIZE: usize = 1000000; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } - - #[test] - fn get_parser() { - let mut parser = ffi::parser_new(SIMDJSON_MAXSIZE_BYTES); - let result = ffi::parser_load(parser.pin_mut(), "json-examples/twitter.json"); - // dbg!(parser.load); - println!("parse code: {}", result.code); - let result = ffi::parser_parse(parser.pin_mut(), r#""1234""#); - println!("parse code: {}", result.code); - println!("value: {:?}", ffi::element_get_string(&result.value).value); - } - - // #[test] - // fn parse_padded_string() { - // let ps = ffi::padded_string_from_string("[1]"); - // let mut parser = ffi::parser_new(SIMDJSON_MAXSIZE_BYTES); - // let mut code = 0; - // let element = ffi::parser_parse_padded_string(&mut parser, &ps, &mut code); - // println!("parse code: {}", code); - // } -} diff --git a/src/padded_string.rs b/src/padded_string.rs index 0a8cf75..3e3ada2 100644 --- a/src/padded_string.rs +++ b/src/padded_string.rs @@ -8,6 +8,7 @@ use crate::{ macros::{impl_drop, map_result}, }; +// TODO: we will remove PaddedString because String is enough. pub struct PaddedString { ptr: NonNull, } From 13fadf50d7d219d9e6c6ebf0d03e34fd4f07fe68 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 17:36:48 +0800 Subject: [PATCH 24/31] fix ci --- .github/workflows/CI.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9c2b56b..bceb36d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -30,6 +30,8 @@ jobs: steps: - uses: actions/checkout@v3 + with: + submodules: true # Cache - name: Cache From 456232646948d8b18ba45d848c1d9a4ead07c234 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 19:00:13 +0800 Subject: [PATCH 25/31] add lifetime --- examples/issue_20.rs | 20 ++--- simdjson-sys/src/lib.rs | 3 + simdjson-sys/src/simdjson_c_api.cpp | 115 +++++++++++++++------------- simdjson-sys/src/simdjson_c_api.h | 20 +++-- src/constants.rs | 2 - src/lib.rs | 1 - src/ondemand/array.rs | 10 +-- src/ondemand/array_iterator.rs | 18 ++--- src/ondemand/document.rs | 14 ++-- src/ondemand/field.rs | 16 ++-- src/ondemand/object.rs | 10 +-- src/ondemand/object_iterator.rs | 18 ++--- src/ondemand/parser.rs | 16 +++- src/ondemand/value.rs | 10 +-- src/padded_string.rs | 63 +-------------- 15 files changed, 154 insertions(+), 182 deletions(-) delete mode 100644 src/constants.rs diff --git a/examples/issue_20.rs b/examples/issue_20.rs index ed34151..41e8357 100644 --- a/examples/issue_20.rs +++ b/examples/issue_20.rs @@ -7,20 +7,20 @@ fn main() -> Result<()> { let padded_string = make_padded_string(data); let mut doc = parser.iterate(&padded_string)?; - let mut arr = doc.get_object()?.at_pointer("/ingredients")?.get_array()?; + // let mut arr = doc.get_object()?.at_pointer("/ingredients")?.get_array()?; - for value in arr.iter()? { - let mut object = value?.get_object()?; + // for value in arr.iter()? { + // let mut object = value?.get_object()?; - for field in object.iter()? { - let mut field = field?; + // for field in object.iter()? { + // let mut field = field?; - let key = field.unescaped_key(false)?.to_owned(); - let mut value = field.take_value(); - println!("key: {} | value: {}", key, value.get_object()?.raw_json()?); - } - } + // let key = field.unescaped_key(false)?.to_owned(); + // let mut value = field.take_value(); + // println!("key: {} | value: {}", key, value.get_object()?.raw_json()?); + // } + // } Ok(()) } diff --git a/simdjson-sys/src/lib.rs b/simdjson-sys/src/lib.rs index a38a13a..343ac7a 100644 --- a/simdjson-sys/src/lib.rs +++ b/simdjson-sys/src/lib.rs @@ -3,3 +3,6 @@ #![allow(non_snake_case)] include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +pub const SIMDJSON_PADDING: usize = 64; +pub const SIMDJSON_MAXSIZE_BYTES: usize = 0xFFFFFFFF; diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index 23d03a6..2bc8de5 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -7,8 +7,9 @@ using namespace simdjson; namespace { -template U *object_to_pointer(T &&t) { - return reinterpret_cast(new T(std::move(t))); + +template inline U object_to_pointer(T &&t) { + return reinterpret_cast(new T(std::move(t))); } } // namespace @@ -25,7 +26,7 @@ template U *object_to_pointer(T &&t) { } \ name *name##_result_value_unsafe(name##_result *r) { \ auto result = reinterpret_cast *>(r); \ - return object_to_pointer(std::move(*result).value_unsafe()); \ + return object_to_pointer(std::move(*result).value_unsafe()); \ } #define IMPL_PRIMITIVE_RESULT(name) \ @@ -39,8 +40,8 @@ template U *object_to_pointer(T &&t) { return std::move(*result).value_unsafe(); \ } -IMPL_CLASS(SJ_padded_string, padded_string) -IMPL_RESULT(SJ_padded_string, padded_string) +// IMPL_CLASS(SJ_padded_string, padded_string) +// IMPL_RESULT(SJ_padded_string, padded_string) IMPL_CLASS(SJ_OD_parser, ondemand::parser) IMPL_CLASS(SJ_OD_document, ondemand::document) IMPL_RESULT(SJ_OD_document, ondemand::document) @@ -67,22 +68,23 @@ IMPL_PRIMITIVE_RESULT(double) IMPL_PRIMITIVE_RESULT(bool) IMPL_PRIMITIVE_RESULT(size_t) -SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) { - return object_to_pointer(padded_string(s, len)); -} - -SJ_padded_string_result *SJ_padded_string_load(const char *path) { - return object_to_pointer(padded_string::load(path)); -} -size_t SJ_padded_string_length(const SJ_padded_string *ps) { - return reinterpret_cast(ps)->length(); -} -const uint8_t *SJ_padded_string_u8data(const SJ_padded_string *ps) { - return reinterpret_cast(ps)->u8data(); -} +// SJ_padded_string *SJ_padded_string_new(const char *s, size_t len) { +// return object_to_pointer(padded_string(s, len)); +// } + +// SJ_padded_string_result *SJ_padded_string_load(const char *path) { +// return object_to_pointer( +// padded_string::load(path)); +// } +// size_t SJ_padded_string_length(const SJ_padded_string *ps) { +// return reinterpret_cast(ps)->length(); +// } +// const uint8_t *SJ_padded_string_u8data(const SJ_padded_string *ps) { +// return reinterpret_cast(ps)->u8data(); +// } SJ_OD_parser *SJ_OD_parser_new(size_t max_capacity) { - return object_to_pointer(ondemand::parser(max_capacity)); + return object_to_pointer(ondemand::parser(max_capacity)); } SJ_OD_document_result * @@ -92,7 +94,7 @@ SJ_OD_parser_iterate_padded_string(SJ_OD_parser *parser, *reinterpret_cast(s)); // return reinterpret_cast(new // simdjson_result(std::move(doc))); - return object_to_pointer(std::move(doc)); + return object_to_pointer(std::move(doc)); } SJ_OD_document_result * @@ -100,19 +102,19 @@ SJ_OD_parser_iterate_padded_string_view(SJ_OD_parser *parser, const char *json, size_t len, size_t allocated) { auto doc = reinterpret_cast(parser)->iterate(json, len, allocated); - return object_to_pointer(std::move(doc)); + return object_to_pointer(std::move(doc)); } SJ_OD_value_result *SJ_OD_document_get_value(SJ_OD_document *doc) { auto value = reinterpret_cast(doc)->get_value(); - return object_to_pointer(std::move(value)); + return object_to_pointer(std::move(value)); } // self, self's real name, output value, how to get output value #define IMPL_GET(self, real_name, value, method) \ value##_result *self##_##method(self *r) { \ auto result = reinterpret_cast(r)->method(); \ - return object_to_pointer(std::move(result)); \ + return object_to_pointer(std::move(result)); \ } IMPL_GET(SJ_OD_value, ondemand::value, SJ_OD_object, get_object) @@ -137,14 +139,14 @@ STD_string_view_result *SJ_OD_value_get_string(SJ_OD_value *self, bool allow_replacement) { auto result = reinterpret_cast(self)->get_string(allow_replacement); - return object_to_pointer(std::move(result)); + return object_to_pointer(std::move(result)); } STD_string_view_result *SJ_OD_document_get_string(SJ_OD_document *self, bool allow_replacement) { auto result = reinterpret_cast(self)->get_string( allow_replacement); - return object_to_pointer(std::move(result)); + return object_to_pointer(std::move(result)); } const char *STD_string_view_data(STD_string_view *sv) { @@ -163,56 +165,63 @@ IMPL_GET(SJ_OD_array, ondemand::array, SJ_OD_array_iterator, end) SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index) { auto result = reinterpret_cast(array)->at(index); - return object_to_pointer(std::move(result)); + return object_to_pointer(std::move(result)); } // ondemand::array_iterator -SJ_OD_value_result* SJ_OD_array_iterator_get(SJ_OD_array_iterator* self) { - auto ptr = reinterpret_cast(self); - return object_to_pointer(**ptr); +SJ_OD_value_result *SJ_OD_array_iterator_get(SJ_OD_array_iterator *self) { + auto ptr = reinterpret_cast(self); + return object_to_pointer(**ptr); } -bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator* lhs, const SJ_OD_array_iterator* rhs) { - return *reinterpret_cast(lhs) != *reinterpret_cast(rhs); +bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator *lhs, + const SJ_OD_array_iterator *rhs) { + return *reinterpret_cast(lhs) != + *reinterpret_cast(rhs); } -void SJ_OD_array_iterator_step(SJ_OD_array_iterator* self) { - auto ptr = reinterpret_cast(self); +void SJ_OD_array_iterator_step(SJ_OD_array_iterator *self) { + auto ptr = reinterpret_cast(self); ++(*ptr); } - // ondemand::object IMPL_GET(SJ_OD_object, ondemand::object, SJ_OD_object_iterator, begin) IMPL_GET(SJ_OD_object, ondemand::object, SJ_OD_object_iterator, end) IMPL_GET(SJ_OD_object, ondemand::object, STD_string_view, raw_json) -SJ_OD_value_result* SJ_OD_object_at_pointer(SJ_OD_object* self, const char *s, size_t len) { - auto result = reinterpret_cast(self)->at_pointer(std::string_view(s, len)); - return object_to_pointer(std::move(result)); +SJ_OD_value_result *SJ_OD_object_at_pointer(SJ_OD_object *self, const char *s, + size_t len) { + auto result = reinterpret_cast(self)->at_pointer( + std::string_view(s, len)); + return object_to_pointer(std::move(result)); } // ondemand::object_iterator -SJ_OD_field_result* SJ_OD_object_iterator_get(SJ_OD_object_iterator* self) { - auto ptr = reinterpret_cast(self); - return object_to_pointer(**ptr); +SJ_OD_field_result *SJ_OD_object_iterator_get(SJ_OD_object_iterator *self) { + auto ptr = reinterpret_cast(self); + return object_to_pointer(**ptr); } -bool SJ_OD_object_iterator_not_equal(const SJ_OD_object_iterator* lhs, const SJ_OD_object_iterator* rhs) { - return *reinterpret_cast(lhs) != *reinterpret_cast(rhs); +bool SJ_OD_object_iterator_not_equal(const SJ_OD_object_iterator *lhs, + const SJ_OD_object_iterator *rhs) { + return *reinterpret_cast(lhs) != + *reinterpret_cast(rhs); } -void SJ_OD_object_iterator_step(SJ_OD_object_iterator* self) { - auto ptr = reinterpret_cast(self); +void SJ_OD_object_iterator_step(SJ_OD_object_iterator *self) { + auto ptr = reinterpret_cast(self); ++(*ptr); } // ondemand::field -STD_string_view_result* SJ_OD_field_unescaped_key(SJ_OD_field* self, bool allow_replacement) { - auto result = reinterpret_cast(self)->unescaped_key(allow_replacement); - return object_to_pointer(std::move(result)); +STD_string_view_result *SJ_OD_field_unescaped_key(SJ_OD_field *self, + bool allow_replacement) { + auto result = reinterpret_cast(self)->unescaped_key( + allow_replacement); + return object_to_pointer(std::move(result)); } -SJ_OD_value* SJ_OD_field_value(SJ_OD_field* self) { - ondemand::value& value = reinterpret_cast(self)->value(); - return reinterpret_cast(&value); +SJ_OD_value *SJ_OD_field_value(SJ_OD_field *self) { + ondemand::value &value = reinterpret_cast(self)->value(); + return reinterpret_cast(&value); } -SJ_OD_value* SJ_OD_field_take_value(SJ_OD_field* self) { - auto field = reinterpret_cast(self); +SJ_OD_value *SJ_OD_field_take_value(SJ_OD_field *self) { + auto field = reinterpret_cast(self); auto value = std::move(*field).value(); - return object_to_pointer(std::move(value)); + return object_to_pointer(std::move(value)); } diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index bc04dd1..10a9316 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -112,24 +112,28 @@ SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index); // ondemand::array_iterator DEFINE_GET(SJ_OD_array_iterator, SJ_OD_value, get) -bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator* lhs,const SJ_OD_array_iterator* rhs); -void SJ_OD_array_iterator_step(SJ_OD_array_iterator* self); +bool SJ_OD_array_iterator_not_equal(const SJ_OD_array_iterator *lhs, + const SJ_OD_array_iterator *rhs); +void SJ_OD_array_iterator_step(SJ_OD_array_iterator *self); // ondemand::object DEFINE_GET(SJ_OD_object, SJ_OD_object_iterator, begin) DEFINE_GET(SJ_OD_object, SJ_OD_object_iterator, end) DEFINE_GET(SJ_OD_object, STD_string_view, raw_json) -SJ_OD_value_result* SJ_OD_object_at_pointer(SJ_OD_object* self, const char *s, size_t len); +SJ_OD_value_result *SJ_OD_object_at_pointer(SJ_OD_object *self, const char *s, + size_t len); // ondemand::object_iterator DEFINE_GET(SJ_OD_object_iterator, SJ_OD_field, get) -bool SJ_OD_object_iterator_not_equal(const SJ_OD_object_iterator* lhs,const SJ_OD_object_iterator* rhs); -void SJ_OD_object_iterator_step(SJ_OD_object_iterator* self); +bool SJ_OD_object_iterator_not_equal(const SJ_OD_object_iterator *lhs, + const SJ_OD_object_iterator *rhs); +void SJ_OD_object_iterator_step(SJ_OD_object_iterator *self); // ondemand::field -STD_string_view_result* SJ_OD_field_unescaped_key(SJ_OD_field* self, bool allow_replacement); -SJ_OD_value* SJ_OD_field_value(SJ_OD_field* self); -SJ_OD_value* SJ_OD_field_take_value(SJ_OD_field* self); +STD_string_view_result *SJ_OD_field_unescaped_key(SJ_OD_field *self, + bool allow_replacement); +SJ_OD_value *SJ_OD_field_value(SJ_OD_field *self); +SJ_OD_value *SJ_OD_field_take_value(SJ_OD_field *self); #ifdef __cplusplus } diff --git a/src/constants.rs b/src/constants.rs deleted file mode 100644 index 1cf48c5..0000000 --- a/src/constants.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub const SIMDJSON_PADDING: usize = 64; -pub const SIMDJSON_MAXSIZE_BYTES: usize = 0xFFFFFFFF; diff --git a/src/lib.rs b/src/lib.rs index a803912..92ffcb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ mod macros; -pub mod constants; pub mod error; pub mod ondemand; pub mod padded_string; diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs index da9347d..b142052 100644 --- a/src/ondemand/array.rs +++ b/src/ondemand/array.rs @@ -9,16 +9,16 @@ use crate::{ use super::parser::Parser; -pub struct Array { +pub struct Array<'a> { ptr: NonNull, - // _document: PhantomData<&'d mut Document<'d, 'd>>, + _document: PhantomData<&'a mut Document<'a, 'a>>, } -impl Array { +impl<'a> Array<'a> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - // _document: PhantomData, + _document: PhantomData, } } @@ -73,4 +73,4 @@ impl Array { } } -impl_drop!(Array, ffi::SJ_OD_array_free); +impl_drop!(Array<'a>, ffi::SJ_OD_array_free); diff --git a/src/ondemand/array_iterator.rs b/src/ondemand/array_iterator.rs index 15fbac1..c98075b 100644 --- a/src/ondemand/array_iterator.rs +++ b/src/ondemand/array_iterator.rs @@ -3,16 +3,16 @@ use std::{marker::PhantomData, ptr::NonNull}; use crate::{error::Result, macros::map_result}; -use super::{array::Array, value::Value}; +use super::{array::Array, document::Document, value::Value}; -pub struct ArrayIterator { +pub struct ArrayIterator<'a> { begin: NonNull, end: NonNull, running: bool, - // _array: PhantomData<&'a mut Array<'a>>, + _array: PhantomData<&'a mut Array<'a>>, } -impl ArrayIterator { +impl<'a> ArrayIterator<'a> { pub fn new( begin: NonNull, end: NonNull, @@ -21,11 +21,11 @@ impl ArrayIterator { begin, end, running: false, - // _array: PhantomData, + _array: PhantomData, } } - pub fn get(&mut self) -> Result { + pub fn get(&mut self) -> Result> { map_result!( ffi::SJ_OD_array_iterator_get(self.begin.as_mut()), ffi::SJ_OD_value_result_error, @@ -43,7 +43,7 @@ impl ArrayIterator { } } -impl Drop for ArrayIterator { +impl<'a> Drop for ArrayIterator<'a> { fn drop(&mut self) { unsafe { ffi::SJ_OD_array_iterator_free(self.begin.as_mut()); @@ -52,8 +52,8 @@ impl Drop for ArrayIterator { } } -impl Iterator for ArrayIterator { - type Item = Result; +impl<'a> Iterator for ArrayIterator<'a> { + type Item = Result>; fn next(&mut self) -> Option { if self.running { diff --git a/src/ondemand/document.rs b/src/ondemand/document.rs index 36f7276..6736296 100644 --- a/src/ondemand/document.rs +++ b/src/ondemand/document.rs @@ -9,17 +9,17 @@ use crate::{ use super::{array::Array, object::Object, parser::Parser, value::Value}; -pub struct Document { +pub struct Document<'p, 's> { ptr: NonNull, - // _parser: PhantomData<&'p mut Parser>, - // _padded_string: PhantomData<&'s String>, + _parser: PhantomData<&'p mut Parser>, + _padded_string: PhantomData<&'s String>, } -impl Document { +impl<'p, 's> Document<'p, 's> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - // _parser: PhantomData, - // _padded_string: PhantomData, + _parser: PhantomData, + _padded_string: PhantomData, } } @@ -87,4 +87,4 @@ impl Document { } } -impl_drop!(Document, ffi::SJ_OD_document_free); +impl_drop!(Document<'p, 's>, ffi::SJ_OD_document_free); diff --git a/src/ondemand/field.rs b/src/ondemand/field.rs index a1553c5..616a425 100644 --- a/src/ondemand/field.rs +++ b/src/ondemand/field.rs @@ -1,19 +1,25 @@ use simdjson_sys as ffi; +use std::marker::PhantomData; use std::ptr::NonNull; use crate::error::Result; use crate::macros::{impl_drop, map_result}; use crate::utils::string_view_to_str; +use super::object::Object; use super::value::Value; -pub struct Field { +pub struct Field<'a> { ptr: NonNull, + _object: PhantomData<&'a mut Object<'a>>, } -impl Field { +impl<'a> Field<'a> { pub fn new(ptr: NonNull) -> Self { - Self { ptr } + Self { + ptr, + _object: PhantomData, + } } pub fn unescaped_key(&mut self, allow_replacement: bool) -> Result<&str> { @@ -44,7 +50,7 @@ impl Field { // Value::new(ptr) // } - pub fn take_value(self) -> Value { + pub fn take_value(self) -> Value<'a> { let ptr = unsafe { let ptr = ffi::SJ_OD_field_take_value(self.ptr.as_ptr()); NonNull::new_unchecked(ptr) @@ -54,4 +60,4 @@ impl Field { } } -impl_drop!(Field, ffi::SJ_OD_field_free); +impl_drop!(Field<'a> , ffi::SJ_OD_field_free); diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index bceed18..4ae17bf 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -10,16 +10,16 @@ use super::parser::Parser; use super::value::Value; use crate::error::Result; -pub struct Object { +pub struct Object<'a> { ptr: NonNull, - // _document: PhantomData<&'d mut Document<'d, 'd>>, + _document: PhantomData<&'a mut Document<'a, 'a>>, } -impl Object { +impl<'a> Object<'a> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - // _document: PhantomData, + _document: PhantomData, } } @@ -60,4 +60,4 @@ impl Object { } } -impl_drop!(Object, ffi::SJ_OD_object_free); +impl_drop!(Object<'a>, ffi::SJ_OD_object_free); diff --git a/src/ondemand/object_iterator.rs b/src/ondemand/object_iterator.rs index 9face69..4934c9e 100644 --- a/src/ondemand/object_iterator.rs +++ b/src/ondemand/object_iterator.rs @@ -3,16 +3,16 @@ use std::{marker::PhantomData, ptr::NonNull}; use crate::{error::Result, macros::map_result}; -use super::{field::Field, value::Value}; +use super::{field::Field, object::Object, value::Value}; -pub struct ObjectIterator { +pub struct ObjectIterator<'a> { begin: NonNull, end: NonNull, running: bool, - // _array: PhantomData<&'a mut Array<'a>>, + _object: PhantomData<&'a mut Object<'a>>, } -impl ObjectIterator { +impl<'a> ObjectIterator<'a> { pub fn new( begin: NonNull, end: NonNull, @@ -21,11 +21,11 @@ impl ObjectIterator { begin, end, running: false, - // _array: PhantomData, + _object: PhantomData, } } - pub fn get(&mut self) -> Result { + pub fn get(&mut self) -> Result> { map_result!( ffi::SJ_OD_object_iterator_get(self.begin.as_mut()), ffi::SJ_OD_field_result_error, @@ -43,7 +43,7 @@ impl ObjectIterator { } } -impl Drop for ObjectIterator { +impl<'a> Drop for ObjectIterator<'a> { fn drop(&mut self) { unsafe { ffi::SJ_OD_object_iterator_free(self.begin.as_mut()); @@ -52,8 +52,8 @@ impl Drop for ObjectIterator { } } -impl Iterator for ObjectIterator { - type Item = Result; +impl<'a> Iterator for ObjectIterator<'a> { + type Item = Result>; fn next(&mut self) -> Option { if self.running { diff --git a/src/ondemand/parser.rs b/src/ondemand/parser.rs index fdfa812..d75c581 100644 --- a/src/ondemand/parser.rs +++ b/src/ondemand/parser.rs @@ -3,7 +3,6 @@ use std::ptr::NonNull; use simdjson_sys as ffi; use crate::{ - constants::SIMDJSON_MAXSIZE_BYTES, error::Result, macros::{impl_drop, map_result}, }; @@ -16,7 +15,7 @@ pub struct Parser { impl Default for Parser { fn default() -> Self { - Parser::new(SIMDJSON_MAXSIZE_BYTES) + Parser::new(ffi::SIMDJSON_MAXSIZE_BYTES) } } @@ -26,7 +25,7 @@ impl Parser { Self { ptr } } - pub fn iterate(&mut self, padded_string: &String) -> Result { + pub fn iterate<'p, 's>(&'p mut self, padded_string: &'s String) -> Result> { map_result!( ffi::SJ_OD_parser_iterate_padded_string_view( self.ptr.as_mut(), @@ -54,9 +53,18 @@ mod tests { let mut parser = Parser::default(); let ps = make_padded_string("[1,2,3]"); let mut doc = parser.iterate(&ps).unwrap(); - doc.get_array().unwrap(); + // drop(ps); + // doc.get_array().unwrap(); + let mut arr = doc.get_array().unwrap(); + // drop(doc); + // for v in arr.iter().unwrap() { + // let _ = v.unwrap().get_uint64().unwrap(); + // } // doc.get_value().unwrap(); + + drop(arr); drop(doc); + let ps2 = make_padded_string("1"); let mut doc2 = parser.iterate(&ps2).unwrap(); let v = doc2.get_int64().unwrap(); diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs index de62db1..960f6ce 100644 --- a/src/ondemand/value.rs +++ b/src/ondemand/value.rs @@ -7,16 +7,16 @@ use super::document::Document; use super::{array::Array, object::Object}; use crate::error::Result; -pub struct Value { +pub struct Value<'a> { ptr: NonNull, - // _document: PhantomData<&'d mut Document<'d, 'd>>, + _document: PhantomData<&'a mut Document<'a, 'a>>, } -impl Value { +impl<'a> Value<'a> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - // _document: PhantomData, + _document: PhantomData, } } @@ -75,4 +75,4 @@ impl Value { } } -impl_drop!(Value, ffi::SJ_OD_value_free); +impl_drop!(Value<'a>, ffi::SJ_OD_value_free); diff --git a/src/padded_string.rs b/src/padded_string.rs index 3e3ada2..2197c11 100644 --- a/src/padded_string.rs +++ b/src/padded_string.rs @@ -1,55 +1,9 @@ -use std::{io::Read, os::unix::prelude::OsStrExt, path::Path, ptr::NonNull}; +use std::{io::Read, path::Path}; use simdjson_sys as ffi; -use crate::{ - constants::SIMDJSON_PADDING, - error::Result, - macros::{impl_drop, map_result}, -}; - -// TODO: we will remove PaddedString because String is enough. -pub struct PaddedString { - ptr: NonNull, -} - -impl PaddedString { - pub fn new(s: &str) -> Self { - let ptr = unsafe { - let ptr = ffi::SJ_padded_string_new(s.as_ptr().cast(), s.len()); - NonNull::new_unchecked(ptr) - }; - Self { ptr } - } - - pub fn load>(path: P) -> Result { - let bytes = path.as_ref().as_os_str().as_bytes(); - let c_path = std::ffi::CString::new(bytes).unwrap(); - map_result!( - ffi::SJ_padded_string_load(c_path.as_ptr()), - ffi::SJ_padded_string_result_error, - ffi::SJ_padded_string_result_value_unsafe - ) - .map(|ptr| PaddedString { ptr }) - } - - pub fn len(&self) -> usize { - unsafe { ffi::SJ_padded_string_length(self.ptr.as_ref()) } - } - - pub fn as_str(&self) -> &str { - unsafe { - let data = ffi::SJ_padded_string_u8data(self.ptr.as_ref()); - let s = std::slice::from_raw_parts(data, self.len()); - std::str::from_utf8_unchecked(s) - } - } -} - -impl_drop!(PaddedString, ffi::SJ_padded_string_free); - pub fn make_padded_string(s: &str) -> String { - let mut ps = String::with_capacity(s.len() + SIMDJSON_PADDING); + let mut ps = String::with_capacity(s.len() + ffi::SIMDJSON_PADDING); ps.push_str(s); ps } @@ -57,19 +11,10 @@ pub fn make_padded_string(s: &str) -> String { pub fn load_padded_string>(path: P) -> std::io::Result { let mut file = std::fs::File::open(path)?; let len = file.metadata()?.len() as usize; - let mut buf = String::with_capacity(len + SIMDJSON_PADDING); + let mut buf = String::with_capacity(len + ffi::SIMDJSON_PADDING); file.read_to_string(&mut buf)?; Ok(buf) } #[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_new() { - let s = "{}"; - let ps = PaddedString::new(s); - assert_eq!(s, ps.as_str()); - } -} +mod tests {} From ae9fa31b4c07dfe97f300e94e0afe9e58302f950 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 19:27:00 +0800 Subject: [PATCH 26/31] fix lifetime --- examples/issue_20.rs | 21 ++++++++++----------- src/ondemand/array.rs | 10 +++++----- src/ondemand/array_iterator.rs | 17 +++++++++-------- src/ondemand/field.rs | 12 ++++++------ src/ondemand/object.rs | 10 +++++----- src/ondemand/object_iterator.rs | 16 ++++++++-------- src/ondemand/value.rs | 10 +++++----- 7 files changed, 48 insertions(+), 48 deletions(-) diff --git a/examples/issue_20.rs b/examples/issue_20.rs index 41e8357..8c96e02 100644 --- a/examples/issue_20.rs +++ b/examples/issue_20.rs @@ -7,20 +7,19 @@ fn main() -> Result<()> { let padded_string = make_padded_string(data); let mut doc = parser.iterate(&padded_string)?; - // let mut arr = doc.get_object()?.at_pointer("/ingredients")?.get_array()?; + let mut arr = doc.get_object()?.at_pointer("/ingredients")?.get_array()?; - // for value in arr.iter()? { - // let mut object = value?.get_object()?; + for value in arr.iter()? { + let mut object = value?.get_object()?; - // for field in object.iter()? { - // let mut field = field?; + for field in object.iter()? { + let mut field = field?; - - // let key = field.unescaped_key(false)?.to_owned(); - // let mut value = field.take_value(); - // println!("key: {} | value: {}", key, value.get_object()?.raw_json()?); - // } - // } + let key = field.unescaped_key(false)?.to_owned(); + let mut value = field.take_value(); + println!("key: {} | value: {}", key, value.get_object()?.raw_json()?); + } + } Ok(()) } diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs index b142052..1de16dc 100644 --- a/src/ondemand/array.rs +++ b/src/ondemand/array.rs @@ -9,16 +9,16 @@ use crate::{ use super::parser::Parser; -pub struct Array<'a> { +pub struct Array { ptr: NonNull, - _document: PhantomData<&'a mut Document<'a, 'a>>, + // _document: PhantomData<&'a mut Document<'a>>, } -impl<'a> Array<'a> { +impl Array { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _document: PhantomData, + // _document: PhantomData, } } @@ -73,4 +73,4 @@ impl<'a> Array<'a> { } } -impl_drop!(Array<'a>, ffi::SJ_OD_array_free); +impl_drop!(Array, ffi::SJ_OD_array_free); diff --git a/src/ondemand/array_iterator.rs b/src/ondemand/array_iterator.rs index c98075b..7d6d17f 100644 --- a/src/ondemand/array_iterator.rs +++ b/src/ondemand/array_iterator.rs @@ -5,14 +5,14 @@ use crate::{error::Result, macros::map_result}; use super::{array::Array, document::Document, value::Value}; -pub struct ArrayIterator<'a> { +pub struct ArrayIterator { begin: NonNull, end: NonNull, running: bool, - _array: PhantomData<&'a mut Array<'a>>, + // _array: PhantomData<&'a mut Array>, } -impl<'a> ArrayIterator<'a> { +impl ArrayIterator { pub fn new( begin: NonNull, end: NonNull, @@ -21,11 +21,11 @@ impl<'a> ArrayIterator<'a> { begin, end, running: false, - _array: PhantomData, + // _array: PhantomData, } } - pub fn get(&mut self) -> Result> { + pub fn get(&mut self) -> Result { map_result!( ffi::SJ_OD_array_iterator_get(self.begin.as_mut()), ffi::SJ_OD_value_result_error, @@ -43,7 +43,7 @@ impl<'a> ArrayIterator<'a> { } } -impl<'a> Drop for ArrayIterator<'a> { +impl Drop for ArrayIterator { fn drop(&mut self) { unsafe { ffi::SJ_OD_array_iterator_free(self.begin.as_mut()); @@ -52,8 +52,8 @@ impl<'a> Drop for ArrayIterator<'a> { } } -impl<'a> Iterator for ArrayIterator<'a> { - type Item = Result>; +impl Iterator for ArrayIterator { + type Item = Result; fn next(&mut self) -> Option { if self.running { @@ -81,6 +81,7 @@ mod tests { let mut parser = Parser::default(); let ps = make_padded_string("[1,2,3]"); let mut doc = parser.iterate(&ps).unwrap(); + // drop(ps); let mut arr = doc.get_array().unwrap(); for (v, num) in arr.iter().unwrap().zip([1u64, 2, 3]) { let res = v.unwrap().get_uint64().unwrap(); diff --git a/src/ondemand/field.rs b/src/ondemand/field.rs index 616a425..0b86ab7 100644 --- a/src/ondemand/field.rs +++ b/src/ondemand/field.rs @@ -9,16 +9,16 @@ use crate::utils::string_view_to_str; use super::object::Object; use super::value::Value; -pub struct Field<'a> { +pub struct Field { ptr: NonNull, - _object: PhantomData<&'a mut Object<'a>>, + // _object: PhantomData<&'a mut Object>, } -impl<'a> Field<'a> { +impl Field { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _object: PhantomData, + // _object: PhantomData, } } @@ -50,7 +50,7 @@ impl<'a> Field<'a> { // Value::new(ptr) // } - pub fn take_value(self) -> Value<'a> { + pub fn take_value(self) -> Value { let ptr = unsafe { let ptr = ffi::SJ_OD_field_take_value(self.ptr.as_ptr()); NonNull::new_unchecked(ptr) @@ -60,4 +60,4 @@ impl<'a> Field<'a> { } } -impl_drop!(Field<'a> , ffi::SJ_OD_field_free); +impl_drop!(Field, ffi::SJ_OD_field_free); diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index 4ae17bf..d3a4282 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -10,16 +10,16 @@ use super::parser::Parser; use super::value::Value; use crate::error::Result; -pub struct Object<'a> { +pub struct Object { ptr: NonNull, - _document: PhantomData<&'a mut Document<'a, 'a>>, + // _document: PhantomData<&'a mut Document<'a, 'a>>, } -impl<'a> Object<'a> { +impl Object { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _document: PhantomData, + // _document: PhantomData, } } @@ -60,4 +60,4 @@ impl<'a> Object<'a> { } } -impl_drop!(Object<'a>, ffi::SJ_OD_object_free); +impl_drop!(Object, ffi::SJ_OD_object_free); diff --git a/src/ondemand/object_iterator.rs b/src/ondemand/object_iterator.rs index 4934c9e..2c9fe63 100644 --- a/src/ondemand/object_iterator.rs +++ b/src/ondemand/object_iterator.rs @@ -5,14 +5,14 @@ use crate::{error::Result, macros::map_result}; use super::{field::Field, object::Object, value::Value}; -pub struct ObjectIterator<'a> { +pub struct ObjectIterator { begin: NonNull, end: NonNull, running: bool, - _object: PhantomData<&'a mut Object<'a>>, + // _object: PhantomData<&'a mut Object>, } -impl<'a> ObjectIterator<'a> { +impl ObjectIterator { pub fn new( begin: NonNull, end: NonNull, @@ -21,11 +21,11 @@ impl<'a> ObjectIterator<'a> { begin, end, running: false, - _object: PhantomData, + // _object: PhantomData, } } - pub fn get(&mut self) -> Result> { + pub fn get(&mut self) -> Result { map_result!( ffi::SJ_OD_object_iterator_get(self.begin.as_mut()), ffi::SJ_OD_field_result_error, @@ -43,7 +43,7 @@ impl<'a> ObjectIterator<'a> { } } -impl<'a> Drop for ObjectIterator<'a> { +impl Drop for ObjectIterator { fn drop(&mut self) { unsafe { ffi::SJ_OD_object_iterator_free(self.begin.as_mut()); @@ -52,8 +52,8 @@ impl<'a> Drop for ObjectIterator<'a> { } } -impl<'a> Iterator for ObjectIterator<'a> { - type Item = Result>; +impl Iterator for ObjectIterator { + type Item = Result; fn next(&mut self) -> Option { if self.running { diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs index 960f6ce..dd5c28b 100644 --- a/src/ondemand/value.rs +++ b/src/ondemand/value.rs @@ -7,16 +7,16 @@ use super::document::Document; use super::{array::Array, object::Object}; use crate::error::Result; -pub struct Value<'a> { +pub struct Value { ptr: NonNull, - _document: PhantomData<&'a mut Document<'a, 'a>>, + // _document: PhantomData<&'a mut Document<'a, 'a>>, } -impl<'a> Value<'a> { +impl Value { pub fn new(ptr: NonNull) -> Self { Self { ptr, - _document: PhantomData, + // _document: PhantomData, } } @@ -75,4 +75,4 @@ impl<'a> Value<'a> { } } -impl_drop!(Value<'a>, ffi::SJ_OD_value_free); +impl_drop!(Value, ffi::SJ_OD_value_free); From b7a617f59dc79aea84bbd6afbc30b514380333a0 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 20:08:13 +0800 Subject: [PATCH 27/31] update lifetime --- examples/quickstart.rs | 2 +- src/error.rs | 3 +++ src/ondemand/array.rs | 14 ++++++-------- src/ondemand/array_iterator.rs | 18 +++++++++--------- src/ondemand/document.rs | 6 +++--- src/ondemand/field.rs | 13 +++++++------ src/ondemand/object.rs | 16 ++++++++-------- src/ondemand/object_iterator.rs | 18 +++++++++--------- src/ondemand/value.rs | 14 +++++++------- 9 files changed, 53 insertions(+), 51 deletions(-) diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 8363152..2404099 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -11,7 +11,7 @@ fn main() -> Result<()> { // .get_u64()? // ); - let ps = load_padded_string("simdjson-sys/simdjson/jsonexamples/twitter.json").unwrap(); + let ps = load_padded_string("simdjson-sys/simdjson/jsonexamples/twitter.json")?; let mut parser = Parser::default(); let tweets = parser.iterate(&ps)?; diff --git a/src/error.rs b/src/error.rs index d234102..768353a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -96,6 +96,9 @@ pub enum SimdJsonError { #[error("todo")] NumErrorCodes, + + #[error("todo")] + StdIoError(#[from] std::io::Error), } impl From for SimdJsonError { diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs index 1de16dc..27c7b0e 100644 --- a/src/ondemand/array.rs +++ b/src/ondemand/array.rs @@ -7,18 +7,16 @@ use crate::{ macros::{impl_drop, map_result}, }; -use super::parser::Parser; - -pub struct Array { +pub struct Array<'a> { ptr: NonNull, - // _document: PhantomData<&'a mut Document<'a>>, + _doc: PhantomData<&'a mut Document<'a, 'a>>, } -impl Array { +impl<'a> Array<'a> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - // _document: PhantomData, + _doc: PhantomData, } } @@ -40,7 +38,7 @@ impl Array { ) } - pub fn at(&mut self, index: usize) -> Result { + pub fn at(&mut self, index: usize) -> Result> { map_result!( ffi::SJ_OD_array_at(self.ptr.as_mut(), index), ffi::SJ_OD_value_result_error, @@ -73,4 +71,4 @@ impl Array { } } -impl_drop!(Array, ffi::SJ_OD_array_free); +impl_drop!(Array<'a>, ffi::SJ_OD_array_free); diff --git a/src/ondemand/array_iterator.rs b/src/ondemand/array_iterator.rs index 7d6d17f..6f8cfb7 100644 --- a/src/ondemand/array_iterator.rs +++ b/src/ondemand/array_iterator.rs @@ -3,16 +3,16 @@ use std::{marker::PhantomData, ptr::NonNull}; use crate::{error::Result, macros::map_result}; -use super::{array::Array, document::Document, value::Value}; +use super::{document::Document, value::Value}; -pub struct ArrayIterator { +pub struct ArrayIterator<'a> { begin: NonNull, end: NonNull, running: bool, - // _array: PhantomData<&'a mut Array>, + _doc: PhantomData<&'a mut Document<'a, 'a>>, } -impl ArrayIterator { +impl<'a> ArrayIterator<'a> { pub fn new( begin: NonNull, end: NonNull, @@ -21,11 +21,11 @@ impl ArrayIterator { begin, end, running: false, - // _array: PhantomData, + _doc: PhantomData, } } - pub fn get(&mut self) -> Result { + pub fn get(&mut self) -> Result> { map_result!( ffi::SJ_OD_array_iterator_get(self.begin.as_mut()), ffi::SJ_OD_value_result_error, @@ -43,7 +43,7 @@ impl ArrayIterator { } } -impl Drop for ArrayIterator { +impl<'a> Drop for ArrayIterator<'a> { fn drop(&mut self) { unsafe { ffi::SJ_OD_array_iterator_free(self.begin.as_mut()); @@ -52,8 +52,8 @@ impl Drop for ArrayIterator { } } -impl Iterator for ArrayIterator { - type Item = Result; +impl<'a> Iterator for ArrayIterator<'a> { + type Item = Result>; fn next(&mut self) -> Option { if self.running { diff --git a/src/ondemand/document.rs b/src/ondemand/document.rs index 6736296..810a3ad 100644 --- a/src/ondemand/document.rs +++ b/src/ondemand/document.rs @@ -59,7 +59,7 @@ impl<'p, 's> Document<'p, 's> { ) } - pub fn get_value(&mut self) -> Result { + pub fn get_value<'a>(&mut self) -> Result> { map_result!( ffi::SJ_OD_document_get_value(self.ptr.as_mut()), ffi::SJ_OD_value_result_error, @@ -68,7 +68,7 @@ impl<'p, 's> Document<'p, 's> { .map(Value::new) } - pub fn get_array(&mut self) -> Result { + pub fn get_array<'a>(&mut self) -> Result> { map_result!( ffi::SJ_OD_document_get_array(self.ptr.as_mut()), ffi::SJ_OD_array_result_error, @@ -77,7 +77,7 @@ impl<'p, 's> Document<'p, 's> { .map(Array::new) } - pub fn get_object(&mut self) -> Result { + pub fn get_object<'a>(&mut self) -> Result> { map_result!( ffi::SJ_OD_document_get_object(self.ptr.as_mut()), ffi::SJ_OD_object_result_error, diff --git a/src/ondemand/field.rs b/src/ondemand/field.rs index 0b86ab7..b9a66ab 100644 --- a/src/ondemand/field.rs +++ b/src/ondemand/field.rs @@ -6,19 +6,20 @@ use crate::error::Result; use crate::macros::{impl_drop, map_result}; use crate::utils::string_view_to_str; +use super::document::Document; use super::object::Object; use super::value::Value; -pub struct Field { +pub struct Field<'a> { ptr: NonNull, - // _object: PhantomData<&'a mut Object>, + _doc: PhantomData<&'a mut Document<'a, 'a>>, } -impl Field { +impl<'a> Field<'a> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - // _object: PhantomData, + _doc: PhantomData, } } @@ -50,7 +51,7 @@ impl Field { // Value::new(ptr) // } - pub fn take_value(self) -> Value { + pub fn take_value(self) -> Value<'a> { let ptr = unsafe { let ptr = ffi::SJ_OD_field_take_value(self.ptr.as_ptr()); NonNull::new_unchecked(ptr) @@ -60,4 +61,4 @@ impl Field { } } -impl_drop!(Field, ffi::SJ_OD_field_free); +impl_drop!(Field<'a>, ffi::SJ_OD_field_free); diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index d3a4282..e6ea86f 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -10,20 +10,20 @@ use super::parser::Parser; use super::value::Value; use crate::error::Result; -pub struct Object { +pub struct Object<'a> { ptr: NonNull, - // _document: PhantomData<&'a mut Document<'a, 'a>>, + _doc: PhantomData<&'a mut Document<'a, 'a>>, } -impl Object { +impl<'a> Object<'a> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - // _document: PhantomData, + _doc: PhantomData, } } - pub fn at_pointer(&mut self, json_pointer: &str) -> Result { + pub fn at_pointer(&mut self, json_pointer: &str) -> Result> { map_result!( ffi::SJ_OD_object_at_pointer( self.ptr.as_mut(), @@ -36,7 +36,7 @@ impl Object { .map(Value::new) } - pub fn iter(&mut self) -> Result { + pub fn iter(&mut self) -> Result> { let begin = map_result!( ffi::SJ_OD_object_begin(self.ptr.as_mut()), ffi::SJ_OD_object_iterator_result_error, @@ -50,7 +50,7 @@ impl Object { Ok(ObjectIterator::new(begin, end)) } - pub fn raw_json(&mut self) -> Result<&str> { + pub fn raw_json(&mut self) -> Result<&'a str> { let sv = map_result!( ffi::SJ_OD_object_raw_json(self.ptr.as_mut()), ffi::STD_string_view_result_error, @@ -60,4 +60,4 @@ impl Object { } } -impl_drop!(Object, ffi::SJ_OD_object_free); +impl_drop!(Object<'a>, ffi::SJ_OD_object_free); diff --git a/src/ondemand/object_iterator.rs b/src/ondemand/object_iterator.rs index 2c9fe63..68636a5 100644 --- a/src/ondemand/object_iterator.rs +++ b/src/ondemand/object_iterator.rs @@ -3,16 +3,16 @@ use std::{marker::PhantomData, ptr::NonNull}; use crate::{error::Result, macros::map_result}; -use super::{field::Field, object::Object, value::Value}; +use super::{document::Document, field::Field, object::Object, value::Value}; -pub struct ObjectIterator { +pub struct ObjectIterator<'a> { begin: NonNull, end: NonNull, running: bool, - // _object: PhantomData<&'a mut Object>, + _doc: PhantomData<&'a mut Document<'a, 'a>>, } -impl ObjectIterator { +impl<'a> ObjectIterator<'a> { pub fn new( begin: NonNull, end: NonNull, @@ -21,11 +21,11 @@ impl ObjectIterator { begin, end, running: false, - // _object: PhantomData, + _doc: PhantomData, } } - pub fn get(&mut self) -> Result { + pub fn get(&mut self) -> Result> { map_result!( ffi::SJ_OD_object_iterator_get(self.begin.as_mut()), ffi::SJ_OD_field_result_error, @@ -43,7 +43,7 @@ impl ObjectIterator { } } -impl Drop for ObjectIterator { +impl<'a> Drop for ObjectIterator<'a> { fn drop(&mut self) { unsafe { ffi::SJ_OD_object_iterator_free(self.begin.as_mut()); @@ -52,8 +52,8 @@ impl Drop for ObjectIterator { } } -impl Iterator for ObjectIterator { - type Item = Result; +impl<'a> Iterator for ObjectIterator<'a> { + type Item = Result>; fn next(&mut self) -> Option { if self.running { diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs index dd5c28b..25233ad 100644 --- a/src/ondemand/value.rs +++ b/src/ondemand/value.rs @@ -7,16 +7,16 @@ use super::document::Document; use super::{array::Array, object::Object}; use crate::error::Result; -pub struct Value { +pub struct Value<'a> { ptr: NonNull, - // _document: PhantomData<&'a mut Document<'a, 'a>>, + _doc: PhantomData<&'a mut Document<'a, 'a>>, } -impl Value { +impl<'a> Value<'a> { pub fn new(ptr: NonNull) -> Self { Self { ptr, - // _document: PhantomData, + _doc: PhantomData, } } @@ -56,7 +56,7 @@ impl Value { ) } - pub fn get_array(&mut self) -> Result { + pub fn get_array(&mut self) -> Result> { map_result!( ffi::SJ_OD_value_get_array(self.ptr.as_mut()), ffi::SJ_OD_array_result_error, @@ -65,7 +65,7 @@ impl Value { .map(Array::new) } - pub fn get_object(&mut self) -> Result { + pub fn get_object(&mut self) -> Result> { map_result!( ffi::SJ_OD_value_get_object(self.ptr.as_mut()), ffi::SJ_OD_object_result_error, @@ -75,4 +75,4 @@ impl Value { } } -impl_drop!(Value, ffi::SJ_OD_value_free); +impl_drop!(Value<'a>, ffi::SJ_OD_value_free); From f81504a5f0b35b7b179d54387aa2e097039502d0 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 20:09:59 +0800 Subject: [PATCH 28/31] clippy fix --- examples/quickstart.rs | 2 +- src/ondemand/array_iterator.rs | 2 +- src/ondemand/field.rs | 2 +- src/ondemand/object.rs | 2 +- src/ondemand/object_iterator.rs | 2 +- src/ondemand/parser.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 2404099..16ad596 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -13,7 +13,7 @@ fn main() -> Result<()> { let ps = load_padded_string("simdjson-sys/simdjson/jsonexamples/twitter.json")?; let mut parser = Parser::default(); - let tweets = parser.iterate(&ps)?; + let _tweets = parser.iterate(&ps)?; Ok(()) } diff --git a/src/ondemand/array_iterator.rs b/src/ondemand/array_iterator.rs index 6f8cfb7..b9a3c70 100644 --- a/src/ondemand/array_iterator.rs +++ b/src/ondemand/array_iterator.rs @@ -74,7 +74,7 @@ impl<'a> Iterator for ArrayIterator<'a> { mod tests { use crate::{ondemand::parser::Parser, padded_string::make_padded_string}; - use super::*; + #[test] fn test_iter() { diff --git a/src/ondemand/field.rs b/src/ondemand/field.rs index b9a66ab..1f70992 100644 --- a/src/ondemand/field.rs +++ b/src/ondemand/field.rs @@ -7,7 +7,7 @@ use crate::macros::{impl_drop, map_result}; use crate::utils::string_view_to_str; use super::document::Document; -use super::object::Object; + use super::value::Value; pub struct Field<'a> { diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index e6ea86f..ac69cc1 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -6,7 +6,7 @@ use crate::utils::string_view_to_str; use super::document::Document; use super::object_iterator::ObjectIterator; -use super::parser::Parser; + use super::value::Value; use crate::error::Result; diff --git a/src/ondemand/object_iterator.rs b/src/ondemand/object_iterator.rs index 68636a5..2efbc70 100644 --- a/src/ondemand/object_iterator.rs +++ b/src/ondemand/object_iterator.rs @@ -3,7 +3,7 @@ use std::{marker::PhantomData, ptr::NonNull}; use crate::{error::Result, macros::map_result}; -use super::{document::Document, field::Field, object::Object, value::Value}; +use super::{document::Document, field::Field}; pub struct ObjectIterator<'a> { begin: NonNull, diff --git a/src/ondemand/parser.rs b/src/ondemand/parser.rs index d75c581..b572492 100644 --- a/src/ondemand/parser.rs +++ b/src/ondemand/parser.rs @@ -55,7 +55,7 @@ mod tests { let mut doc = parser.iterate(&ps).unwrap(); // drop(ps); // doc.get_array().unwrap(); - let mut arr = doc.get_array().unwrap(); + let arr = doc.get_array().unwrap(); // drop(doc); // for v in arr.iter().unwrap() { // let _ = v.unwrap().get_uint64().unwrap(); From 0bd2e8562746bd7d5df47e01bdbe065cc79e4746 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 22:09:24 +0800 Subject: [PATCH 29/31] add more methods --- simdjson-sys/src/simdjson_c_api.cpp | 10 ++++++++ simdjson-sys/src/simdjson_c_api.h | 5 ++++ src/ondemand/mod.rs | 1 - src/ondemand/object.rs | 36 +++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index 2bc8de5..502b56d 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -187,6 +187,9 @@ void SJ_OD_array_iterator_step(SJ_OD_array_iterator *self) { IMPL_GET(SJ_OD_object, ondemand::object, SJ_OD_object_iterator, begin) IMPL_GET(SJ_OD_object, ondemand::object, SJ_OD_object_iterator, end) IMPL_GET(SJ_OD_object, ondemand::object, STD_string_view, raw_json) +IMPL_GET(SJ_OD_object, ondemand::object, bool, is_empty) +IMPL_GET(SJ_OD_object, ondemand::object, bool, reset) +IMPL_GET(SJ_OD_object, ondemand::object, size_t, count_fields) SJ_OD_value_result *SJ_OD_object_at_pointer(SJ_OD_object *self, const char *s, size_t len) { auto result = reinterpret_cast(self)->at_pointer( @@ -194,6 +197,13 @@ SJ_OD_value_result *SJ_OD_object_at_pointer(SJ_OD_object *self, const char *s, return object_to_pointer(std::move(result)); } +SJ_OD_value_result *SJ_OD_object_find_field(SJ_OD_object *object, + const char *data, size_t len) { + auto result = reinterpret_cast(object)->find_field( + std::string_view(data, len)); + return object_to_pointer(std::move(result)); +} + // ondemand::object_iterator SJ_OD_field_result *SJ_OD_object_iterator_get(SJ_OD_object_iterator *self) { auto ptr = reinterpret_cast(self); diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index 10a9316..deee681 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -120,8 +120,13 @@ void SJ_OD_array_iterator_step(SJ_OD_array_iterator *self); DEFINE_GET(SJ_OD_object, SJ_OD_object_iterator, begin) DEFINE_GET(SJ_OD_object, SJ_OD_object_iterator, end) DEFINE_GET(SJ_OD_object, STD_string_view, raw_json) +DEFINE_GET(SJ_OD_object, bool, is_empty) +DEFINE_GET(SJ_OD_object, bool, reset) +DEFINE_GET(SJ_OD_object, size_t, count_fields) SJ_OD_value_result *SJ_OD_object_at_pointer(SJ_OD_object *self, const char *s, size_t len); +SJ_OD_value_result *SJ_OD_object_find_field(SJ_OD_object *object, + const char *data, size_t len); // ondemand::object_iterator DEFINE_GET(SJ_OD_object_iterator, SJ_OD_field, get) diff --git a/src/ondemand/mod.rs b/src/ondemand/mod.rs index 8250d67..63fc86d 100644 --- a/src/ondemand/mod.rs +++ b/src/ondemand/mod.rs @@ -5,5 +5,4 @@ pub mod field; pub mod object; pub mod object_iterator; pub mod parser; - pub mod value; diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index ac69cc1..878d14d 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -58,6 +58,42 @@ impl<'a> Object<'a> { )?; Ok(string_view_to_str(sv)) } + + pub fn find_field(&mut self, key: &str) -> Result> { + map_result!( + ffi::SJ_OD_object_find_field(self.ptr.as_mut(), key.as_ptr().cast(), key.len()), + ffi::SJ_OD_value_result_error, + ffi::SJ_OD_value_result_value_unsafe + ) + .map(Value::new) + } + + pub fn count_fields(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_object_count_fields(self.ptr.as_mut()), + ffi::size_t_result_error, + ffi::size_t_result_value_unsafe + ) + } + + pub fn is_empty(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_object_is_empty(self.ptr.as_mut()), + ffi::bool_result_error, + ffi::bool_result_value_unsafe + ) + } + + pub fn reset(&mut self) -> Result { + map_result!( + primitive, + ffi::SJ_OD_object_reset(self.ptr.as_mut()), + ffi::bool_result_error, + ffi::bool_result_value_unsafe + ) + } } impl_drop!(Object<'a>, ffi::SJ_OD_object_free); From a10d2ec4b5711cdbf6036c2157a74894f9b30d93 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 23:35:22 +0800 Subject: [PATCH 30/31] add get_string --- simdjson-sys/src/simdjson_c_api.cpp | 16 ++++++++++++++++ simdjson-sys/src/simdjson_c_api.h | 6 ++++++ src/ondemand/array.rs | 19 +++++++++++++++++++ src/ondemand/document.rs | 19 +++++++++++++++++++ src/ondemand/object.rs | 13 +++++++++++++ src/ondemand/value.rs | 10 ++++++++++ src/utils.rs | 2 +- 7 files changed, 84 insertions(+), 1 deletion(-) diff --git a/simdjson-sys/src/simdjson_c_api.cpp b/simdjson-sys/src/simdjson_c_api.cpp index 502b56d..91c8cc6 100644 --- a/simdjson-sys/src/simdjson_c_api.cpp +++ b/simdjson-sys/src/simdjson_c_api.cpp @@ -162,12 +162,20 @@ IMPL_GET(SJ_OD_array, ondemand::array, bool, is_empty) IMPL_GET(SJ_OD_array, ondemand::array, bool, reset) IMPL_GET(SJ_OD_array, ondemand::array, SJ_OD_array_iterator, begin) IMPL_GET(SJ_OD_array, ondemand::array, SJ_OD_array_iterator, end) +IMPL_GET(SJ_OD_array, ondemand::array, STD_string_view, raw_json) SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index) { auto result = reinterpret_cast(array)->at(index); return object_to_pointer(std::move(result)); } +SJ_OD_value_result *SJ_OD_array_at_pointer(SJ_OD_array *self, const char *s, + size_t len) { + auto result = reinterpret_cast(self)->at_pointer( + std::string_view(s, len)); + return object_to_pointer(std::move(result)); +} + // ondemand::array_iterator SJ_OD_value_result *SJ_OD_array_iterator_get(SJ_OD_array_iterator *self) { auto ptr = reinterpret_cast(self); @@ -203,6 +211,14 @@ SJ_OD_value_result *SJ_OD_object_find_field(SJ_OD_object *object, std::string_view(data, len)); return object_to_pointer(std::move(result)); } +SJ_OD_value_result *SJ_OD_object_find_field_unordered(SJ_OD_object *object, + const char *data, + size_t len) { + auto result = + reinterpret_cast(object)->find_field_unordered( + std::string_view(data, len)); + return object_to_pointer(std::move(result)); +} // ondemand::object_iterator SJ_OD_field_result *SJ_OD_object_iterator_get(SJ_OD_object_iterator *self) { diff --git a/simdjson-sys/src/simdjson_c_api.h b/simdjson-sys/src/simdjson_c_api.h index deee681..01e4016 100644 --- a/simdjson-sys/src/simdjson_c_api.h +++ b/simdjson-sys/src/simdjson_c_api.h @@ -108,7 +108,10 @@ DEFINE_GET(SJ_OD_array, bool, is_empty) DEFINE_GET(SJ_OD_array, bool, reset) DEFINE_GET(SJ_OD_array, SJ_OD_array_iterator, begin) DEFINE_GET(SJ_OD_array, SJ_OD_array_iterator, end) +DEFINE_GET(SJ_OD_array, STD_string_view, raw_json) SJ_OD_value_result *SJ_OD_array_at(SJ_OD_array *array, size_t index); +SJ_OD_value_result *SJ_OD_array_at_pointer(SJ_OD_array *self, const char *s, + size_t len); // ondemand::array_iterator DEFINE_GET(SJ_OD_array_iterator, SJ_OD_value, get) @@ -127,6 +130,9 @@ SJ_OD_value_result *SJ_OD_object_at_pointer(SJ_OD_object *self, const char *s, size_t len); SJ_OD_value_result *SJ_OD_object_find_field(SJ_OD_object *object, const char *data, size_t len); +SJ_OD_value_result *SJ_OD_object_find_field_unordered(SJ_OD_object *object, + const char *data, + size_t len); // ondemand::object_iterator DEFINE_GET(SJ_OD_object_iterator, SJ_OD_field, get) diff --git a/src/ondemand/array.rs b/src/ondemand/array.rs index 27c7b0e..10d50e1 100644 --- a/src/ondemand/array.rs +++ b/src/ondemand/array.rs @@ -5,6 +5,7 @@ use super::{array_iterator::ArrayIterator, document::Document, value::Value}; use crate::{ error::Result, macros::{impl_drop, map_result}, + utils::string_view_to_str, }; pub struct Array<'a> { @@ -47,6 +48,15 @@ impl<'a> Array<'a> { .map(Value::new) } + pub fn at_pointer(&mut self, key: &str) -> Result> { + map_result!( + ffi::SJ_OD_array_at_pointer(self.ptr.as_mut(), key.as_ptr().cast(), key.len()), + ffi::SJ_OD_value_result_error, + ffi::SJ_OD_value_result_value_unsafe + ) + .map(Value::new) + } + pub fn reset(&mut self) -> Result { map_result!( primitive, @@ -69,6 +79,15 @@ impl<'a> Array<'a> { )?; Ok(ArrayIterator::new(begin, end)) } + + pub fn raw_json(&mut self) -> Result<&'a str> { + let sv = map_result!( + ffi::SJ_OD_array_raw_json(self.ptr.as_mut()), + ffi::STD_string_view_result_error, + ffi::STD_string_view_result_value_unsafe + )?; + Ok(string_view_to_str(sv)) + } } impl_drop!(Array<'a>, ffi::SJ_OD_array_free); diff --git a/src/ondemand/document.rs b/src/ondemand/document.rs index 810a3ad..7fdc092 100644 --- a/src/ondemand/document.rs +++ b/src/ondemand/document.rs @@ -5,6 +5,7 @@ use simdjson_sys as ffi; use crate::{ error::Result, macros::{impl_drop, map_result}, + utils::string_view_to_str, }; use super::{array::Array, object::Object, parser::Parser, value::Value}; @@ -85,6 +86,24 @@ impl<'p, 's> Document<'p, 's> { ) .map(Object::new) } + + pub fn get_wobbly_string<'a>(&mut self) -> Result<&'a str> { + let sv = map_result!( + ffi::SJ_OD_document_get_wobbly_string(self.ptr.as_mut()), + ffi::STD_string_view_result_error, + ffi::STD_string_view_result_value_unsafe + )?; + Ok(string_view_to_str(sv)) + } + + pub fn get_string<'a>(&mut self) -> Result<&'a str> { + let sv = map_result!( + ffi::SJ_OD_document_get_string(self.ptr.as_mut()), + ffi::STD_string_view_result_error, + ffi::STD_string_view_result_value_unsafe + )?; + Ok(string_view_to_str(sv)) + } } impl_drop!(Document<'p, 's>, ffi::SJ_OD_document_free); diff --git a/src/ondemand/object.rs b/src/ondemand/object.rs index 878d14d..da51583 100644 --- a/src/ondemand/object.rs +++ b/src/ondemand/object.rs @@ -68,6 +68,19 @@ impl<'a> Object<'a> { .map(Value::new) } + pub fn find_field_unordered(&mut self, key: &str) -> Result> { + map_result!( + ffi::SJ_OD_object_find_field_unordered( + self.ptr.as_mut(), + key.as_ptr().cast(), + key.len() + ), + ffi::SJ_OD_value_result_error, + ffi::SJ_OD_value_result_value_unsafe + ) + .map(Value::new) + } + pub fn count_fields(&mut self) -> Result { map_result!( primitive, diff --git a/src/ondemand/value.rs b/src/ondemand/value.rs index 25233ad..2300afb 100644 --- a/src/ondemand/value.rs +++ b/src/ondemand/value.rs @@ -2,6 +2,7 @@ use simdjson_sys as ffi; use std::{marker::PhantomData, ptr::NonNull}; use crate::macros::{impl_drop, map_result}; +use crate::utils::string_view_to_str; use super::document::Document; use super::{array::Array, object::Object}; @@ -73,6 +74,15 @@ impl<'a> Value<'a> { ) .map(Object::new) } + + pub fn get_string(&mut self, allow_replacement: bool) -> Result<&'a str> { + let sv = map_result!( + ffi::SJ_OD_value_get_string(self.ptr.as_mut(), allow_replacement), + ffi::STD_string_view_result_error, + ffi::STD_string_view_result_value_unsafe + )?; + Ok(string_view_to_str(sv)) + } } impl_drop!(Value<'a>, ffi::SJ_OD_value_free); diff --git a/src/utils.rs b/src/utils.rs index 678f2b7..0147219 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,7 @@ use simdjson_sys as ffi; use std::ptr::NonNull; - +#[inline] pub fn string_view_to_str<'a>(sv: NonNull) -> &'a str { let s = unsafe { let s = std::slice::from_raw_parts( From 1347fc49ec01a80e29b0f016a2ca2da940f0b714 Mon Sep 17 00:00:00 2001 From: SunDoge <384813529@qq.com> Date: Thu, 24 Aug 2023 23:40:56 +0800 Subject: [PATCH 31/31] update readme --- README.md | 33 +++++++++++---------------------- examples/simple.rs | 12 ++++++++++++ 2 files changed, 23 insertions(+), 22 deletions(-) create mode 100644 examples/simple.rs diff --git a/README.md b/README.md index 7bbf39a..316a077 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![CI](https://github.com/SunDoge/simdjson-rust/actions/workflows/CI.yml/badge.svg)](https://github.com/SunDoge/simdjson-rust/actions/workflows/CI.yml) -This crate currently uses `simdjson 2.2.2`. You can have a try and give feedback. +This crate currently uses `simdjson 3.2.3`. You can have a try and give feedback. ## Usage @@ -16,27 +16,16 @@ simdjson-rust = {git = "https://github.com/SunDoge/simdjson-rust"} Then, get started. ```rust -use simdjson_rust::dom; - -fn main() -> Result<(), Box> { - let mut parser = dom::Parser::default(); - let tweets = parser.load("json-examples/twitter.json")?; - println!( - "{} results.", - tweets - .at_key("search_metadata")? - .at_key("count")? - .get_u64()? - ); +use simdjson_rust::{error::Result, ondemand::parser::Parser, padded_string::make_padded_string}; + +fn main() -> Result<()> { + let mut parser = Parser::default(); + let ps = make_padded_string("[0,1,2,3]"); + let mut doc = parser.iterate(&ps)?; + let mut array = doc.get_array()?; + for (index, value) in array.iter()?.enumerate() { + assert_eq!(index as u64, value?.get_uint64()?); + } Ok(()) } ``` - -## Roadmap - -- [x] ParsedJson -- [x] ParsedJsonIterator -- [x] printjson (impl Display) -- [x] ci -- [ ] tests -- [ ] benchmark \ No newline at end of file diff --git a/examples/simple.rs b/examples/simple.rs new file mode 100644 index 0000000..9e95b58 --- /dev/null +++ b/examples/simple.rs @@ -0,0 +1,12 @@ +use simdjson_rust::{error::Result, ondemand::parser::Parser, padded_string::make_padded_string}; + +fn main() -> Result<()> { + let mut parser = Parser::default(); + let ps = make_padded_string("[0,1,2,3]"); + let mut doc = parser.iterate(&ps)?; + let mut array = doc.get_array()?; + for (index, value) in array.iter()?.enumerate() { + assert_eq!(index as u64, value?.get_uint64()?); + } + Ok(()) +}