/* 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 namespace simdjson { const std::map 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 #include 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 #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 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(_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(buf + idx + 0)); __m256i input_hi = _mm256_loadu_si256(reinterpret_cast(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(static_cast(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(_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(mask128_epi8) + (mask2 & 0x7FFF), reinterpret_cast(mask128_epi8) + (mask1 & 0x7FFF)); __m256i vmask2 = _mm256_loadu2_m128i(reinterpret_cast(mask128_epi8) + (mask4 & 0x7FFF), reinterpret_cast(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(buffer)); __m256i input_hi = _mm256_loadu_si256(reinterpret_cast(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(_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(mask128_epi8) + (mask2 & 0x7FFF), reinterpret_cast(mask128_epi8) + (mask1 & 0x7FFF)); __m256i vmask2 = _mm256_loadu2_m128i(reinterpret_cast(mask128_epi8) + (mask4 & 0x7FFF), reinterpret_cast(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 #include #else #include #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; break; case architecture::westmere: json_parse_ptr = &json_parse_implementation; break; #endif #ifdef IS_ARM64 case architecture::arm64: json_parse_ptr = &json_parse_implementation; 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(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(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(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(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(malloc(len + SIMDJSON_PADDING)); \ if(copy == nullptr) { \ goto fail; \ } \ memcpy(copy, buf, len); \ copy[len] = ' '; \ if (!is_valid_true_atom(reinterpret_cast(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(malloc(len + SIMDJSON_PADDING)); \ if(copy == nullptr) { \ goto fail; \ } \ memcpy(copy, buf, len); \ copy[len] = ' '; \ if (!is_valid_false_atom(reinterpret_cast(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(malloc(len + SIMDJSON_PADDING)); \ if(copy == nullptr) { \ goto fail; \ } \ memcpy(copy, buf, len); \ copy[len] = ' '; \ if (!is_valid_null_atom(reinterpret_cast(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(malloc(len + SIMDJSON_PADDING)); \ if(copy == nullptr) { \ goto fail; \ } \ memcpy(copy, buf, len); \ copy[len] = ' '; \ if (!parse_number(reinterpret_cast(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(malloc(len + SIMDJSON_PADDING)); \ if(copy == nullptr) { \ goto fail; \ } \ memcpy(copy, buf, len); \ copy[len] = ' '; \ if (!parse_number(reinterpret_cast(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(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(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(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(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(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(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(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(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(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 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(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(current_type); break; default: return false; } return true; } } /* end file src/parsedjsoniterator.cpp */