sajson 0.1.0.0 → 0.2.0.0
raw patch · 3 files changed
+1795/−44 lines, 3 filesdep ~base
Dependency ranges changed: base
Files
- cbits/sajson.hpp +1722/−0
- cbits/sajson_wrapper.h +28/−0
- sajson.cabal +45/−44
+ cbits/sajson.hpp view
@@ -0,0 +1,1722 @@+// -*- mode: c++; c-basic-offset: 4; flycheck-clang-language-standard: "c++14"; -*-++/*+ * Copyright (c) 2012-2017 Chad Austin+ *+ * Permission is hereby granted, free of charge, to any person+ * obtaining a copy of this software and associated documentation+ * files (the "Software"), to deal in the Software without+ * restriction, including without limitation the rights to use, copy,+ * modify, merge, publish, distribute, sublicense, and/or sell copies+ * of the Software, and to permit persons to whom the Software is+ * furnished to do so, subject to the following conditions:+ *+ * The above copyright notice and this permission notice shall be+ * included in all copies or substantial portions of the Software.+ *+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+ * SOFTWARE.+ */++#pragma once++#include <assert.h>+#include <stdarg.h>+#include <stddef.h>+#include <string.h>+#include <math.h>+#include <limits.h>+#include <algorithm>+#include <cstdio>+#include <limits>++#ifndef SAJSON_NO_STD_STRING+#include <string> // for convenient access to error messages and string values.+#endif++#if defined(__GNUC__) || defined(__clang__)+#define SAJSON_LIKELY(x) __builtin_expect(!!(x), 1)+#define SAJSON_UNLIKELY(x) __builtin_expect(!!(x), 0)+#define SAJSON_ALWAYS_INLINE __attribute__((always_inline))+#define SAJSON_UNREACHABLE() __builtin_unreachable()+#else+#define SAJSON_LIKELY(x) x+#define SAJSON_UNLIKELY(x) x+#define SAJSON_ALWAYS_INLINE __forceinline+#define SAJSON_UNREACHABLE() assert(!"unreachable")+#endif++namespace sajson {+ namespace internal {+ // This template utilizes the One Definition Rule to create global arrays in a header.+ // This trick courtesy of Rich Geldreich's Purple JSON parser.+ template<typename unused=void>+ struct globals_struct {+ static const unsigned char parse_flags[256];+ };+ typedef globals_struct<> globals;++ // bit 0 (1) - set if: plain ASCII string character+ // bit 1 (2) - set if: whitespace+ // bit 4 (0x10) - set if: 0-9 e E .+ template<typename unused>+ const uint8_t globals_struct<unused>::parse_flags[256] = {+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0, // 0+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1+ 3, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x11,1, // 2+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,1, 1, 1, 1, 1, 1, // 3+ 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 5+ 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7++ // 128-255+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,+ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0+ };++ inline bool is_plain_string_character(char c) {+ //return c >= 0x20 && c <= 0x7f && c != 0x22 && c != 0x5c;+ return (globals::parse_flags[static_cast<unsigned char>(c)] & 1) != 0;+ }++ inline bool is_whitespace(char c) {+ //return c == '\r' || c == '\n' || c == '\t' || c == ' ';+ return (globals::parse_flags[static_cast<unsigned char>(c)] & 2) != 0;+ }+ }++ enum type: uint8_t {+ TYPE_INTEGER = 0,+ TYPE_DOUBLE = 1,+ TYPE_NULL = 2,+ TYPE_FALSE = 3,+ TYPE_TRUE = 4,+ TYPE_STRING = 5,+ TYPE_ARRAY = 6,+ TYPE_OBJECT = 7,+ };++ static const size_t TYPE_BITS = 3;+ static const size_t TYPE_MASK = (1 << TYPE_BITS) - 1;+ static const size_t VALUE_MASK = size_t(-1) >> TYPE_BITS;++ static const size_t ROOT_MARKER = VALUE_MASK;++ inline type get_element_type(size_t s) {+ return static_cast<type>(s & TYPE_MASK);+ }++ inline size_t get_element_value(size_t s) {+ return s >> TYPE_BITS;+ }++ inline size_t make_element(type t, size_t value) {+ //assert((value & ~VALUE_MASK) == 0);+ //value &= VALUE_MASK;+ return static_cast<size_t>(t) | (value << TYPE_BITS);+ }++ class string {+ public:+ string(const char* text, size_t length)+ : text(text)+ , _length(length)+ {}++ const char* data() const {+ return text;+ }++ size_t length() const {+ return _length;+ }++#ifndef SAJSON_NO_STD_STRING+ std::string as_string() const {+ return std::string(text, text + _length);+ }+#endif++ private:+ const char* const text;+ const size_t _length;++ string(); /*=delete*/+ };++ class literal : public string {+ public:+ explicit literal(const char* text)+ : string(text, strlen(text))+ {}+ };++ struct object_key_record {+ size_t key_start;+ size_t key_end;+ size_t value;+ };++ struct object_key_comparator {+ object_key_comparator(const char* object_data)+ : data(object_data)+ {}++ bool operator()(const object_key_record& lhs, const string& rhs) const {+ const size_t lhs_length = lhs.key_end - lhs.key_start;+ const size_t rhs_length = rhs.length();+ if (lhs_length < rhs_length) {+ return true;+ } else if (lhs_length > rhs_length) {+ return false;+ }+ return memcmp(data + lhs.key_start, rhs.data(), lhs_length) < 0;+ }++ bool operator()(const string& lhs, const object_key_record& rhs) const {+ return !(*this)(rhs, lhs);+ }++ bool operator()(const object_key_record& lhs, const+ object_key_record& rhs)+ {+ const size_t lhs_length = lhs.key_end - lhs.key_start;+ const size_t rhs_length = rhs.key_end - rhs.key_start;+ if (lhs_length < rhs_length) {+ return true;+ } else if (lhs_length > rhs_length) {+ return false;+ }+ return memcmp(data + lhs.key_start, data + rhs.key_start,+ lhs_length) < 0;+ }++ const char* data;+ };++ class refcount {+ public:+ refcount()+ : pn(new size_t(1))+ {}++ refcount(const refcount& rc)+ : pn(rc.pn)+ {+ ++*pn;+ }++ ~refcount() {+ if (--*pn == 0) {+ delete pn;+ }+ }++ size_t count() const {+ return *pn;+ }++ private:+ size_t* pn;++ refcount& operator=(const refcount&) = delete;+ };++ class mutable_string_view {+ public:+ mutable_string_view()+ : length_(0)+ , data(0)+ {}++ mutable_string_view(size_t length, char* data)+ : length_(length)+ , data(data)+ {}++ size_t length() const {+ return length_;+ }++ char* get_data() const {+ return data;+ }++ private:+ size_t length_;+ char* data;+ };++ union integer_storage {+ enum {+ word_length = 1+ };++ static void store(size_t* location, int value) {+ integer_storage is;+ is.i = value;+ *location = is.u;+ }++ int i;+ size_t u;+ };+ static_assert(sizeof(integer_storage) == sizeof(size_t), "integer_storage must have same size as one structure slot");++ union double_storage {+ enum {+ word_length = sizeof(double) / sizeof(size_t)+ };++#if defined(_M_IX86) || defined(__i386__) || defined(_X86_)+ static double load(const size_t* location) {+ return *reinterpret_cast<const double*>(location);+ }+ static void store(size_t* location, double value) {+ *reinterpret_cast<double*>(location) = value;+ }+#else+ static double load(const size_t* location) {+ double_storage s;+ for (unsigned i = 0; i < double_storage::word_length; ++i) {+ s.u[i] = location[i];+ }+ return s.d;+ }++ static void store(size_t* location, double value) {+ double_storage ns;+ ns.d = value;++ for (int i = 0; i < ns.word_length; ++i) {+ location[i] = ns.u[i];+ }+ }++ double d;+ size_t u[word_length];+#endif+ };+ // TODO: reinstate with c++03 implementation+ //static_assert(sizeof(double_storage) == sizeof(double), "double_storage should have same size as double");++ class value {+ public:+ explicit value(type value_type, const size_t* payload, const char* text)+ : value_type(value_type)+ , payload(payload)+ , text(text)+ {}++ type get_type() const {+ return value_type;+ }++ // valid iff get_type() is TYPE_ARRAY or TYPE_OBJECT+ size_t get_length() const {+ assert_type_2(TYPE_ARRAY, TYPE_OBJECT);+ return payload[0];+ }++ // valid iff get_type() is TYPE_ARRAY+ value get_array_element(size_t index) const {+ assert_type(TYPE_ARRAY);+ size_t element = payload[1 + index];+ return value(get_element_type(element), payload + get_element_value(element), text);+ }++ // valid iff get_type() is TYPE_OBJECT+ string get_object_key(size_t index) const {+ assert_type(TYPE_OBJECT);+ const size_t* s = payload + 1 + index * 3;+ return string(text + s[0], s[1] - s[0]);+ }++ // valid iff get_type() is TYPE_OBJECT+ value get_object_value(size_t index) const {+ assert_type(TYPE_OBJECT);+ size_t element = payload[3 + index * 3];+ return value(get_element_type(element), payload + get_element_value(element), text);+ }++ // valid iff get_type() is TYPE_OBJECT+ value get_value_of_key(const string& key) const {+ assert_type(TYPE_OBJECT);+ size_t i = find_object_key(key);+ assert_in_bounds(i);+ return get_object_value(i);+ }++ // valid iff get_type() is TYPE_OBJECT+ // return get_length() if there is no such key+ size_t find_object_key(const string& key) const {+ assert_type(TYPE_OBJECT);+ const object_key_record* start = reinterpret_cast<const object_key_record*>(payload + 1);+ const object_key_record* end = start + get_length();+ const object_key_record* i = std::lower_bound(start, end, key, object_key_comparator(text));+ return (i != end+ && (i->key_end - i->key_start) == key.length()+ && memcmp(key.data(), text + i->key_start, key.length()) == 0)? i - start : get_length();+ }++ // valid iff get_type() is TYPE_INTEGER+ int get_integer_value() const {+ assert_type(TYPE_INTEGER);+ integer_storage s;+ s.u = payload[0];+ return s.i;+ }++ // valid iff get_type() is TYPE_DOUBLE+ double get_double_value() const {+ assert_type(TYPE_DOUBLE);+ return double_storage::load(payload);+ }++ // valid iff get_type() is TYPE_INTEGER or TYPE_DOUBLE+ double get_number_value() const {+ assert_type_2(TYPE_INTEGER, TYPE_DOUBLE);+ if (get_type() == TYPE_INTEGER) {+ return get_integer_value();+ } else {+ return get_double_value();+ }+ }++ // valid iff get_type() is TYPE_INTEGER or TYPE_DOUBLE+ // returns true if out is modified written.+ // returns false if the value is a non-integral double+ // or out of range of a 53-bit integer.+ bool get_int53_value(int64_t* out) const {+ // Make sure the output variable is always defined to avoid any+ // possible situation like+ // https://gist.github.com/chadaustin/2c249cb850619ddec05b23ca42cf7a18+ *out = 0;++ assert_type_2(TYPE_INTEGER, TYPE_DOUBLE);+ if (get_type() == TYPE_INTEGER) {+ *out = get_integer_value();+ return true;+ } else if (get_type() == TYPE_DOUBLE) {+ double v = get_double_value();+ if (v < -(1LL << 53) || v > (1LL << 53)) {+ return false;+ }+ int64_t as_int = static_cast<int64_t>(v);+ if (as_int != v) {+ return false;+ }+ *out = as_int;+ return true;+ } else {+ return false;+ }+ }++ // valid iff get_type() is TYPE_STRING+ size_t get_string_length() const {+ assert_type(TYPE_STRING);+ return payload[1] - payload[0];+ }++ // valid iff get_type() is TYPE_STRING+ // WARNING: calling this function and using the return value as a+ // C-style string (that is, without also using get_string_length())+ // will cause the string to appear truncated if the string has+ // embedded NULs.+ const char* as_cstring() const {+ assert_type(TYPE_STRING);+ return text + payload[0];+ }++#ifndef SAJSON_NO_STD_STRING+ // valid iff get_type() is TYPE_STRING+ std::string as_string() const {+ assert_type(TYPE_STRING);+ return std::string(text + payload[0], text + payload[1]);+ }+#endif++ const size_t* _internal_get_payload() const {+ return payload;+ }+++ private:+ void assert_type(type expected) const {+ assert(expected == get_type());+ }++ void assert_type_2(type e1, type e2) const {+ assert(e1 == get_type() || e2 == get_type());+ }++ void assert_in_bounds(size_t i) const {+ assert(i < get_length());+ }++ const type value_type;+ const size_t* const payload;+ const char* const text;+ };++ enum error {+ ERROR_SUCCESS,+ ERROR_OUT_OF_MEMORY,+ ERROR_UNEXPECTED_END,+ ERROR_MISSING_ROOT_ELEMENT,+ ERROR_BAD_ROOT,+ ERROR_EXPECTED_COMMA,+ ERROR_MISSING_OBJECT_KEY,+ ERROR_EXPECTED_COLON,+ ERROR_EXPECTED_END_OF_INPUT,+ ERROR_UNEXPECTED_COMMA,+ ERROR_EXPECTED_VALUE,+ ERROR_EXPECTED_NULL,+ ERROR_EXPECTED_FALSE,+ ERROR_EXPECTED_TRUE,+ ERROR_MSSING_EXPONENT,+ ERROR_ILLEGAL_CODEPOINT,+ ERROR_INVALID_UNICODE_ESCAPE,+ ERROR_UNEXPECTED_END_OF_UTF16,+ ERROR_EXPECTED_U,+ ERROR_INVALID_UTF16_TRAIL_SURROGATE,+ ERROR_UNKNOWN_ESCAPE,+ ERROR_INVALID_UTF8,+ };++ class document {+ public:+ explicit document(const mutable_string_view& input, type root_type, const size_t* root)+ : input(input)+ , root_type(root_type)+ , root(root)+ , error_line(0)+ , error_column(0)+ , error_code(ERROR_SUCCESS)+ , error_arg(0)+ {+ formatted_error_message[0] = 0;+ }++ explicit document(const mutable_string_view& input, size_t error_line, size_t error_column, const error error_code, int error_arg)+ : input(input)+ , root_type(TYPE_NULL)+ , root(0)+ , error_line(error_line)+ , error_column(error_column)+ , error_code(error_code)+ , error_arg(error_arg)+ {+ formatted_error_message[ERROR_BUFFER_LENGTH - 1] = 0;+ int written = has_significant_error_arg()+ ? snprintf(formatted_error_message, ERROR_BUFFER_LENGTH - 1, "%s: %d", _internal_get_error_text(), error_arg)+ : snprintf(formatted_error_message, ERROR_BUFFER_LENGTH - 1, "%s", _internal_get_error_text());+ (void)written;+ assert(written >= 0 && written < ERROR_BUFFER_LENGTH);+ }++ document(const document&) = delete;+ void operator=(const document&) = delete;++ document(document&& rhs)+ : input(rhs.input)+ , root_type(rhs.root_type)+ , root(rhs.root)+ , error_line(rhs.error_line)+ , error_column(rhs.error_column)+ , error_code(rhs.error_code)+ , error_arg(rhs.error_arg)+ {+ // Yikes... but strcpy is okay here because formatted_error is+ // guaranteed to be null-terminated.+ strcpy(formatted_error_message, rhs.formatted_error_message);+ // should rhs's fields be zeroed too?+ }++ bool is_valid() const {+ return root_type == TYPE_ARRAY || root_type == TYPE_OBJECT;+ }++ value get_root() const {+ return value(root_type, root, input.get_data());+ }++ size_t get_error_line() const {+ return error_line;+ }++ size_t get_error_column() const {+ return error_column;+ }++#ifndef SAJSON_NO_STD_STRING+ std::string get_error_message_as_string() const {+ return formatted_error_message;+ }+#endif++ const char* get_error_message_as_cstring() const {+ return formatted_error_message;+ }++ /// WARNING: Internal function which is subject to change+ error _internal_get_error_code() const {+ return error_code;+ }++ /// WARNING: Internal function which is subject to change+ int _internal_get_error_argument() const {+ return error_arg;+ }++ /// WARNING: Internal function which is subject to change+ const char* _internal_get_error_text() const {+ switch (error_code) {+ case ERROR_SUCCESS: return "no error";+ case ERROR_OUT_OF_MEMORY: return "out of memory";+ case ERROR_UNEXPECTED_END: return "unexpected end of input";+ case ERROR_MISSING_ROOT_ELEMENT: return "missing root element";+ case ERROR_BAD_ROOT: return "document root must be object or array";+ case ERROR_EXPECTED_COMMA: return "expected ,";+ case ERROR_MISSING_OBJECT_KEY: return "missing object key";+ case ERROR_EXPECTED_COLON: return "expected :";+ case ERROR_EXPECTED_END_OF_INPUT: return "expected end of input";+ case ERROR_UNEXPECTED_COMMA: return "unexpected comma";+ case ERROR_EXPECTED_VALUE: return "expected value";+ case ERROR_EXPECTED_NULL: return "expected 'null'";+ case ERROR_EXPECTED_FALSE: return "expected 'false'";+ case ERROR_EXPECTED_TRUE: return "expected 'true'";+ case ERROR_MSSING_EXPONENT: return "missing exponent";+ case ERROR_ILLEGAL_CODEPOINT: return "illegal unprintable codepoint in string";+ case ERROR_INVALID_UNICODE_ESCAPE: return "invalid character in unicode escape";+ case ERROR_UNEXPECTED_END_OF_UTF16: return "unexpected end of input during UTF-16 surrogate pair";+ case ERROR_EXPECTED_U: return "expected \\u";+ case ERROR_INVALID_UTF16_TRAIL_SURROGATE: return "invalid UTF-16 trail surrogate";+ case ERROR_UNKNOWN_ESCAPE: return "unknown escape";+ case ERROR_INVALID_UTF8: return "invalid UTF-8";+ }++ SAJSON_UNREACHABLE();+ }++ /// WARNING: Internal function exposed only for high-performance language bindings.+ type _internal_get_root_type() const {+ return root_type;+ }++ /// WARNING: Internal function exposed only for high-performance language bindings.+ const size_t* _internal_get_root() const {+ return root;+ }++ const mutable_string_view& _internal_get_input() const {+ return input;+ }++ private:+ bool has_significant_error_arg() const {+ return error_code == ERROR_ILLEGAL_CODEPOINT;+ }++ mutable_string_view input;+ const type root_type;+ const size_t* const root;+ const size_t error_line;+ const size_t error_column;+ const error error_code;+ const int error_arg;++ enum { ERROR_BUFFER_LENGTH = 128 };+ char formatted_error_message[ERROR_BUFFER_LENGTH];+ };++ class single_allocation {+ public:+ class stack_head {+ public:+ stack_head(stack_head&& other)+ : stack_bottom(other.stack_bottom)+ , stack_top(other.stack_top)+ {}++ bool has_allocation_error() {+ return false;+ }++ // check has_allocation_error() immediately after calling+ void push(size_t element) {+ *stack_top++ = element;+ }++ // check has_allocation_error() immediately after calling+ size_t* reserve(size_t amount) {+ size_t* rv = stack_top;+ stack_top += amount;+ return rv;+ }++ // The compiler does not see the stack_head (stored in a local)+ // and the allocator (stored as a field) have the same stack_bottom+ // values, so it does a bit of redundant work.+ // So there's a microoptimization available here: introduce a type+ // "stack_mark" and make it polymorphic on the allocator. For+ // single_allocation, it merely needs to be a single pointer.++ void reset(size_t new_top) {+ stack_top = stack_bottom + new_top;+ }++ size_t get_size() {+ return stack_top - stack_bottom;+ }++ size_t* get_top() {+ return stack_top;+ }++ size_t* get_pointer_from_offset(size_t offset) {+ return stack_bottom + offset;+ }++ private:+ stack_head() = delete;+ stack_head(const stack_head&) = delete;+ void operator=(const stack_head&) = delete;++ explicit stack_head(size_t* base)+ : stack_bottom(base)+ , stack_top(base)+ {}++ size_t* const stack_bottom;+ size_t* stack_top;++ friend class single_allocation;+ };++ class allocator {+ public:+ allocator() = delete;+ allocator(const allocator&) = delete;+ void operator=(const allocator&) = delete;++ explicit allocator(size_t* buffer, size_t input_size)+ : structure(buffer)+ , structure_end(buffer ? buffer + input_size : 0)+ , write_cursor(structure_end)+ {}++ explicit allocator(std::nullptr_t)+ : structure(0)+ , structure_end(0)+ , write_cursor(0)+ {}++ allocator(allocator&& other)+ : structure(other.structure)+ , structure_end(other.structure_end)+ , write_cursor(other.write_cursor)+ {+ other.structure = 0;+ other.structure_end = 0;+ other.write_cursor = 0;+ }++ ~allocator() = default;++ stack_head get_stack_head() {+ return stack_head(structure);+ }++ size_t get_write_offset() {+ return structure_end - write_cursor;+ }++ size_t* get_write_pointer_of(size_t v) {+ return structure_end - v;+ }++ bool has_allocation_error() {+ return false;+ }++ // check has_allocation_error immediately after calling+ size_t* reserve(size_t size) {+ write_cursor -= size;+ return write_cursor;+ }++ size_t* get_ast_root() {+ return write_cursor;+ }++ void transfer_ownership() {+ structure = 0;+ structure_end = 0;+ write_cursor = 0;+ }++ private:+ size_t* structure;+ size_t* structure_end;+ size_t* write_cursor;+ };++ /// Allocate a single worst-case AST buffer with one word per byte in+ /// the input document.+ single_allocation() = delete;+++ /// Write the AST into an existing buffer. Will fail with an out of+ /// memory error if the buffer is not guaranteed to be big enough for+ /// the document.+ single_allocation(size_t* existing_buffer, size_t size_in_words)+ : existing_buffer(existing_buffer)+ , existing_buffer_size(size_in_words)+ {}++ allocator make_allocator(size_t input_document_size_in_bytes, bool* succeeded) const {+ {+ if (existing_buffer_size < input_document_size_in_bytes) {+ *succeeded = false;+ return allocator(nullptr);+ }+ *succeeded = true;+ return allocator(existing_buffer, input_document_size_in_bytes);+ }+ }++ private:+ size_t* existing_buffer;+ size_t existing_buffer_size;+ };++ template<typename Allocator>+ class parser {+ public:+ parser(const mutable_string_view& msv, Allocator&& allocator)+ : input(msv)+ , input_end(input.get_data() + input.length())+ , allocator(std::move(allocator))+ , root_type(TYPE_NULL)+ , error_line(0)+ , error_column(0)+ {}++ document get_document() {+ if (parse()) {+ size_t* ast_root = allocator.get_ast_root();+ allocator.transfer_ownership();+ return document(input, root_type, ast_root);+ } else {+ return document(input, error_line, error_column, error_code, error_arg);+ }+ }++ private:+ struct error_result {+ operator bool() const {+ return false;+ }+ operator char*() const {+ return 0;+ }+ };++ bool at_eof(const char* p) {+ return p == input_end;+ }++ char* skip_whitespace(char* p) {+ // There is an opportunity to make better use of superscalar+ // hardware here* but if someone cares about JSON parsing+ // performance the first thing they do is minify, so prefer+ // to optimize for code size here.+ // * https://github.com/chadaustin/Web-Benchmarks/blob/master/json/third-party/pjson/pjson.h#L1873+ for (;;) {+ if (SAJSON_UNLIKELY(p == input_end)) {+ return 0;+ } else if (internal::is_whitespace(*p)) {+ ++p;+ } else {+ return p;+ }+ }+ }++ error_result oom(char* p) {+ return make_error(p, ERROR_OUT_OF_MEMORY);+ }++ error_result unexpected_end() {+ return make_error(0, ERROR_UNEXPECTED_END);+ }++ error_result unexpected_end(char* p) {+ return make_error(p, ERROR_UNEXPECTED_END);+ }++ error_result make_error(char* p, error code, int arg = 0) {+ if (!p) {+ p = input_end;+ }++ error_line = 1;+ error_column = 1;++ char* c = input.get_data();+ while (c < p) {+ if (*c == '\r') {+ if (c + 1 < p && c[1] == '\n') {+ ++error_line;+ error_column = 1;+ ++c;+ } else {+ ++error_line;+ error_column = 1;+ }+ } else if (*c == '\n') {+ ++error_line;+ error_column = 1;+ } else {+ // TODO: count UTF-8 characters+ ++error_column;+ }+ ++c;+ }++ error_code = code;+ error_arg = arg;+ return error_result();+ }++ bool parse() {+ // p points to the character currently being parsed+ char* p = input.get_data();++ auto stack = allocator.get_stack_head();+ if (SAJSON_UNLIKELY(stack.has_allocation_error())) {+ return oom(p);+ }++ p = skip_whitespace(p);+ if (SAJSON_UNLIKELY(!p)) {+ return make_error(p, ERROR_MISSING_ROOT_ELEMENT);+ }++ // current_base is an offset to the first element of the current structure (object or array)+ size_t current_base = stack.get_size();+ type current_structure_type;+ if (*p == '[') {+ current_structure_type = TYPE_ARRAY;+ stack.push(make_element(current_structure_type, ROOT_MARKER));+ if (SAJSON_UNLIKELY(stack.has_allocation_error())) {+ return oom(p);+ }+ goto array_close_or_element;+ } else if (*p == '{') {+ current_structure_type = TYPE_OBJECT;+ stack.push(make_element(current_structure_type, ROOT_MARKER));+ if (SAJSON_UNLIKELY(stack.has_allocation_error())) {+ return oom(p);+ }+ goto object_close_or_element;+ } else {+ return make_error(p, ERROR_BAD_ROOT);+ }++ // BEGIN STATE MACHINE++ size_t pop_element; // used as an argument into the `pop` routine++ if (0) { // purely for structure++ // ASSUMES: byte at p SHOULD be skipped+ array_close_or_element:+ p = skip_whitespace(p + 1);+ if (SAJSON_UNLIKELY(!p)) {+ return unexpected_end();+ }+ if (*p == ']') {+ goto pop_array;+ } else {+ goto next_element;+ }+ SAJSON_UNREACHABLE();++ // ASSUMES: byte at p SHOULD be skipped+ object_close_or_element:+ p = skip_whitespace(p + 1);+ if (SAJSON_UNLIKELY(!p)) {+ return unexpected_end();+ }+ if (*p == '}') {+ goto pop_object;+ } else {+ goto object_key;+ }+ SAJSON_UNREACHABLE();++ // ASSUMES: byte at p SHOULD NOT be skipped+ structure_close_or_comma:+ p = skip_whitespace(p);+ if (SAJSON_UNLIKELY(!p)) {+ return unexpected_end();+ }++ if (current_structure_type == TYPE_ARRAY) {+ if (*p == ']') {+ goto pop_array;+ } else {+ if (SAJSON_UNLIKELY(*p != ',')) {+ return make_error(p, ERROR_EXPECTED_COMMA);+ }+ ++p;+ goto next_element;+ }+ } else {+ assert(current_structure_type == TYPE_OBJECT);+ if (*p == '}') {+ goto pop_object;+ } else {+ if (SAJSON_UNLIKELY(*p != ',')) {+ return make_error(p, ERROR_EXPECTED_COMMA);+ }+ ++p;+ goto object_key;+ }+ }+ SAJSON_UNREACHABLE();++ // ASSUMES: *p == '}'+ pop_object: {+ ++p;+ size_t* base_ptr = stack.get_pointer_from_offset(current_base);+ pop_element = *base_ptr;+ if (SAJSON_UNLIKELY(!install_object(base_ptr + 1, stack.get_top()))) {+ return oom(p);+ }+ goto pop;+ }++ // ASSUMES: *p == ']'+ pop_array: {+ ++p;+ size_t* base_ptr = stack.get_pointer_from_offset(current_base);+ pop_element = *base_ptr;+ if (SAJSON_UNLIKELY(!install_array(base_ptr + 1, stack.get_top()))) {+ return oom(p);+ }+ goto pop;+ }++ // ASSUMES: byte at p SHOULD NOT be skipped+ object_key: {+ p = skip_whitespace(p);+ if (SAJSON_UNLIKELY(!p)) {+ return unexpected_end();+ }+ if (SAJSON_UNLIKELY(*p != '"')) {+ return make_error(p, ERROR_MISSING_OBJECT_KEY);+ }+ size_t* out = stack.reserve(2);+ if (SAJSON_UNLIKELY(stack.has_allocation_error())) {+ return oom(p);+ }+ p = parse_string(p, out);+ if (SAJSON_UNLIKELY(!p)) {+ return false;+ }+ p = skip_whitespace(p);+ if (SAJSON_UNLIKELY(!p || *p != ':')) {+ return make_error(p, ERROR_EXPECTED_COLON);+ }+ ++p;+ goto next_element;+ }++ // ASSUMES: byte at p SHOULD NOT be skipped+ next_element:+ p = skip_whitespace(p);+ if (SAJSON_UNLIKELY(!p)) {+ return unexpected_end();+ }++ type value_type_result;+ switch (*p) {+ case 0:+ return unexpected_end(p);+ case 'n':+ p = parse_null(p);+ if (!p) {+ return false;+ }+ value_type_result = TYPE_NULL;+ break;+ case 'f':+ p = parse_false(p);+ if (!p) {+ return false;+ }+ value_type_result = TYPE_FALSE;+ break;+ case 't':+ p = parse_true(p);+ if (!p) {+ return false;+ }+ value_type_result = TYPE_TRUE;+ break;+ case '0':+ case '1':+ case '2':+ case '3':+ case '4':+ case '5':+ case '6':+ case '7':+ case '8':+ case '9':+ case '-': {+ auto result = parse_number(p);+ p = result.first;+ if (!p) {+ return false;+ }+ value_type_result = result.second;+ break;+ }+ case '"': {+ size_t* string_tag = allocator.reserve(2);+ if (allocator.has_allocation_error()) {+ return oom(p);+ }+ p = parse_string(p, string_tag);+ if (!p) {+ return false;+ }+ value_type_result = TYPE_STRING;+ break;+ }++ case '[': {+ size_t previous_base = current_base;+ current_base = stack.get_size();+ stack.push(make_element(current_structure_type, previous_base));+ if (stack.has_allocation_error()) {+ return oom(p);+ }+ current_structure_type = TYPE_ARRAY;+ goto array_close_or_element;+ }+ case '{': {+ size_t previous_base = current_base;+ current_base = stack.get_size();+ stack.push(make_element(current_structure_type, previous_base));+ if (stack.has_allocation_error()) {+ return oom(p);+ }+ current_structure_type = TYPE_OBJECT;+ goto object_close_or_element;+ }+ pop: {+ size_t parent = get_element_value(pop_element);+ if (parent == ROOT_MARKER) {+ root_type = current_structure_type;+ p = skip_whitespace(p);+ if (SAJSON_UNLIKELY(p)) {+ return make_error(p, ERROR_EXPECTED_END_OF_INPUT);+ }+ return true;+ }+ stack.reset(current_base);+ current_base = parent;+ value_type_result = current_structure_type;+ current_structure_type = get_element_type(pop_element);+ break;+ }++ case ',':+ return make_error(p, ERROR_UNEXPECTED_COMMA);+ default:+ return make_error(p, ERROR_EXPECTED_VALUE);+ }++ stack.push(make_element(value_type_result, allocator.get_write_offset()));+ if (SAJSON_UNLIKELY(stack.has_allocation_error())) {+ return oom(p);+ }++ goto structure_close_or_comma;+ }++ SAJSON_UNREACHABLE();+ }++ bool has_remaining_characters(char* p, ptrdiff_t remaining) {+ return input_end - p >= remaining;+ }++ char* parse_null(char* p) {+ if (SAJSON_UNLIKELY(!has_remaining_characters(p, 4))) {+ make_error(p, ERROR_UNEXPECTED_END);+ return 0;+ }+ char p1 = p[1];+ char p2 = p[2];+ char p3 = p[3];+ if (SAJSON_UNLIKELY(p1 != 'u' || p2 != 'l' || p3 != 'l')) {+ make_error(p, ERROR_EXPECTED_NULL);+ return 0;+ }+ return p + 4;+ }++ char* parse_false(char* p) {+ if (SAJSON_UNLIKELY(!has_remaining_characters(p, 5))) {+ return make_error(p, ERROR_UNEXPECTED_END);+ }+ char p1 = p[1];+ char p2 = p[2];+ char p3 = p[3];+ char p4 = p[4];+ if (SAJSON_UNLIKELY(p1 != 'a' || p2 != 'l' || p3 != 's' || p4 != 'e')) {+ return make_error(p, ERROR_EXPECTED_FALSE);+ }+ return p + 5;+ }++ char* parse_true(char* p) {+ if (SAJSON_UNLIKELY(!has_remaining_characters(p, 4))) {+ return make_error(p, ERROR_UNEXPECTED_END);+ }+ char p1 = p[1];+ char p2 = p[2];+ char p3 = p[3];+ if (SAJSON_UNLIKELY(p1 != 'r' || p2 != 'u' || p3 != 'e')) {+ return make_error(p, ERROR_EXPECTED_TRUE);+ }+ return p + 4;+ }++ static double pow10(int exponent) {+ if (exponent > 308) {+ return std::numeric_limits<double>::infinity();+ } else if (exponent < -323) {+ return 0.0;+ }+ static const double constants[] = {+ 1e-323,1e-322,1e-321,1e-320,1e-319,1e-318,1e-317,1e-316,1e-315,1e-314,+ 1e-313,1e-312,1e-311,1e-310,1e-309,1e-308,1e-307,1e-306,1e-305,1e-304,+ 1e-303,1e-302,1e-301,1e-300,1e-299,1e-298,1e-297,1e-296,1e-295,1e-294,+ 1e-293,1e-292,1e-291,1e-290,1e-289,1e-288,1e-287,1e-286,1e-285,1e-284,+ 1e-283,1e-282,1e-281,1e-280,1e-279,1e-278,1e-277,1e-276,1e-275,1e-274,+ 1e-273,1e-272,1e-271,1e-270,1e-269,1e-268,1e-267,1e-266,1e-265,1e-264,+ 1e-263,1e-262,1e-261,1e-260,1e-259,1e-258,1e-257,1e-256,1e-255,1e-254,+ 1e-253,1e-252,1e-251,1e-250,1e-249,1e-248,1e-247,1e-246,1e-245,1e-244,+ 1e-243,1e-242,1e-241,1e-240,1e-239,1e-238,1e-237,1e-236,1e-235,1e-234,+ 1e-233,1e-232,1e-231,1e-230,1e-229,1e-228,1e-227,1e-226,1e-225,1e-224,+ 1e-223,1e-222,1e-221,1e-220,1e-219,1e-218,1e-217,1e-216,1e-215,1e-214,+ 1e-213,1e-212,1e-211,1e-210,1e-209,1e-208,1e-207,1e-206,1e-205,1e-204,+ 1e-203,1e-202,1e-201,1e-200,1e-199,1e-198,1e-197,1e-196,1e-195,1e-194,+ 1e-193,1e-192,1e-191,1e-190,1e-189,1e-188,1e-187,1e-186,1e-185,1e-184,+ 1e-183,1e-182,1e-181,1e-180,1e-179,1e-178,1e-177,1e-176,1e-175,1e-174,+ 1e-173,1e-172,1e-171,1e-170,1e-169,1e-168,1e-167,1e-166,1e-165,1e-164,+ 1e-163,1e-162,1e-161,1e-160,1e-159,1e-158,1e-157,1e-156,1e-155,1e-154,+ 1e-153,1e-152,1e-151,1e-150,1e-149,1e-148,1e-147,1e-146,1e-145,1e-144,+ 1e-143,1e-142,1e-141,1e-140,1e-139,1e-138,1e-137,1e-136,1e-135,1e-134,+ 1e-133,1e-132,1e-131,1e-130,1e-129,1e-128,1e-127,1e-126,1e-125,1e-124,+ 1e-123,1e-122,1e-121,1e-120,1e-119,1e-118,1e-117,1e-116,1e-115,1e-114,+ 1e-113,1e-112,1e-111,1e-110,1e-109,1e-108,1e-107,1e-106,1e-105,1e-104,+ 1e-103,1e-102,1e-101,1e-100,1e-99,1e-98,1e-97,1e-96,1e-95,1e-94,1e-93,+ 1e-92,1e-91,1e-90,1e-89,1e-88,1e-87,1e-86,1e-85,1e-84,1e-83,1e-82,1e-81,+ 1e-80,1e-79,1e-78,1e-77,1e-76,1e-75,1e-74,1e-73,1e-72,1e-71,1e-70,1e-69,+ 1e-68,1e-67,1e-66,1e-65,1e-64,1e-63,1e-62,1e-61,1e-60,1e-59,1e-58,1e-57,+ 1e-56,1e-55,1e-54,1e-53,1e-52,1e-51,1e-50,1e-49,1e-48,1e-47,1e-46,1e-45,+ 1e-44,1e-43,1e-42,1e-41,1e-40,1e-39,1e-38,1e-37,1e-36,1e-35,1e-34,1e-33,+ 1e-32,1e-31,1e-30,1e-29,1e-28,1e-27,1e-26,1e-25,1e-24,1e-23,1e-22,1e-21,+ 1e-20,1e-19,1e-18,1e-17,1e-16,1e-15,1e-14,1e-13,1e-12,1e-11,1e-10,1e-9,+ 1e-8,1e-7,1e-6,1e-5,1e-4,1e-3,1e-2,1e-1,1e0,1e1,1e2,1e3,1e4,1e5,1e6,1e7,+ 1e8,1e9,1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18,1e19,1e20,1e21,+ 1e22,1e23,1e24,1e25,1e26,1e27,1e28,1e29,1e30,1e31,1e32,1e33,1e34,1e35,+ 1e36,1e37,1e38,1e39,1e40,1e41,1e42,1e43,1e44,1e45,1e46,1e47,1e48,1e49,+ 1e50,1e51,1e52,1e53,1e54,1e55,1e56,1e57,1e58,1e59,1e60,1e61,1e62,1e63,+ 1e64,1e65,1e66,1e67,1e68,1e69,1e70,1e71,1e72,1e73,1e74,1e75,1e76,1e77,+ 1e78,1e79,1e80,1e81,1e82,1e83,1e84,1e85,1e86,1e87,1e88,1e89,1e90,1e91,+ 1e92,1e93,1e94,1e95,1e96,1e97,1e98,1e99,1e100,1e101,1e102,1e103,1e104,+ 1e105,1e106,1e107,1e108,1e109,1e110,1e111,1e112,1e113,1e114,1e115,1e116,+ 1e117,1e118,1e119,1e120,1e121,1e122,1e123,1e124,1e125,1e126,1e127,1e128,+ 1e129,1e130,1e131,1e132,1e133,1e134,1e135,1e136,1e137,1e138,1e139,1e140,+ 1e141,1e142,1e143,1e144,1e145,1e146,1e147,1e148,1e149,1e150,1e151,1e152,+ 1e153,1e154,1e155,1e156,1e157,1e158,1e159,1e160,1e161,1e162,1e163,1e164,+ 1e165,1e166,1e167,1e168,1e169,1e170,1e171,1e172,1e173,1e174,1e175,1e176,+ 1e177,1e178,1e179,1e180,1e181,1e182,1e183,1e184,1e185,1e186,1e187,1e188,+ 1e189,1e190,1e191,1e192,1e193,1e194,1e195,1e196,1e197,1e198,1e199,1e200,+ 1e201,1e202,1e203,1e204,1e205,1e206,1e207,1e208,1e209,1e210,1e211,1e212,+ 1e213,1e214,1e215,1e216,1e217,1e218,1e219,1e220,1e221,1e222,1e223,1e224,+ 1e225,1e226,1e227,1e228,1e229,1e230,1e231,1e232,1e233,1e234,1e235,1e236,+ 1e237,1e238,1e239,1e240,1e241,1e242,1e243,1e244,1e245,1e246,1e247,1e248,+ 1e249,1e250,1e251,1e252,1e253,1e254,1e255,1e256,1e257,1e258,1e259,1e260,+ 1e261,1e262,1e263,1e264,1e265,1e266,1e267,1e268,1e269,1e270,1e271,1e272,+ 1e273,1e274,1e275,1e276,1e277,1e278,1e279,1e280,1e281,1e282,1e283,1e284,+ 1e285,1e286,1e287,1e288,1e289,1e290,1e291,1e292,1e293,1e294,1e295,1e296,+ 1e297,1e298,1e299,1e300,1e301,1e302,1e303,1e304,1e305,1e306,1e307,1e308+ };+ return constants[exponent + 323];+ }++ std::pair<char*, type> parse_number(char* p) {+ bool negative = false;+ if ('-' == *p) {+ ++p;+ negative = true;++ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }+ }++ bool try_double = false;++ int i = 0;+ double d = 0.0; // gcc complains that d might be used uninitialized which isn't true. appease the warning anyway.+ if (*p == '0') {+ ++p;+ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }+ } else for (;;) {+ unsigned char c = *p;+ if (c < '0' || c > '9') {+ break;+ }++ ++p;+ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }++ unsigned char digit = c - '0';++ if (SAJSON_UNLIKELY(!try_double && i > INT_MAX / 10 - 9)) {+ // TODO: could split this into two loops+ try_double = true;+ d = i;+ }+ if (SAJSON_UNLIKELY(try_double)) {+ d = 10.0 * d + digit;+ } else {+ i = 10 * i + digit;+ }+ }++ int exponent = 0;++ if ('.' == *p) {+ if (!try_double) {+ try_double = true;+ d = i;+ }+ ++p;+ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }+ for (;;) {+ char c = *p;+ if (c < '0' || c > '9') {+ break;+ }++ ++p;+ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }+ d = d * 10 + (c - '0');+ --exponent;+ }+ }++ char e = *p;+ if ('e' == e || 'E' == e) {+ if (!try_double) {+ try_double = true;+ d = i;+ }+ ++p;+ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }++ bool negativeExponent = false;+ if ('-' == *p) {+ negativeExponent = true;+ ++p;+ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }+ } else if ('+' == *p) {+ ++p;+ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }+ }++ int exp = 0;++ char c = *p;+ if (SAJSON_UNLIKELY(c < '0' || c > '9')) {+ return std::make_pair(make_error(p, ERROR_MSSING_EXPONENT), TYPE_NULL);+ }+ for (;;) {+ exp = 10 * exp + (c - '0');++ ++p;+ if (SAJSON_UNLIKELY(at_eof(p))) {+ return std::make_pair(make_error(p, ERROR_UNEXPECTED_END), TYPE_NULL);+ }++ c = *p;+ if (c < '0' || c > '9') {+ break;+ }+ }+ exponent += (negativeExponent ? -exp : exp);+ }++ if (exponent) {+ assert(try_double);+ d *= pow10(exponent);+ }++ if (negative) {+ if (try_double) {+ d = -d;+ } else {+ i = -i;+ }+ }+ if (try_double) {+ size_t* out = allocator.reserve(double_storage::word_length);+ if (allocator.has_allocation_error()) {+ return std::make_pair(oom(p), TYPE_NULL);+ }+ double_storage::store(out, d);+ return std::make_pair(p, TYPE_DOUBLE);+ } else {+ size_t* out = allocator.reserve(integer_storage::word_length);+ if (allocator.has_allocation_error()) {+ return std::make_pair(oom(p), TYPE_NULL);+ }+ integer_storage::store(out, i);+ return std::make_pair(p, TYPE_INTEGER);+ }+ }++ bool install_array(size_t* array_base, size_t* array_end) {+ const size_t length = array_end - array_base;+ size_t* const new_base = allocator.reserve(length + 1);+ if (SAJSON_UNLIKELY(allocator.has_allocation_error())) {+ return false;+ }+ size_t* out = new_base + length + 1;+ size_t* const structure_end = allocator.get_write_pointer_of(0);++ while (array_end > array_base) {+ size_t element = *--array_end;+ type element_type = get_element_type(element);+ size_t element_value = get_element_value(element);+ size_t* element_ptr = structure_end - element_value;+ *--out = make_element(element_type, element_ptr - new_base);+ }+ *--out = length;+ return true;+ }++ bool install_object(size_t* object_base, size_t* object_end) {+ assert((object_end - object_base) % 3 == 0);+ const size_t length_times_3 = object_end - object_base;+ std::sort(+ reinterpret_cast<object_key_record*>(object_base),+ reinterpret_cast<object_key_record*>(object_end),+ object_key_comparator(input.get_data()));++ size_t* const new_base = allocator.reserve(length_times_3 + 1);+ if (SAJSON_UNLIKELY(allocator.has_allocation_error())) {+ return false;+ }+ size_t* out = new_base + length_times_3 + 1;+ size_t* const structure_end = allocator.get_write_pointer_of(0);++ while (object_end > object_base) {+ size_t element = *--object_end;+ type element_type = get_element_type(element);+ size_t element_value = get_element_value(element);+ size_t* element_ptr = structure_end - element_value;++ *--out = make_element(element_type, element_ptr - new_base);+ *--out = *--object_end;+ *--out = *--object_end;+ }+ *--out = length_times_3 / 3;+ return true;+ }++ char* parse_string(char* p, size_t* tag) {+ ++p; // "+ size_t start = p - input.get_data();+ char* input_end_local = input_end;+ while (input_end_local - p >= 4) {+ if (!internal::is_plain_string_character(p[0])) { goto found; }+ if (!internal::is_plain_string_character(p[1])) { p += 1; goto found; }+ if (!internal::is_plain_string_character(p[2])) { p += 2; goto found; }+ if (!internal::is_plain_string_character(p[3])) { p += 3; goto found; }+ p += 4;+ }+ for (;;) {+ if (SAJSON_UNLIKELY(p >= input_end_local)) {+ return make_error(p, ERROR_UNEXPECTED_END);+ }++ if (!internal::is_plain_string_character(*p)) {+ break;+ }++ ++p;+ }+ found:+ if (SAJSON_LIKELY(*p == '"')) {+ tag[0] = start;+ tag[1] = p - input.get_data();+ *p = '\0';+ return p + 1;+ }++ if (*p >= 0 && *p < 0x20) {+ return make_error(p, ERROR_ILLEGAL_CODEPOINT, static_cast<int>(*p));+ } else {+ // backslash or >0x7f+ return parse_string_slow(p, tag, start);+ }+ }++ char* read_hex(char* p, unsigned& u) {+ unsigned v = 0;+ int i = 4;+ while (i--) {+ unsigned char c = *p++;+ if (c >= '0' && c <= '9') {+ c -= '0';+ } else if (c >= 'a' && c <= 'f') {+ c = c - 'a' + 10;+ } else if (c >= 'A' && c <= 'F') {+ c = c - 'A' + 10;+ } else {+ return make_error(p, ERROR_INVALID_UNICODE_ESCAPE);+ }+ v = (v << 4) + c;+ }++ u = v;+ return p;+ }++ void write_utf8(unsigned codepoint, char*& end) {+ if (codepoint < 0x80) {+ *end++ = codepoint;+ } else if (codepoint < 0x800) {+ *end++ = 0xC0 | (codepoint >> 6);+ *end++ = 0x80 | (codepoint & 0x3F);+ } else if (codepoint < 0x10000) {+ *end++ = 0xE0 | (codepoint >> 12);+ *end++ = 0x80 | ((codepoint >> 6) & 0x3F);+ *end++ = 0x80 | (codepoint & 0x3F);+ } else {+ assert(codepoint < 0x200000);+ *end++ = 0xF0 | (codepoint >> 18);+ *end++ = 0x80 | ((codepoint >> 12) & 0x3F);+ *end++ = 0x80 | ((codepoint >> 6) & 0x3F);+ *end++ = 0x80 | (codepoint & 0x3F);+ }+ }++ char* parse_string_slow(char* p, size_t* tag, size_t start) {+ char* end = p;+ char* input_end_local = input_end;++ for (;;) {+ if (SAJSON_UNLIKELY(p >= input_end_local)) {+ return make_error(p, ERROR_UNEXPECTED_END);+ }++ if (SAJSON_UNLIKELY(*p >= 0 && *p < 0x20)) {+ return make_error(p, ERROR_ILLEGAL_CODEPOINT, static_cast<int>(*p));+ }++ switch (*p) {+ case '"':+ tag[0] = start;+ tag[1] = end - input.get_data();+ *end = '\0';+ return p + 1;++ case '\\':+ ++p;+ if (SAJSON_UNLIKELY(p >= input_end_local)) {+ return make_error(p, ERROR_UNEXPECTED_END);+ }++ char replacement;+ switch (*p) {+ case '"': replacement = '"'; goto replace;+ case '\\': replacement = '\\'; goto replace;+ case '/': replacement = '/'; goto replace;+ case 'b': replacement = '\b'; goto replace;+ case 'f': replacement = '\f'; goto replace;+ case 'n': replacement = '\n'; goto replace;+ case 'r': replacement = '\r'; goto replace;+ case 't': replacement = '\t'; goto replace;+ replace:+ *end++ = replacement;+ ++p;+ break;+ case 'u': {+ ++p;+ if (SAJSON_UNLIKELY(!has_remaining_characters(p, 4))) {+ return make_error(p, ERROR_UNEXPECTED_END);+ }+ unsigned u = 0; // gcc's complaining that this could be used uninitialized. wrong.+ p = read_hex(p, u);+ if (!p) {+ return 0;+ }+ if (u >= 0xD800 && u <= 0xDBFF) {+ if (SAJSON_UNLIKELY(!has_remaining_characters(p, 6))) {+ return make_error(p, ERROR_UNEXPECTED_END_OF_UTF16);+ }+ char p0 = p[0];+ char p1 = p[1];+ if (p0 != '\\' || p1 != 'u') {+ return make_error(p, ERROR_EXPECTED_U);+ }+ p += 2;+ unsigned v = 0; // gcc's complaining that this could be used uninitialized. wrong.+ p = read_hex(p, v);+ if (!p) {+ return p;+ }++ if (v < 0xDC00 || v > 0xDFFF) {+ return make_error(p, ERROR_INVALID_UTF16_TRAIL_SURROGATE);+ }+ u = 0x10000 + (((u - 0xD800) << 10) | (v - 0xDC00));+ }+ write_utf8(u, end);+ break;+ }+ default:+ return make_error(p, ERROR_UNKNOWN_ESCAPE);+ }+ break;++ default:+ // validate UTF-8+ unsigned char c0 = p[0];+ if (c0 < 128) {+ *end++ = *p++;+ } else if (c0 < 224) {+ if (SAJSON_UNLIKELY(!has_remaining_characters(p, 2))) {+ return unexpected_end(p);+ }+ unsigned char c1 = p[1];+ if (c1 < 128 || c1 >= 192) {+ return make_error(p + 1, ERROR_INVALID_UTF8);+ }+ end[0] = c0;+ end[1] = c1;+ end += 2;+ p += 2;+ } else if (c0 < 240) {+ if (SAJSON_UNLIKELY(!has_remaining_characters(p, 3))) {+ return unexpected_end(p);+ }+ unsigned char c1 = p[1];+ if (c1 < 128 || c1 >= 192) {+ return make_error(p + 1, ERROR_INVALID_UTF8);+ }+ unsigned char c2 = p[2];+ if (c2 < 128 || c2 >= 192) {+ return make_error(p + 2, ERROR_INVALID_UTF8);+ }+ end[0] = c0;+ end[1] = c1;+ end[2] = c2;+ end += 3;+ p += 3;+ } else if (c0 < 248) {+ if (SAJSON_UNLIKELY(!has_remaining_characters(p, 4))) {+ return unexpected_end(p);+ }+ unsigned char c1 = p[1];+ if (c1 < 128 || c1 >= 192) {+ return make_error(p + 1, ERROR_INVALID_UTF8);+ }+ unsigned char c2 = p[2];+ if (c2 < 128 || c2 >= 192) {+ return make_error(p + 2, ERROR_INVALID_UTF8);+ }+ unsigned char c3 = p[3];+ if (c3 < 128 || c3 >= 192) {+ return make_error(p + 3, ERROR_INVALID_UTF8);+ }+ end[0] = c0;+ end[1] = c1;+ end[2] = c2;+ end[3] = c3;+ end += 4;+ p += 4;+ } else {+ return make_error(p, ERROR_INVALID_UTF8);+ }+ break;+ }+ }+ }++ mutable_string_view input;+ char* const input_end;+ Allocator allocator;++ type root_type;+ size_t error_line;+ size_t error_column;+ error error_code;+ int error_arg; // optional argument for the error+ };++ template<typename AllocationStrategy, typename StringType>+ document parse(const AllocationStrategy& strategy, const StringType& string) {+ mutable_string_view input(string);++ bool success;+ auto allocator = strategy.make_allocator(input.length(), &success);+ if (!success) {+ return document(input, 1, 1, ERROR_OUT_OF_MEMORY, 0);+ }++ return parser<typename AllocationStrategy::allocator>(+ input,+ std::move(allocator)+ ).get_document();+ }+}
+ cbits/sajson_wrapper.h view
@@ -0,0 +1,28 @@+#ifndef SAJSON_WRAPPER_H+#define SAJSON_WRAPPER_H++#include <stddef.h>+#include <stdint.h>++struct sajson_document;++#ifdef __cplusplus+extern "C" {+#endif++size_t sajson_document_sizeof(void);+struct sajson_document *sajson_parse_single_allocation(char *str, size_t length, size_t *buffer, char *rv);+void sajson_free_document(struct sajson_document *doc);+int sajson_has_error(struct sajson_document *doc);+size_t sajson_get_error_line(struct sajson_document *doc);+size_t sajson_get_error_column(struct sajson_document *doc);+const char *sajson_get_error_message(struct sajson_document *doc);+uint8_t sajson_get_root_type(struct sajson_document *doc);+const size_t *sajson_get_root(struct sajson_document *doc);+const unsigned char *sajson_get_input(struct sajson_document *doc);++#ifdef __cplusplus+}+#endif++#endif
sajson.cabal view
@@ -1,30 +1,40 @@-name: sajson-version: 0.1.0.0-cabal-version: >=1.10-build-type: Simple-license: MIT-license-file: LICENSE-copyright: (c) 2012-2017 Chad Austin- (c) 2017 Zhouyu Qian-maintainer: qzy@qzy.io-stability: experimental-homepage: https://github.com/kccqzy/haskell-sajson#readme-synopsis: Fast JSON parsing powered by Chad Austin's sajson library-description:- A fast JSON parsing library that is faster than aeson.-category: Web, Text, JSON-author: Chad Austin, Zhouyu Qian-tested-with: GHC ==8.0.2-extra-source-files:- README.md+cabal-version: >=1.10+name: sajson+version: 0.2.0.0+license: MIT+license-file: LICENSE+copyright:+ (c) 2012-2017 Chad Austin+ (c) 2017 Zhouyu Qian +maintainer: qzy@qzy.io+author: Chad Austin, Zhouyu Qian+stability: experimental+tested-with: ghc ==8.0.2+homepage: https://github.com/kccqzy/haskell-sajson#readme+synopsis: Fast JSON parsing powered by Chad Austin's sajson library+description: A fast JSON parsing library that is faster than aeson.+category: Web, Text, JSON+build-type: Simple+extra-source-files: README.md+ source-repository head- type: git+ type: git location: https://github.com/kccqzy/haskell-sajson library- exposed-modules:- Data.Sajson+ exposed-modules: Data.Sajson+ cc-options: -Wall -O3 -march=native -std=c++11 -fomit-frame-pointer+ c-sources:+ cbits/sajson_wrapper.cpp+ cbits/sajson_wrapper.h++ hs-source-dirs: src+ default-language: Haskell2010+ extra-libraries: stdc+++ include-dirs: cbits+ install-includes: cbits/sajson.hpp+ ghc-options: -Wall -O2 build-depends: base >=4.7 && <5, bytestring >=0.10.8.1 && <0.11,@@ -33,34 +43,28 @@ vector >=0.11.0.0 && <0.12, scientific >=0.3.4.12 && <0.4, unordered-containers >=0.2.8.0 && <0.3- cc-options: -Wall -O3 -march=native -std=c++11 -fomit-frame-pointer- c-sources:- cbits/sajson_wrapper.cpp- default-language: Haskell2010- extra-libraries:- stdc++- include-dirs: cbits- hs-source-dirs: src- ghc-options: -Wall -O2 executable sajson-bench- main-is: Bench.hs+ main-is: Bench.hs+ hs-source-dirs: bench+ default-language: Haskell2010+ ghc-options: -Wall -threaded -rtsopts build-depends:- base >=4.9.1.0 && <5,- sajson,+ base >=4.9.1.0 && <4.10,+ sajson -any, criterion >=1.1.4.0 && <1.2, aeson >=1.0.2.1 && <1.1, bytestring >=0.10.8.1 && <0.11- default-language: Haskell2010- hs-source-dirs: bench- ghc-options: -Wall -threaded -rtsopts test-suite sajson-test- type: exitcode-stdio-1.0- main-is: Spec.hs+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: test+ default-language: Haskell2010+ ghc-options: -Wall -threaded -rtsopts build-depends:- base >=4.9.1.0 && <5,- sajson,+ base >=4.9.1.0 && <4.10,+ sajson -any, QuickCheck >=2.9.2 && <2.10, aeson >=1.0.2.1 && <1.1, bytestring >=0.10.8.1 && <0.11,@@ -69,6 +73,3 @@ text >=1.2.2.1 && <1.3, unordered-containers >=0.2.8.0 && <0.3, vector >=0.11.0.0 && <0.12- default-language: Haskell2010- hs-source-dirs: test- ghc-options: -Wall -threaded -rtsopts