Files
simdjson-simdjson/singleheader/simdjson.cpp
T
Daniel Lemire eba02dc1b9 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.
2019-07-28 22:46:33 -04:00

1405 lines
98 KiB
C++

/* auto-generated on Sun 28 Jul 2019 18:08:49 EDT. Do not edit! */
#include "simdjson.h"
/* used for http://dmalloc.com/ Dmalloc - Debug Malloc Library */
#ifdef DMALLOC
#include "dmalloc.h"
#endif
/* begin file src/simdjson.cpp */
#include <map>
namespace simdjson {
const std::map<int, const std::string> errorStrings = {
{SUCCESS, "No errors"},
{CAPACITY, "This ParsedJson can't support a document that big"},
{MEMALLOC, "Error allocating memory, we're most likely out of memory"},
{TAPE_ERROR, "Something went wrong while writing to the tape"},
{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"},
{UNITIALIZED, "Unitialized"},
{EMPTY, "Empty"},
{UNESCAPED_CHARS, "Within strings, some characters must be escapted, we found unescapted characters"},
{UNEXPECTED_ERROR, "Unexpected error, consider reporting this problem as you may have found a bug in simdjson"},
};
const std::string& errorMsg(const int errorCode) {
return errorStrings.at(errorCode);
}
}
/* end file src/simdjson.cpp */
/* begin file src/jsonioutil.cpp */
#include <cstring>
#include <cstdlib>
namespace simdjson {
char * allocate_padded_buffer(size_t length) {
// we could do a simple malloc
//return (char *) malloc(length + SIMDJSON_PADDING);
// However, we might as well align to cache lines...
size_t totalpaddedlength = length + SIMDJSON_PADDING;
char *padded_buffer = aligned_malloc_char(64, totalpaddedlength);
return padded_buffer;
}
padded_string get_corpus(const std::string& filename) {
std::FILE *fp = std::fopen(filename.c_str(), "rb");
if (fp != nullptr) {
std::fseek(fp, 0, SEEK_END);
size_t len = std::ftell(fp);
padded_string s(len);
if(s.data() == nullptr) {
std::fclose(fp);
throw std::runtime_error("could not allocate memory");
}
std::rewind(fp);
size_t readb = std::fread(s.data(), 1, len, fp);
std::fclose(fp);
if(readb != len) {
throw std::runtime_error("could not read the data");
}
return s;
}
throw std::runtime_error("could not load corpus");
}
}
/* end file src/jsonioutil.cpp */
/* begin file src/jsonminifier.cpp */
#include <cstdint>
#ifndef __AVX2__
namespace simdjson {
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,
};
size_t jsonminify(const unsigned char *bytes, size_t howmany,
unsigned char *out) {
size_t i = 0, pos = 0;
uint8_t quote = 0;
uint8_t nonescape = 1;
while (i < howmany) {
unsigned char c = bytes[i];
uint8_t *meta = jump_table + 3 * c;
quote = quote ^ (meta[0] & nonescape);
out[pos] = c;
pos += meta[2] | quote;
i += 1;
nonescape = (~nonescape) | (meta[1]);
}
return pos;
}
} // namespace simdjson
#else
#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) {
__m256i cmp_res_0 = _mm256_cmpeq_epi8(input_lo, mask);
uint64_t res_0 = static_cast<uint32_t>(_mm256_movemask_epi8(cmp_res_0));
__m256i cmp_res_1 = _mm256_cmpeq_epi8(input_hi, mask);
uint64_t res_1 = _mm256_movemask_epi8(cmp_res_1);
return res_0 | (res_1 << 32);
}
// take input from buf and remove useless whitespace, input and output can be
// the same, result is null terminated, return the string length (minus the null termination)
size_t jsonminify(const uint8_t *buf, size_t len, uint8_t *out) {
// Useful constant masks
const uint64_t even_bits = 0x5555555555555555ULL;
const uint64_t odd_bits = ~even_bits;
uint8_t *initout(out);
uint64_t prev_iter_ends_odd_backslash =
0ULL; // either 0 or 1, but a 64-bit value
uint64_t prev_iter_inside_quote = 0ULL; // either all zeros or all ones
size_t idx = 0;
if (len >= 64) {
size_t avxlen = len - 63;
for (; idx < avxlen; idx += 64) {
__m256i input_lo = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf + idx + 0));
__m256i input_hi = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf + idx + 32));
uint64_t bs_bits = cmp_mask_against_input_mini(input_lo, input_hi,
_mm256_set1_epi8('\\'));
uint64_t start_edges = bs_bits & ~(bs_bits << 1);
uint64_t even_start_mask = even_bits ^ prev_iter_ends_odd_backslash;
uint64_t even_starts = start_edges & even_start_mask;
uint64_t odd_starts = start_edges & ~even_start_mask;
uint64_t even_carries = bs_bits + even_starts;
uint64_t odd_carries;
bool iter_ends_odd_backslash = add_overflow(
bs_bits, odd_starts, &odd_carries);
odd_carries |= prev_iter_ends_odd_backslash;
prev_iter_ends_odd_backslash = iter_ends_odd_backslash ? 0x1ULL : 0x0ULL;
uint64_t even_carry_ends = even_carries & ~bs_bits;
uint64_t odd_carry_ends = odd_carries & ~bs_bits;
uint64_t even_start_odd_end = even_carry_ends & odd_bits;
uint64_t odd_start_even_end = odd_carry_ends & even_bits;
uint64_t odd_ends = even_start_odd_end | odd_start_even_end;
uint64_t quote_bits = cmp_mask_against_input_mini(input_lo, input_hi,
_mm256_set1_epi8('"'));
quote_bits = quote_bits & ~odd_ends;
uint64_t quote_mask = _mm_cvtsi128_si64(_mm_clmulepi64_si128(
_mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFF), 0));
quote_mask ^= prev_iter_inside_quote;
prev_iter_inside_quote = static_cast<uint64_t>(static_cast<int64_t>(quote_mask) >> 63);// might be undefined behavior, should be fully defined in C++20, ok according to John Regher from Utah University
const __m256i low_nibble_mask = _mm256_setr_epi8(
// 0 9 a b c d
16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0, 16, 0, 0, 0, 0, 0,
0, 0, 0, 8, 12, 1, 2, 9, 0, 0);
const __m256i high_nibble_mask = _mm256_setr_epi8(
// 0 2 3 5 7
8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0, 8, 0, 18, 4, 0, 1, 0,
1, 0, 0, 0, 3, 2, 1, 0, 0);
__m256i whitespace_shufti_mask = _mm256_set1_epi8(0x18);
__m256i v_lo = _mm256_and_si256(
_mm256_shuffle_epi8(low_nibble_mask, input_lo),
_mm256_shuffle_epi8(high_nibble_mask,
_mm256_and_si256(_mm256_srli_epi32(input_lo, 4),
_mm256_set1_epi8(0x7f))));
__m256i v_hi = _mm256_and_si256(
_mm256_shuffle_epi8(low_nibble_mask, input_hi),
_mm256_shuffle_epi8(high_nibble_mask,
_mm256_and_si256(_mm256_srli_epi32(input_hi, 4),
_mm256_set1_epi8(0x7f))));
__m256i tmp_ws_lo = _mm256_cmpeq_epi8(
_mm256_and_si256(v_lo, whitespace_shufti_mask), _mm256_set1_epi8(0));
__m256i tmp_ws_hi = _mm256_cmpeq_epi8(
_mm256_and_si256(v_hi, whitespace_shufti_mask), _mm256_set1_epi8(0));
uint64_t ws_res_0 = static_cast<uint32_t>(_mm256_movemask_epi8(tmp_ws_lo));
uint64_t ws_res_1 = _mm256_movemask_epi8(tmp_ws_hi);
uint64_t whitespace = ~(ws_res_0 | (ws_res_1 << 32));
whitespace &= ~quote_mask;
int mask1 = whitespace & 0xFFFF;
int mask2 = (whitespace >> 16) & 0xFFFF;
int mask3 = (whitespace >> 32) & 0xFFFF;
int mask4 = (whitespace >> 48) & 0xFFFF;
int pop1 = hamming((~whitespace) & 0xFFFF);
int pop2 = hamming((~whitespace) & UINT64_C(0xFFFFFFFF));
int pop3 = hamming((~whitespace) & UINT64_C(0xFFFFFFFFFFFF));
int pop4 = hamming((~whitespace));
__m256i vmask1 =
_mm256_loadu2_m128i(reinterpret_cast<const __m128i *>(mask128_epi8) + (mask2 & 0x7FFF),
reinterpret_cast<const __m128i *>(mask128_epi8) + (mask1 & 0x7FFF));
__m256i vmask2 =
_mm256_loadu2_m128i(reinterpret_cast<const __m128i *>(mask128_epi8) + (mask4 & 0x7FFF),
reinterpret_cast<const __m128i *>(mask128_epi8) + (mask3 & 0x7FFF));
__m256i result1 = _mm256_shuffle_epi8(input_lo, vmask1);
__m256i result2 = _mm256_shuffle_epi8(input_hi, vmask2);
_mm256_storeu2_m128i(reinterpret_cast<__m128i *>(out + pop1), reinterpret_cast<__m128i *>(out), result1);
_mm256_storeu2_m128i(reinterpret_cast<__m128i *>(out + pop3), reinterpret_cast<__m128i *>(out + pop2),
result2);
out += pop4;
}
}
// we finish off the job... copying and pasting the code is not ideal here,
// but it gets the job done.
if (idx < len) {
uint8_t buffer[64];
memset(buffer, 0, 64);
memcpy(buffer, buf + idx, len - idx);
__m256i input_lo = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buffer));
__m256i input_hi = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buffer + 32));
uint64_t bs_bits =
cmp_mask_against_input_mini(input_lo, input_hi, _mm256_set1_epi8('\\'));
uint64_t start_edges = bs_bits & ~(bs_bits << 1);
uint64_t even_start_mask = even_bits ^ prev_iter_ends_odd_backslash;
uint64_t even_starts = start_edges & even_start_mask;
uint64_t odd_starts = start_edges & ~even_start_mask;
uint64_t even_carries = bs_bits + even_starts;
uint64_t odd_carries;
//bool iter_ends_odd_backslash =
add_overflow( bs_bits, odd_starts, &odd_carries);
odd_carries |= prev_iter_ends_odd_backslash;
//prev_iter_ends_odd_backslash = iter_ends_odd_backslash ? 0x1ULL : 0x0ULL; // we never use it
uint64_t even_carry_ends = even_carries & ~bs_bits;
uint64_t odd_carry_ends = odd_carries & ~bs_bits;
uint64_t even_start_odd_end = even_carry_ends & odd_bits;
uint64_t odd_start_even_end = odd_carry_ends & even_bits;
uint64_t odd_ends = even_start_odd_end | odd_start_even_end;
uint64_t quote_bits =
cmp_mask_against_input_mini(input_lo, input_hi, _mm256_set1_epi8('"'));
quote_bits = quote_bits & ~odd_ends;
uint64_t quote_mask = _mm_cvtsi128_si64(_mm_clmulepi64_si128(
_mm_set_epi64x(0ULL, quote_bits), _mm_set1_epi8(0xFF), 0));
quote_mask ^= prev_iter_inside_quote;
// prev_iter_inside_quote = (uint64_t)((int64_t)quote_mask >> 63);// we don't need this anymore
__m256i mask_20 = _mm256_set1_epi8(0x20); // c==32
__m256i mask_70 =
_mm256_set1_epi8(0x70); // adding 0x70 does not check low 4-bits
// but moves any value >= 16 above 128
__m256i lut_cntrl = _mm256_setr_epi8(
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00);
__m256i tmp_ws_lo = _mm256_or_si256(
_mm256_cmpeq_epi8(mask_20, input_lo),
_mm256_shuffle_epi8(lut_cntrl, _mm256_adds_epu8(mask_70, input_lo)));
__m256i tmp_ws_hi = _mm256_or_si256(
_mm256_cmpeq_epi8(mask_20, input_hi),
_mm256_shuffle_epi8(lut_cntrl, _mm256_adds_epu8(mask_70, input_hi)));
uint64_t ws_res_0 = static_cast<uint32_t>(_mm256_movemask_epi8(tmp_ws_lo));
uint64_t ws_res_1 = _mm256_movemask_epi8(tmp_ws_hi);
uint64_t whitespace = (ws_res_0 | (ws_res_1 << 32));
whitespace &= ~quote_mask;
if (len - idx < 64) {
whitespace |= UINT64_C(0xFFFFFFFFFFFFFFFF) << (len - idx);
}
int mask1 = whitespace & 0xFFFF;
int mask2 = (whitespace >> 16) & 0xFFFF;
int mask3 = (whitespace >> 32) & 0xFFFF;
int mask4 = (whitespace >> 48) & 0xFFFF;
int pop1 = hamming((~whitespace) & 0xFFFF);
int pop2 = hamming((~whitespace) & UINT64_C(0xFFFFFFFF));
int pop3 = hamming((~whitespace) & UINT64_C(0xFFFFFFFFFFFF));
int pop4 = hamming((~whitespace));
__m256i vmask1 =
_mm256_loadu2_m128i(reinterpret_cast<const __m128i *>(mask128_epi8) + (mask2 & 0x7FFF),
reinterpret_cast<const __m128i *>(mask128_epi8) + (mask1 & 0x7FFF));
__m256i vmask2 =
_mm256_loadu2_m128i(reinterpret_cast<const __m128i *>(mask128_epi8) + (mask4 & 0x7FFF),
reinterpret_cast<const __m128i *>(mask128_epi8) + (mask3 & 0x7FFF));
__m256i result1 = _mm256_shuffle_epi8(input_lo, vmask1);
__m256i result2 = _mm256_shuffle_epi8(input_hi, vmask2);
_mm256_storeu2_m128i(reinterpret_cast<__m128i *>(buffer + pop1), reinterpret_cast<__m128i *>(buffer),
result1);
_mm256_storeu2_m128i(reinterpret_cast<__m128i *>(buffer + pop3), reinterpret_cast<__m128i *>(buffer + pop2),
result2);
memcpy(out, buffer, pop4);
out += pop4;
}
*out = '\0';// NULL termination
return out - initout;
}
}
#endif
/* end file src/jsonminifier.cpp */
/* begin file src/jsonparser.cpp */
#ifdef _MSC_VER
#include <windows.h>
#include <sysinfoapi.h>
#else
#include <unistd.h>
#endif
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 & supports) 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) {
architecture best_implementation = find_best_supported_implementation();
// Selecting the best implementation
switch (best_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
#ifdef IS_ARM64
case architecture::arm64:
json_parse_ptr = &json_parse_implementation<architecture::arm64>;
break;
#endif
default :
std::cerr << "The processor is not supported by simdjson." << std::endl;
return simdjson::UNEXPECTED_ERROR;
}
return json_parse_ptr(buf, len, pj, reallocifneeded);
}
json_parse_functype *json_parse_ptr = &json_parse_dispatch;
WARN_UNUSED
ParsedJson build_parsed_json(const uint8_t *buf, size_t len, bool reallocifneeded) {
ParsedJson pj;
bool ok = pj.allocateCapacity(len);
if(ok) {
json_parse(buf, len, pj, reallocifneeded);
} else {
std::cerr << "failure during memory allocation " << std::endl;
}
return pj;
}
}
/* end file src/jsonparser.cpp */
/* begin file src/stage1_find_marks.cpp */
#ifdef IS_X86_64
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);
}
}
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);
}
}
UNTARGET_REGION
#endif
#ifdef IS_ARM64
namespace simdjson {
template<>
inline int find_structural_bits<architecture::arm64>(const uint8_t *buf, size_t len, ParsedJson &pj) {
FIND_STRUCTURAL_BITS(architecture::arm64, buf, len, pj);
}
}
#endif
/* end file src/stage1_find_marks.cpp */
/* begin file src/stage2_build_tape.cpp */
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
/* end file src/stage2_build_tape.cpp */
/* begin file src/parsedjson.cpp */
namespace simdjson {
ParsedJson::ParsedJson() :
structural_indexes(nullptr), tape(nullptr), containing_scope_offset(nullptr),
ret_address(nullptr), string_buf(nullptr), current_string_buf_loc(nullptr) {}
ParsedJson::~ParsedJson() {
deallocate();
}
ParsedJson::ParsedJson(ParsedJson && p)
: bytecapacity(p.bytecapacity),
depthcapacity(p.depthcapacity),
tapecapacity(p.tapecapacity),
stringcapacity(p.stringcapacity),
current_loc(p.current_loc),
n_structural_indexes(p.n_structural_indexes),
structural_indexes(p.structural_indexes),
tape(p.tape),
containing_scope_offset(p.containing_scope_offset),
ret_address(p.ret_address),
string_buf(p.string_buf),
current_string_buf_loc(p.current_string_buf_loc),
isvalid(p.isvalid) {
p.structural_indexes=nullptr;
p.tape=nullptr;
p.containing_scope_offset=nullptr;
p.ret_address=nullptr;
p.string_buf=nullptr;
p.current_string_buf_loc=nullptr;
}
WARN_UNUSED
bool ParsedJson::allocateCapacity(size_t len, size_t maxdepth) {
if ((maxdepth == 0) || (len == 0)) {
std::cerr << "capacities must be non-zero " << std::endl;
return false;
}
if(len > SIMDJSON_MAXSIZE_BYTES) {
return false;
}
if ((len <= bytecapacity) && (depthcapacity < maxdepth)) {
return true;
}
deallocate();
isvalid = false;
bytecapacity = 0; // will only set it to len after allocations are a success
n_structural_indexes = 0;
uint32_t max_structures = ROUNDUP_N(len, 64) + 2 + 7;
structural_indexes = new (std::nothrow) uint32_t[max_structures];
// a pathological input like "[[[[..." would generate len tape elements, so need a capacity of len + 1
size_t localtapecapacity = ROUNDUP_N(len + 1, 64);
// a document with only zero-length strings... could have len/3 string
// and we would need len/3 * 5 bytes on the string buffer
size_t localstringcapacity = ROUNDUP_N(5 * len / 3 + 32, 64);
string_buf = new (std::nothrow) uint8_t[localstringcapacity];
tape = new (std::nothrow) uint64_t[localtapecapacity];
containing_scope_offset = new (std::nothrow) uint32_t[maxdepth];
#ifdef SIMDJSON_USE_COMPUTED_GOTO
ret_address = new (std::nothrow) void *[maxdepth];
#else
ret_address = new (std::nothrow) char[maxdepth];
#endif
if ((string_buf == nullptr) || (tape == nullptr) ||
(containing_scope_offset == nullptr) || (ret_address == nullptr) || (structural_indexes == nullptr)) {
std::cerr << "Could not allocate memory" << std::endl;
delete[] ret_address;
delete[] containing_scope_offset;
delete[] tape;
delete[] string_buf;
delete[] structural_indexes;
return false;
}
/*
// We do not need to initialize this content for parsing, though we could
// need to initialize it for safety.
memset(string_buf, 0 , localstringcapacity);
memset(structural_indexes, 0, max_structures * sizeof(uint32_t));
memset(tape, 0, localtapecapacity * sizeof(uint64_t));
*/
bytecapacity = len;
depthcapacity = maxdepth;
tapecapacity = localtapecapacity;
stringcapacity = localstringcapacity;
return true;
}
bool ParsedJson::isValid() const {
return isvalid;
}
int ParsedJson::getErrorCode() const {
return errorcode;
}
std::string ParsedJson::getErrorMsg() const {
return errorMsg(errorcode);
}
void ParsedJson::deallocate() {
bytecapacity = 0;
depthcapacity = 0;
tapecapacity = 0;
stringcapacity = 0;
delete[] ret_address;
delete[] containing_scope_offset;
delete[] tape;
delete[] string_buf;
delete[] structural_indexes;
isvalid = false;
}
void ParsedJson::init() {
current_string_buf_loc = string_buf;
current_loc = 0;
isvalid = false;
}
WARN_UNUSED
bool ParsedJson::printjson(std::ostream &os) {
if(!isvalid) {
return false;
}
uint32_t string_length;
size_t tapeidx = 0;
uint64_t tape_val = tape[tapeidx];
uint8_t type = (tape_val >> 56);
size_t howmany = 0;
if (type == 'r') {
howmany = tape_val & JSONVALUEMASK;
} else {
fprintf(stderr, "Error: no starting root node?");
return false;
}
if (howmany > tapecapacity) {
fprintf(stderr,
"We may be exceeding the tape capacity. Is this a valid document?\n");
return false;
}
tapeidx++;
bool *inobject = new bool[depthcapacity];
auto *inobjectidx = new size_t[depthcapacity];
int depth = 1; // only root at level 0
inobjectidx[depth] = 0;
inobject[depth] = false;
for (; tapeidx < howmany; tapeidx++) {
tape_val = tape[tapeidx];
uint64_t payload = tape_val & JSONVALUEMASK;
type = (tape_val >> 56);
if (!inobject[depth]) {
if ((inobjectidx[depth] > 0) && (type != ']')) {
os << ",";
}
inobjectidx[depth]++;
} else { // if (inobject) {
if ((inobjectidx[depth] > 0) && ((inobjectidx[depth] & 1) == 0) &&
(type != '}')) {
os << ",";
}
if (((inobjectidx[depth] & 1) == 1)) {
os << ":";
}
inobjectidx[depth]++;
}
switch (type) {
case '"': // we have a string
os << '"';
memcpy(&string_length,string_buf + payload, sizeof(uint32_t));
print_with_escapes((const unsigned char *)(string_buf + payload + sizeof(uint32_t)), string_length);
os << '"';
break;
case 'l': // we have a long int
if (tapeidx + 1 >= howmany) {
delete[] inobject;
delete[] inobjectidx;
return false;
}
os << static_cast<int64_t>(tape[++tapeidx]);
break;
case 'd': // we have a double
if (tapeidx + 1 >= howmany){
delete[] inobject;
delete[] inobjectidx;
return false;
}
double answer;
memcpy(&answer, &tape[++tapeidx], sizeof(answer));
os << answer;
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
os << '{';
depth++;
inobject[depth] = true;
inobjectidx[depth] = 0;
break;
case '}': // we end an object
depth--;
os << '}';
break;
case '[': // we start an array
os << '[';
depth++;
inobject[depth] = false;
inobjectidx[depth] = 0;
break;
case ']': // we end an array
depth--;
os << ']';
break;
case 'r': // we start and end with the root node
fprintf(stderr, "should we be hitting the root node?\n");
delete[] inobject;
delete[] inobjectidx;
return false;
default:
fprintf(stderr, "bug %c\n", type);
delete[] inobject;
delete[] inobjectidx;
return false;
}
}
delete[] inobject;
delete[] inobjectidx;
return true;
}
WARN_UNUSED
bool ParsedJson::dump_raw_tape(std::ostream &os) {
if(!isvalid) {
return false;
}
uint32_t string_length;
size_t tapeidx = 0;
uint64_t tape_val = tape[tapeidx];
uint8_t type = (tape_val >> 56);
os << tapeidx << " : " << type;
tapeidx++;
size_t howmany = 0;
if (type == 'r') {
howmany = tape_val & JSONVALUEMASK;
} else {
fprintf(stderr, "Error: no starting root node?");
return false;
}
os << "\t// pointing to " << howmany <<" (right after last node)\n";
uint64_t payload;
for (; tapeidx < howmany; tapeidx++) {
os << tapeidx << " : ";
tape_val = tape[tapeidx];
payload = tape_val & JSONVALUEMASK;
type = (tape_val >> 56);
switch (type) {
case '"': // we have a string
os << "string \"";
memcpy(&string_length,string_buf + payload, sizeof(uint32_t));
print_with_escapes((const unsigned char *)(string_buf + payload + sizeof(uint32_t)), string_length);
os << '"';
os << '\n';
break;
case 'l': // we have a long int
if (tapeidx + 1 >= howmany) {
return false;
}
os << "integer " << static_cast<int64_t>(tape[++tapeidx]) << "\n";
break;
case 'd': // we have a double
os << "float ";
if (tapeidx + 1 >= howmany) {
return false;
}
double answer;
memcpy(&answer, &tape[++tapeidx], 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 " << payload << " (first node after the scope) \n";
break;
case '}': // we end an object
os << "}\t// pointing to previous tape location " << payload << " (start of the scope) \n";
break;
case '[': // we start an array
os << "[\t// pointing to next tape location " << payload << " (first node after the scope) \n";
break;
case ']': // we end an array
os << "]\t// pointing to previous tape location " << payload << " (start of the scope) \n";
break;
case 'r': // we start and end with the root node
printf("end of root\n");
return false;
default:
return false;
}
}
tape_val = tape[tapeidx];
payload = tape_val & JSONVALUEMASK;
type = (tape_val >> 56);
os << tapeidx << " : "<< type <<"\t// pointing to " << payload <<" (start root)\n";
return true;
}
}
/* end file src/parsedjson.cpp */
/* begin file src/parsedjsoniterator.cpp */
#include <iterator>
namespace simdjson {
ParsedJson::iterator::iterator(ParsedJson &pj_) : pj(pj_), depth(0), location(0), tape_length(0), depthindex(nullptr) {
if(!pj.isValid()) {
throw InvalidJSON();
}
depthindex = new scopeindex_t[pj.depthcapacity];
// memory allocation would throw
//if(depthindex == nullptr) {
// return;
//}
depthindex[0].start_of_scope = location;
current_val = pj.tape[location++];
current_type = (current_val >> 56);
depthindex[0].scope_type = current_type;
if (current_type == 'r') {
tape_length = current_val & JSONVALUEMASK;
if(location < tape_length) {
current_val = pj.tape[location];
current_type = (current_val >> 56);
depth++;
depthindex[depth].start_of_scope = location;
depthindex[depth].scope_type = current_type;
}
} else {
// should never happen
throw InvalidJSON();
}
}
ParsedJson::iterator::~iterator() {
delete[] depthindex;
}
ParsedJson::iterator::iterator(const iterator &o):
pj(o.pj), depth(o.depth), location(o.location),
tape_length(0), current_type(o.current_type),
current_val(o.current_val), depthindex(nullptr) {
depthindex = new scopeindex_t[pj.depthcapacity];
// allocation might throw
memcpy(depthindex, o.depthindex, pj.depthcapacity * sizeof(depthindex[0]));
tape_length = o.tape_length;
}
ParsedJson::iterator::iterator(iterator &&o):
pj(o.pj), depth(o.depth), location(o.location),
tape_length(o.tape_length), current_type(o.current_type),
current_val(o.current_val), depthindex(o.depthindex) {
o.depthindex = nullptr;// we take ownership
}
bool ParsedJson::iterator::print(std::ostream &os, bool escape_strings) const {
if(!isOk()) {
return false;
}
switch (current_type) {
case '"': // we have a string
os << '"';
if(escape_strings) {
print_with_escapes(get_string(), os, 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<char>(os));
}
os << '"';
break;
case 'l': // we have a long int
os << get_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 << static_cast<char>(current_type);
break;
default:
return false;
}
return true;
}
}
/* end file src/parsedjsoniterator.cpp */