mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
Runtime dispatch
* Attempt 1 - fn targeting GCC won't work with templates with different targets, need to specialize all the way up the call stack. * Compiles properly with cmake. Does not with the Makefile. * Compilation works with Makefile * instruction_set changes to architecture * some aesthetic changes * fix amalgation and tests + aesthetic changes * This now compiles and passes tests under CLANG * Minor correction. * Trying to make it work on ARM * Adding missing namespace * Missing bracket * Fixing minor compilation issues. * Getting parse to use runtime dispatch * Fixing amalgamation script. * Making sure that NEON is supported. * Fixing typo * Merging https://github.com/lemire/simdjson/pull/229 * Manual merge of https://github.com/lemire/simdjson/pull/229 by @jkeiser (second part) * Trying another way. * Removing the paral. * Fixing the make file * Let us make the practice run long enough. * Resolved the awful slowness. * Cleaning the README.md * With runtime dispatching, we should not need flags anymore. * Changing isa detection file's name + fixing typos.
This commit is contained in:
@@ -63,6 +63,30 @@ size_t jsonminify(const unsigned char *bytes, size_t howmany,
|
||||
#include <cstring>
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
|
||||
// some intrinsics are missing under GCC?
|
||||
#ifndef __clang__
|
||||
#ifndef _MSC_VER
|
||||
static __m256i inline _mm256_loadu2_m128i(__m128i const *__addr_hi,
|
||||
__m128i const *__addr_lo) {
|
||||
__m256i __v256 = _mm256_castsi128_si256(_mm_loadu_si128(__addr_lo));
|
||||
return _mm256_insertf128_si256(__v256, _mm_loadu_si128(__addr_hi), 1);
|
||||
}
|
||||
|
||||
static inline void _mm256_storeu2_m128i(__m128i *__addr_hi, __m128i *__addr_lo,
|
||||
__m256i __a) {
|
||||
__m128i __v128;
|
||||
__v128 = _mm256_castsi256_si128(__a);
|
||||
_mm_storeu_si128(__addr_lo, __v128);
|
||||
__v128 = _mm256_extractf128_si256(__a, 1);
|
||||
_mm_storeu_si128(__addr_hi, __v128);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// a straightforward comparison of a mask against input.
|
||||
static uint64_t cmp_mask_against_input_mini(__m256i input_lo, __m256i input_hi,
|
||||
__m256i mask) {
|
||||
|
||||
+28
-35
@@ -6,52 +6,45 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "simdjson/simdjson.h"
|
||||
#include "simdjson/isadetection.h"
|
||||
#include "simdjson/portability.h"
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
architecture find_best_supported_implementation() {
|
||||
constexpr uint32_t haswell_flags = SIMDExtensions::AVX2 | SIMDExtensions::PCLMULQDQ
|
||||
| SIMDExtensions::BMI1 | SIMDExtensions::BMI2;
|
||||
constexpr uint32_t westmere_flags = SIMDExtensions::SSE42 | SIMDExtensions::PCLMULQDQ;
|
||||
|
||||
uint32_t supports = detect_supported_architectures();
|
||||
// Order from best to worst (within architecture)
|
||||
if ((haswell_flags & supports) == haswell_flags) return architecture::haswell;
|
||||
if ((westmere_flags & supports) == westmere_flags) return architecture::westmere;
|
||||
if (SIMDExtensions::NEON) return architecture::arm64;
|
||||
|
||||
return architecture::none;
|
||||
}
|
||||
|
||||
// Responsible to select the best json_parse implementation
|
||||
int json_parse_dispatch(const uint8_t *buf, size_t len, ParsedJson &pj, bool reallocifneeded) {
|
||||
// Versions for each implementation
|
||||
#ifdef __AVX2__
|
||||
json_parse_functype* avx_implementation = &json_parse_implementation<instruction_set::avx2>;
|
||||
#endif
|
||||
#if defined(__SSE4_2__) || (defined(_MSC_VER) && defined(_M_AMD64))
|
||||
json_parse_functype* sse4_2_implementation = &json_parse_implementation<instruction_set::sse4_2>;
|
||||
#endif
|
||||
#if defined(__ARM_NEON) || (defined(_MSC_VER) && defined(_M_ARM64))
|
||||
json_parse_functype* neon_implementation = &json_parse_implementation<instruction_set::neon>;
|
||||
#endif
|
||||
|
||||
// Determining which implementation is the more suitable
|
||||
// Should be done at runtime. Does not make any sense on preprocessor.
|
||||
#ifdef __AVX2__
|
||||
instruction_set best_implementation = instruction_set::avx2;
|
||||
#elif defined (__SSE4_2__) || (defined(_MSC_VER) && defined(_M_AMD64))
|
||||
instruction_set best_implementation = instruction_set::sse4_2;
|
||||
#elif defined (__ARM_NEON) || (defined(_MSC_VER) && defined(_M_ARM64))
|
||||
instruction_set best_implementation = instruction_set::neon;
|
||||
#else
|
||||
instruction_set best_implementation = instruction_set::none;
|
||||
#endif
|
||||
|
||||
architecture best_implementation = find_best_supported_implementation();
|
||||
// Selecting the best implementation
|
||||
switch (best_implementation) {
|
||||
#ifdef __AVX2__
|
||||
case instruction_set::avx2 :
|
||||
json_parse_ptr = avx_implementation;
|
||||
#ifdef IS_X86_64
|
||||
case architecture::haswell:
|
||||
json_parse_ptr = &json_parse_implementation<architecture::haswell>;
|
||||
break;
|
||||
case architecture::westmere:
|
||||
json_parse_ptr = &json_parse_implementation<architecture::westmere>;
|
||||
break;
|
||||
#endif
|
||||
#if defined(__SSE4_2__) || (defined(_MSC_VER) && defined(_M_AMD64))
|
||||
case instruction_set::sse4_2 :
|
||||
json_parse_ptr = sse4_2_implementation;
|
||||
break;
|
||||
#endif
|
||||
#if defined(__ARM_NEON) || (defined(_MSC_VER) && defined(_M_ARM64))
|
||||
case instruction_set::neon :
|
||||
json_parse_ptr = neon_implementation;
|
||||
#ifdef IS_ARM64
|
||||
case architecture::arm64:
|
||||
json_parse_ptr = &json_parse_implementation<architecture::arm64>;
|
||||
break;
|
||||
#endif
|
||||
default :
|
||||
std::cerr << "No implemented simd instruction set supported" << std::endl;
|
||||
std::cerr << "The processor is not supported by simdjson." << std::endl;
|
||||
return simdjson::UNEXPECTED_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1,37 @@
|
||||
// File kept in case we want to reuse it soon. (many configuration files to edit)
|
||||
#include "simdjson/portability.h"
|
||||
|
||||
|
||||
#ifdef IS_X86_64
|
||||
|
||||
#include "simdjson/stage1_find_marks_haswell.h"
|
||||
#include "simdjson/stage1_find_marks_westmere.h"
|
||||
TARGET_HASWELL
|
||||
namespace simdjson {
|
||||
template<>
|
||||
int find_structural_bits<architecture::haswell>(const uint8_t *buf, size_t len, ParsedJson &pj) {
|
||||
FIND_STRUCTURAL_BITS(architecture::haswell, buf, len, pj);
|
||||
}
|
||||
} // simdjson
|
||||
UNTARGET_REGION
|
||||
|
||||
TARGET_WESTMERE
|
||||
namespace simdjson {
|
||||
template<>
|
||||
int find_structural_bits<architecture::westmere>(const uint8_t *buf, size_t len, ParsedJson &pj) {
|
||||
FIND_STRUCTURAL_BITS(architecture::westmere, buf, len, pj);
|
||||
}
|
||||
} // simdjson
|
||||
UNTARGET_REGION
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef IS_ARM64
|
||||
#include "simdjson/stage1_find_marks_arm64.h"
|
||||
namespace simdjson {
|
||||
template<>
|
||||
int find_structural_bits<architecture::arm64>(const uint8_t *buf, size_t len, ParsedJson &pj) {
|
||||
FIND_STRUCTURAL_BITS(architecture::arm64, buf, len, pj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
+534
-1
@@ -1 +1,534 @@
|
||||
// File kept in case we want to reuse it soon. (many configuration files to edit)
|
||||
#include "simdjson/stage2_build_tape.h"
|
||||
|
||||
namespace simdjson {
|
||||
|
||||
// this macro reads the next structural character, updating idx, i and c.
|
||||
#define UPDATE_CHAR() \
|
||||
{ \
|
||||
idx = pj.structural_indexes[i++]; \
|
||||
c = buf[idx]; \
|
||||
}
|
||||
|
||||
#ifdef SIMDJSON_USE_COMPUTED_GOTO
|
||||
#define SET_GOTO_ARRAY_CONTINUE() pj.ret_address[depth] = &&array_continue;
|
||||
#define SET_GOTO_OBJECT_CONTINUE() pj.ret_address[depth] = &&object_continue;
|
||||
#define SET_GOTO_START_CONTINUE() pj.ret_address[depth] = &&start_continue;
|
||||
#define GOTO_CONTINUE() goto *pj.ret_address[depth];
|
||||
#else
|
||||
#define SET_GOTO_ARRAY_CONTINUE() pj.ret_address[depth] = 'a';
|
||||
#define SET_GOTO_OBJECT_CONTINUE() pj.ret_address[depth] = 'o';
|
||||
#define SET_GOTO_START_CONTINUE() pj.ret_address[depth] = 's';
|
||||
#define GOTO_CONTINUE() { \
|
||||
if(pj.ret_address[depth] == 'a') { \
|
||||
goto array_continue; \
|
||||
} else if (pj.ret_address[depth] == 'o') { \
|
||||
goto object_continue; \
|
||||
} else { \
|
||||
goto start_continue; \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
/************
|
||||
* The JSON is parsed to a tape, see the accompanying tape.md file
|
||||
* for documentation.
|
||||
***********/
|
||||
// We need to compile that code for multiple architectures. However, target attributes can be used
|
||||
// only once by function definition. Huge macro seemed better than huge code duplication.
|
||||
// int UNIFIED_MACHINE(const uint8_t *buf, size_t len, ParsedJson &pj)
|
||||
#define UNIFIED_MACHINE(T, buf, len, pj) { \
|
||||
if (ALLOW_SAME_PAGE_BUFFER_OVERRUN) { \
|
||||
memset((uint8_t*)buf + len, 0, SIMDJSON_PADDING); /* to please valgrind */ \
|
||||
} \
|
||||
uint32_t i = 0; /* index of the structural character (0,1,2,3...) */ \
|
||||
uint32_t idx; /* location of the structural character in the input (buf) */ \
|
||||
uint8_t c; /* used to track the (structural) character we are looking at, updated */ \
|
||||
/* by UPDATE_CHAR macro */ \
|
||||
uint32_t depth = 0; /* could have an arbitrary starting depth */ \
|
||||
pj.init(); /* sets isvalid to false */ \
|
||||
if(pj.bytecapacity < len) { \
|
||||
pj.errorcode = simdjson::CAPACITY; \
|
||||
return pj.errorcode; \
|
||||
} \
|
||||
\
|
||||
/*//////////////////////////// START STATE ///////////////////////////// */ \
|
||||
SET_GOTO_START_CONTINUE() \
|
||||
pj.containing_scope_offset[depth] = pj.get_current_loc(); \
|
||||
pj.write_tape(0, 'r'); /* r for root, 0 is going to get overwritten */ \
|
||||
/* the root is used, if nothing else, to capture the size of the tape */ \
|
||||
depth++; /* everything starts at depth = 1, depth = 0 is just for the root, the root may contain an object, an array or something else. */ \
|
||||
if (depth >= pj.depthcapacity) { \
|
||||
goto fail; \
|
||||
} \
|
||||
\
|
||||
UPDATE_CHAR(); \
|
||||
switch (c) { \
|
||||
case '{': \
|
||||
pj.containing_scope_offset[depth] = pj.get_current_loc(); \
|
||||
SET_GOTO_START_CONTINUE(); \
|
||||
depth++; \
|
||||
if (depth >= pj.depthcapacity) { \
|
||||
goto fail; \
|
||||
} \
|
||||
pj.write_tape(0, c); /* strangely, moving this to object_begin slows things down */ \
|
||||
goto object_begin; \
|
||||
case '[': \
|
||||
pj.containing_scope_offset[depth] = pj.get_current_loc(); \
|
||||
SET_GOTO_START_CONTINUE(); \
|
||||
depth++; \
|
||||
if (depth >= pj.depthcapacity) { \
|
||||
goto fail; \
|
||||
} \
|
||||
pj.write_tape(0, c); \
|
||||
goto array_begin; \
|
||||
/* #define SIMDJSON_ALLOWANYTHINGINROOT */ \
|
||||
/* A JSON text is a serialized value. Note that certain previous */ \
|
||||
/* specifications of JSON constrained a JSON text to be an object or an */ \
|
||||
/* array. Implementations that generate only objects or arrays where a */ \
|
||||
/* JSON text is called for will be interoperable in the sense that all */ \
|
||||
/* implementations will accept these as conforming JSON texts. */ \
|
||||
/* https://tools.ietf.org/html/rfc8259 */ \
|
||||
/* #ifdef SIMDJSON_ALLOWANYTHINGINROOT */ \
|
||||
case '"': { \
|
||||
if (!parse_string<T>(buf, len, pj, depth, idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case 't': { \
|
||||
/* we need to make a copy to make sure that the string is space terminated. */ \
|
||||
/* this only applies to the JSON document made solely of the true value. */ \
|
||||
/* this will almost never be called in practice */ \
|
||||
char * copy = static_cast<char *>(malloc(len + SIMDJSON_PADDING)); \
|
||||
if(copy == nullptr) { \
|
||||
goto fail; \
|
||||
} \
|
||||
memcpy(copy, buf, len); \
|
||||
copy[len] = ' '; \
|
||||
if (!is_valid_true_atom(reinterpret_cast<const uint8_t *>(copy) + idx)) { \
|
||||
free(copy); \
|
||||
goto fail; \
|
||||
} \
|
||||
free(copy); \
|
||||
pj.write_tape(0, c); \
|
||||
break; \
|
||||
} \
|
||||
case 'f': { \
|
||||
/* we need to make a copy to make sure that the string is space terminated. */ \
|
||||
/* this only applies to the JSON document made solely of the false value. */ \
|
||||
/* this will almost never be called in practice */ \
|
||||
char * copy = static_cast<char *>(malloc(len + SIMDJSON_PADDING)); \
|
||||
if(copy == nullptr) { \
|
||||
goto fail; \
|
||||
} \
|
||||
memcpy(copy, buf, len); \
|
||||
copy[len] = ' '; \
|
||||
if (!is_valid_false_atom(reinterpret_cast<const uint8_t *>(copy) + idx)) { \
|
||||
free(copy); \
|
||||
goto fail; \
|
||||
} \
|
||||
free(copy); \
|
||||
pj.write_tape(0, c); \
|
||||
break; \
|
||||
} \
|
||||
case 'n': { \
|
||||
/* we need to make a copy to make sure that the string is space terminated. */ \
|
||||
/* this only applies to the JSON document made solely of the null value. */ \
|
||||
/* this will almost never be called in practice */ \
|
||||
char * copy = static_cast<char *>(malloc(len + SIMDJSON_PADDING)); \
|
||||
if(copy == nullptr) { \
|
||||
goto fail; \
|
||||
} \
|
||||
memcpy(copy, buf, len); \
|
||||
copy[len] = ' '; \
|
||||
if (!is_valid_null_atom(reinterpret_cast<const uint8_t *>(copy) + idx)) { \
|
||||
free(copy); \
|
||||
goto fail; \
|
||||
} \
|
||||
free(copy); \
|
||||
pj.write_tape(0, c); \
|
||||
break; \
|
||||
} \
|
||||
case '0': \
|
||||
case '1': \
|
||||
case '2': \
|
||||
case '3': \
|
||||
case '4': \
|
||||
case '5': \
|
||||
case '6': \
|
||||
case '7': \
|
||||
case '8': \
|
||||
case '9': { \
|
||||
/* we need to make a copy to make sure that the string is space terminated. */ \
|
||||
/* this is done only for JSON documents made of a sole number */ \
|
||||
/* this will almost never be called in practice. We terminate with a space */ \
|
||||
/* because we do not want to allow NULLs in the middle of a number (whereas a */ \
|
||||
/* space in the middle of a number would be identified in stage 1). */ \
|
||||
char * copy = static_cast<char *>(malloc(len + SIMDJSON_PADDING)); \
|
||||
if(copy == nullptr) { \
|
||||
goto fail; \
|
||||
} \
|
||||
memcpy(copy, buf, len); \
|
||||
copy[len] = ' '; \
|
||||
if (!parse_number(reinterpret_cast<const uint8_t *>(copy), pj, idx, false)) { \
|
||||
free(copy); \
|
||||
goto fail; \
|
||||
} \
|
||||
free(copy); \
|
||||
break; \
|
||||
} \
|
||||
case '-': { \
|
||||
/* we need to make a copy to make sure that the string is NULL terminated. */ \
|
||||
/* this is done only for JSON documents made of a sole number */ \
|
||||
/* this will almost never be called in practice */ \
|
||||
char * copy = static_cast<char *>(malloc(len + SIMDJSON_PADDING)); \
|
||||
if(copy == nullptr) { \
|
||||
goto fail; \
|
||||
} \
|
||||
memcpy(copy, buf, len); \
|
||||
copy[len] = ' '; \
|
||||
if (!parse_number(reinterpret_cast<const uint8_t *>(copy), pj, idx, true)) { \
|
||||
free(copy); \
|
||||
goto fail; \
|
||||
} \
|
||||
free(copy); \
|
||||
break; \
|
||||
} \
|
||||
/* #endif // ALLOWANYTHINGINROOT */ \
|
||||
default: \
|
||||
goto fail; \
|
||||
} \
|
||||
start_continue: \
|
||||
/* the string might not be NULL terminated. */ \
|
||||
if(i + 1 == pj.n_structural_indexes) { \
|
||||
goto succeed; \
|
||||
} else { \
|
||||
goto fail; \
|
||||
} \
|
||||
/*//////////////////////////// OBJECT STATES ///////////////////////////// */ \
|
||||
\
|
||||
object_begin: \
|
||||
UPDATE_CHAR(); \
|
||||
switch (c) { \
|
||||
case '"': { \
|
||||
if (!parse_string<T>(buf, len, pj, depth, idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
goto object_key_state; \
|
||||
} \
|
||||
case '}': \
|
||||
goto scope_end; /* could also go to object_continue */ \
|
||||
default: \
|
||||
goto fail; \
|
||||
} \
|
||||
\
|
||||
object_key_state: \
|
||||
UPDATE_CHAR(); \
|
||||
if (c != ':') { \
|
||||
goto fail; \
|
||||
} \
|
||||
UPDATE_CHAR(); \
|
||||
switch (c) { \
|
||||
case '"': { \
|
||||
if (!parse_string<T>(buf, len, pj, depth, idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case 't': \
|
||||
if (!is_valid_true_atom(buf + idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
pj.write_tape(0, c); \
|
||||
break; \
|
||||
case 'f': \
|
||||
if (!is_valid_false_atom(buf + idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
pj.write_tape(0, c); \
|
||||
break; \
|
||||
case 'n': \
|
||||
if (!is_valid_null_atom(buf + idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
pj.write_tape(0, c); \
|
||||
break; \
|
||||
case '0': \
|
||||
case '1': \
|
||||
case '2': \
|
||||
case '3': \
|
||||
case '4': \
|
||||
case '5': \
|
||||
case '6': \
|
||||
case '7': \
|
||||
case '8': \
|
||||
case '9': { \
|
||||
if (!parse_number(buf, pj, idx, false)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case '-': { \
|
||||
if (!parse_number(buf, pj, idx, true)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case '{': { \
|
||||
pj.containing_scope_offset[depth] = pj.get_current_loc(); \
|
||||
pj.write_tape(0, c); /* here the compilers knows what c is so this gets optimized */ \
|
||||
/* we have not yet encountered } so we need to come back for it */ \
|
||||
SET_GOTO_OBJECT_CONTINUE() \
|
||||
/* we found an object inside an object, so we need to increment the depth */ \
|
||||
depth++; \
|
||||
if (depth >= pj.depthcapacity) { \
|
||||
goto fail; \
|
||||
} \
|
||||
\
|
||||
goto object_begin; \
|
||||
} \
|
||||
case '[': { \
|
||||
pj.containing_scope_offset[depth] = pj.get_current_loc(); \
|
||||
pj.write_tape(0, c); /* here the compilers knows what c is so this gets optimized */ \
|
||||
/* we have not yet encountered } so we need to come back for it */ \
|
||||
SET_GOTO_OBJECT_CONTINUE() \
|
||||
/* we found an array inside an object, so we need to increment the depth */ \
|
||||
depth++; \
|
||||
if (depth >= pj.depthcapacity) { \
|
||||
goto fail; \
|
||||
} \
|
||||
goto array_begin; \
|
||||
} \
|
||||
default: \
|
||||
goto fail; \
|
||||
} \
|
||||
\
|
||||
object_continue: \
|
||||
UPDATE_CHAR(); \
|
||||
switch (c) { \
|
||||
case ',': \
|
||||
UPDATE_CHAR(); \
|
||||
if (c != '"') { \
|
||||
goto fail; \
|
||||
} else { \
|
||||
if (!parse_string<T>(buf, len, pj, depth, idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
goto object_key_state; \
|
||||
} \
|
||||
case '}': \
|
||||
goto scope_end; \
|
||||
default: \
|
||||
goto fail; \
|
||||
} \
|
||||
\
|
||||
/*//////////////////////////// COMMON STATE ///////////////////////////// */ \
|
||||
\
|
||||
scope_end: \
|
||||
/* write our tape location to the header scope */ \
|
||||
depth--; \
|
||||
pj.write_tape(pj.containing_scope_offset[depth], c); \
|
||||
pj.annotate_previousloc(pj.containing_scope_offset[depth], \
|
||||
pj.get_current_loc()); \
|
||||
/* goto saved_state */ \
|
||||
GOTO_CONTINUE() \
|
||||
\
|
||||
/*//////////////////////////// ARRAY STATES ///////////////////////////// */ \
|
||||
array_begin: \
|
||||
UPDATE_CHAR(); \
|
||||
if (c == ']') { \
|
||||
goto scope_end; /* could also go to array_continue */ \
|
||||
} \
|
||||
\
|
||||
main_array_switch: \
|
||||
/* we call update char on all paths in, so we can peek at c on the */ \
|
||||
/* on paths that can accept a close square brace (post-, and at start) */ \
|
||||
switch (c) { \
|
||||
case '"': { \
|
||||
if (!parse_string<T>(buf, len, pj, depth, idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case 't': \
|
||||
if (!is_valid_true_atom(buf + idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
pj.write_tape(0, c); \
|
||||
break; \
|
||||
case 'f': \
|
||||
if (!is_valid_false_atom(buf + idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
pj.write_tape(0, c); \
|
||||
break; \
|
||||
case 'n': \
|
||||
if (!is_valid_null_atom(buf + idx)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
pj.write_tape(0, c); \
|
||||
break; /* goto array_continue; */ \
|
||||
\
|
||||
case '0': \
|
||||
case '1': \
|
||||
case '2': \
|
||||
case '3': \
|
||||
case '4': \
|
||||
case '5': \
|
||||
case '6': \
|
||||
case '7': \
|
||||
case '8': \
|
||||
case '9': { \
|
||||
if (!parse_number(buf, pj, idx, false)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
break; /* goto array_continue; */ \
|
||||
} \
|
||||
case '-': { \
|
||||
if (!parse_number(buf, pj, idx, true)) { \
|
||||
goto fail; \
|
||||
} \
|
||||
break; /* goto array_continue; */ \
|
||||
} \
|
||||
case '{': { \
|
||||
/* we have not yet encountered ] so we need to come back for it */ \
|
||||
pj.containing_scope_offset[depth] = pj.get_current_loc(); \
|
||||
pj.write_tape(0, c); /* here the compilers knows what c is so this gets optimized */ \
|
||||
SET_GOTO_ARRAY_CONTINUE() \
|
||||
/* we found an object inside an array, so we need to increment the depth */ \
|
||||
depth++; \
|
||||
if (depth >= pj.depthcapacity) { \
|
||||
goto fail; \
|
||||
} \
|
||||
\
|
||||
goto object_begin; \
|
||||
} \
|
||||
case '[': { \
|
||||
/* we have not yet encountered ] so we need to come back for it */ \
|
||||
pj.containing_scope_offset[depth] = pj.get_current_loc(); \
|
||||
pj.write_tape(0, c); /* here the compilers knows what c is so this gets optimized */ \
|
||||
SET_GOTO_ARRAY_CONTINUE() \
|
||||
/* we found an array inside an array, so we need to increment the depth */ \
|
||||
depth++; \
|
||||
if (depth >= pj.depthcapacity) { \
|
||||
goto fail; \
|
||||
} \
|
||||
goto array_begin; \
|
||||
} \
|
||||
default: \
|
||||
goto fail; \
|
||||
} \
|
||||
\
|
||||
array_continue: \
|
||||
UPDATE_CHAR(); \
|
||||
switch (c) { \
|
||||
case ',': \
|
||||
UPDATE_CHAR(); \
|
||||
goto main_array_switch; \
|
||||
case ']': \
|
||||
goto scope_end; \
|
||||
default: \
|
||||
goto fail; \
|
||||
} \
|
||||
\
|
||||
/*//////////////////////////// FINAL STATES ///////////////////////////// */ \
|
||||
\
|
||||
succeed: \
|
||||
depth --; \
|
||||
if(depth != 0) { \
|
||||
fprintf(stderr, "internal bug\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
if(pj.containing_scope_offset[depth] != 0) { \
|
||||
fprintf(stderr, "internal bug\n"); \
|
||||
abort(); \
|
||||
} \
|
||||
pj.annotate_previousloc(pj.containing_scope_offset[depth], \
|
||||
pj.get_current_loc()); \
|
||||
pj.write_tape(pj.containing_scope_offset[depth], 'r'); /* r is root */ \
|
||||
\
|
||||
pj.isvalid = true; \
|
||||
pj.errorcode = simdjson::SUCCESS; \
|
||||
return pj.errorcode; \
|
||||
fail: \
|
||||
/* we do not need the next line because this is done by pj.init(), pessimistically. */ \
|
||||
/* pj.isvalid = false; */ \
|
||||
/* At this point in the code, we have all the time in the world. */ \
|
||||
/* Note that we know exactly where we are in the document so we could, */ \
|
||||
/* without any overhead on the processing code, report a specific location. */ \
|
||||
/* We could even trigger special code paths to assess what happened carefully, */ \
|
||||
/* all without any added cost. */ \
|
||||
if (depth >= pj.depthcapacity) { \
|
||||
pj.errorcode = simdjson::DEPTH_ERROR; \
|
||||
return pj.errorcode; \
|
||||
} \
|
||||
switch(c) { \
|
||||
case '"': \
|
||||
pj.errorcode = simdjson::STRING_ERROR; \
|
||||
return pj.errorcode; \
|
||||
case '0': \
|
||||
case '1': \
|
||||
case '2': \
|
||||
case '3': \
|
||||
case '4': \
|
||||
case '5': \
|
||||
case '6': \
|
||||
case '7': \
|
||||
case '8': \
|
||||
case '9': \
|
||||
case '-': \
|
||||
pj.errorcode = simdjson::NUMBER_ERROR; \
|
||||
return pj.errorcode; \
|
||||
case 't': \
|
||||
pj.errorcode = simdjson::T_ATOM_ERROR; \
|
||||
return pj.errorcode; \
|
||||
case 'n': \
|
||||
pj.errorcode = simdjson::N_ATOM_ERROR; \
|
||||
return pj.errorcode; \
|
||||
case 'f': \
|
||||
pj.errorcode = simdjson::F_ATOM_ERROR; \
|
||||
return pj.errorcode; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
pj.errorcode = simdjson::TAPE_ERROR; \
|
||||
return pj.errorcode; \
|
||||
} \
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef IS_X86_64
|
||||
TARGET_HASWELL
|
||||
namespace simdjson {
|
||||
template<>
|
||||
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER
|
||||
int unified_machine<architecture::haswell>(const uint8_t *buf, size_t len, ParsedJson &pj) {
|
||||
UNIFIED_MACHINE(architecture::haswell, buf, len, pj);
|
||||
}
|
||||
}
|
||||
UNTARGET_REGION
|
||||
|
||||
TARGET_WESTMERE
|
||||
namespace simdjson {
|
||||
template<>
|
||||
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER
|
||||
int unified_machine<architecture::westmere>(const uint8_t *buf, size_t len, ParsedJson &pj) {
|
||||
UNIFIED_MACHINE(architecture::westmere, buf, len, pj);
|
||||
}
|
||||
}
|
||||
UNTARGET_REGION
|
||||
#endif // IS_X86_64
|
||||
|
||||
#ifdef IS_ARM64
|
||||
namespace simdjson {
|
||||
template<>
|
||||
WARN_UNUSED ALLOW_SAME_PAGE_BUFFER_OVERRUN_QUALIFIER LENIENT_MEM_SANITIZER
|
||||
int unified_machine<architecture::arm64>(const uint8_t *buf, size_t len, ParsedJson &pj) {
|
||||
UNIFIED_MACHINE(architecture::arm64, buf, len, pj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user