Use struct for UTF-8 checks, remove templating

- Removes templating from simd_input, utf8_checker, and parse_string
- Make drone gcc run a lot faster
- Make drone clang run a little faster (NOTE:
https://hub.docker.com/r/silkeh/clang helps even more, but I wasn't sure
whether we wanted to trust that)
- Make drone arm run in parallel to get results quicker
This commit is contained in:
John Keiser
2019-10-06 12:39:55 -07:00
parent 81f9aac13f
commit 69caa477fb
30 changed files with 2467 additions and 2320 deletions
+107 -58
View File
@@ -1,44 +1,102 @@
kind: pipeline
name: x64
name: x64-quicktests
platform:
os: linux
arch: amd64
steps:
- name: test
- name: quicktests
image: gcc:8
commands:
- make
- make quiettest
- make amalgamate
commands: [ make quicktests ]
---
kind: pipeline
name: x64-checkperf
platform:
os: linux
arch: amd64
steps:
- name: checkperf
image: gcc:8
environment:
CHECKPERF_REPOSITORY: https://github.com/lemire/simdjson
commands:
- make checkperf
commands: [ make checkperf ]
---
kind: pipeline
name: arm64
name: x64-build
platform:
os: linux
arch: amd64
steps:
- name: build
image: gcc:8
commands: [ make, make amalgamate ]
---
kind: pipeline
name: x64-slowtests
platform:
os: linux
arch: amd64
steps:
- name: slowtests
image: gcc:8
commands: [ make slowtests ]
---
kind: pipeline
name: arm64-quicktests
platform:
os: linux
arch: arm64
steps:
- name: test
- name: quicktests
image: gcc:8
commands:
- make
- make quiettest
- make amalgamate
commands: [ make quicktests ]
---
kind: pipeline
name: arm64-checkperf
platform:
os: linux
arch: arm64
steps:
- name: checkperf
image: gcc:8
environment:
CHECKPERF_REPOSITORY: https://github.com/lemire/simdjson
commands:
- make checkperf
commands: [ make checkperf ]
---
kind: pipeline
name: arm64-build
platform:
os: linux
arch: arm64
steps:
- name: build
image: gcc:8
commands: [ make, make amalgamate ]
---
kind: pipeline
name: arm64-slowtests
platform:
os: linux
arch: arm64
steps:
- name: slowtests
image: gcc:8
commands: [ make slowtests ]
---
kind: pipeline
name: stylecheck
@@ -69,8 +127,8 @@ steps:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y clang make cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_BUILD_STATIC=OFF $CMAKE_FLAGS ..
@@ -91,8 +149,8 @@ steps:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y clang make cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_BUILD_STATIC=ON $CMAKE_FLAGS ..
@@ -108,13 +166,13 @@ platform:
steps:
- name: Build and Test
image: ubuntu:18.04
image: gcc:7
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_BUILD_STATIC=ON $CMAKE_FLAGS ..
@@ -130,13 +188,13 @@ platform:
steps:
- name: Build and Test
image: ubuntu:18.04
image: gcc:7
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_BUILD_STATIC=OFF $CMAKE_FLAGS ..
@@ -157,8 +215,8 @@ steps:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y clang make cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_BUILD_STATIC=OFF $CMAKE_FLAGS ..
@@ -174,22 +232,13 @@ platform:
steps:
- name: Build and Test
image: ubuntu:18.04
image: gcc:7
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- $CC --version
- mkdir build && cd build
- cmake $CMAKE_FLAGS ..
- make -j
- ctest --output-on-failure
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_BUILD_STATIC=OFF $CMAKE_FLAGS ..
@@ -210,8 +259,8 @@ steps:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y clang make cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_BUILD_STATIC=ON $CMAKE_FLAGS ..
@@ -227,13 +276,13 @@ platform:
steps:
- name: Build and Test
image: ubuntu:18.04
image: gcc:7
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_BUILD_STATIC=ON $CMAKE_FLAGS ..
@@ -255,8 +304,8 @@ steps:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y clang make cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_SANITIZE=ON $CMAKE_FLAGS ..
@@ -272,13 +321,13 @@ platform:
steps:
- name: Build and Test
image: ubuntu:18.04
image: gcc:7
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_SANITIZE=ON $CMAKE_FLAGS ..
@@ -317,13 +366,13 @@ platform:
steps:
- name: Build and Test
image: ubuntu:18.04
image: gcc:7
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_SANITIZE=ON $CMAKE_FLAGS ..
@@ -345,8 +394,8 @@ steps:
CC: clang
CXX: clang++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y clang make cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_SANITIZE=ON -DSIMDJSON_DISABLE_AVX=ON $CMAKE_FLAGS ..
@@ -362,13 +411,13 @@ platform:
steps:
- name: Build and Test
image: ubuntu:18.04
image: gcc:7
environment:
CC: gcc
CXX: g++
commands:
- apt-get update -y
- apt-get install -y make $CC g++ cmake
- apt-get update -qq
- apt-get install -y cmake
- $CC --version
- mkdir build && cd build
- cmake -DSIMDJSON_SANITIZE=ON -DSIMDJSON_DISABLE_AVX=ON $CMAKE_FLAGS ..
+24 -12
View File
@@ -64,7 +64,7 @@ COMPARISONEXECUTABLES=minifiercompetition parsingcompetition parseandstatcompeti
SUPPLEMENTARYEXECUTABLES=parse_noutf8validation parse_nonumberparsing parse_nostringparsing
# Load headers and sources
LIBHEADERS=src/simd_input.h src/simdutf8check.h src/stringparsing.h src/arm64/architecture.h src/arm64/simd_input.h src/arm64/simdutf8check.h src/arm64/stage1_find_marks.h src/arm64/stage2_build_tape.h src/arm64/stringparsing.h src/generic/stage1_find_marks_flatten.h src/generic/stage1_find_marks.h src/generic/stage2_build_tape.h src/generic/stringparsing.h src/haswell/architecture.h src/haswell/simd_input.h src/haswell/simdutf8check.h src/haswell/stage1_find_marks.h src/haswell/stage2_build_tape.h src/haswell/stringparsing.h src/westmere/architecture.h src/westmere/simd_input.h src/westmere/simdutf8check.h src/westmere/stage1_find_marks.h src/westmere/stage2_build_tape.h src/westmere/stringparsing.h
LIBHEADERS=src/arm64/simd_input.h src/arm64/simdutf8check.h src/arm64/stage1_find_marks.h src/arm64/stage2_build_tape.h src/arm64/stringparsing.h src/generic/stage1_find_marks_flatten.h src/generic/stage1_find_marks.h src/generic/stage2_build_tape.h src/generic/stringparsing.h src/haswell/simd_input.h src/haswell/simdutf8check.h src/haswell/stage1_find_marks.h src/haswell/stage2_build_tape.h src/haswell/stringparsing.h src/westmere/simd_input.h src/westmere/simdutf8check.h src/westmere/stage1_find_marks.h src/westmere/stage2_build_tape.h src/westmere/stringparsing.h
PUBHEADERS=include/simdjson/common_defs.h include/simdjson/isadetection.h include/simdjson/jsoncharutils.h include/simdjson/jsonformatutils.h include/simdjson/jsonioutil.h include/simdjson/jsonminifier.h include/simdjson/jsonparser.h include/simdjson/numberparsing.h include/simdjson/padded_string.h include/simdjson/parsedjson.h include/simdjson/parsedjsoniterator.h include/simdjson/portability.h include/simdjson/simdjson.h include/simdjson/simdjson_version.h include/simdjson/simdprune_tables.h include/simdjson/stage1_find_marks.h include/simdjson/stage2_build_tape.h
HEADERS=$(PUBHEADERS) $(LIBHEADERS)
@@ -96,26 +96,38 @@ benchmark:
bash ./scripts/parser.sh
bash ./scripts/parseandstat.sh
test: jsoncheck integer_tests numberparsingcheck stringparsingcheck basictests allparserscheckfile minify json2json pointercheck
run_basictests: basictests
./basictests
run_numberparsingcheck: numberparsingcheck
./numberparsingcheck
run_integer_tests: integer_tests
./integer_tests
run_stringparsingcheck: stringparsingcheck
./stringparsingcheck
run_jsoncheck: jsoncheck
./jsoncheck
run_pointercheck: pointercheck
./pointercheck
./scripts/testjson2json.sh
run_issue150_sh: allparserscheckfile
./scripts/issue150.sh
run_testjson2json_sh: minify json2json
./scripts/testjson2json.sh
test: run_basictests run_numberparsingcheck run_integer_tests run_stringparsingcheck run_jsoncheck run_pointercheck run_testjson2json_sh run_issue150_sh
@echo "It looks like the code is good!"
quiettest: jsoncheck integer_tests numberparsingcheck stringparsingcheck basictests allparserscheckfile minify json2json pointercheck
./basictests
./numberparsingcheck
./integer_tests
./stringparsingcheck
./jsoncheck
./pointercheck
./scripts/testjson2json.sh
./scripts/issue150.sh
quiettest: run_basictests run_numberparsingcheck run_integer_tests run_stringparsingcheck run_jsoncheck run_pointercheck run_testjson2json_sh run_issue150_sh
quicktests: run_basictests run_numberparsingcheck run_integer_tests run_stringparsingcheck run_jsoncheck run_pointercheck
slowtests: run_testjson2json_sh run_issue150_sh
amalgamate:
./amalgamation.sh
-6
View File
@@ -17,14 +17,9 @@ $SCRIPTPATH/src/simdjson.cpp
$SCRIPTPATH/src/jsonioutil.cpp
$SCRIPTPATH/src/jsonminifier.cpp
$SCRIPTPATH/src/jsonparser.cpp
$SCRIPTPATH/src/simd_input.h
$SCRIPTPATH/src/arm64/architecture.h
$SCRIPTPATH/src/haswell/architecture.h
$SCRIPTPATH/src/westmere/architecture.h
$SCRIPTPATH/src/arm64/simd_input.h
$SCRIPTPATH/src/haswell/simd_input.h
$SCRIPTPATH/src/westmere/simd_input.h
$SCRIPTPATH/src/simdutf8check.h
$SCRIPTPATH/src/arm64/simdutf8check.h
$SCRIPTPATH/src/haswell/simdutf8check.h
$SCRIPTPATH/src/westmere/simdutf8check.h
@@ -32,7 +27,6 @@ $SCRIPTPATH/src/arm64/stage1_find_marks.h
$SCRIPTPATH/src/haswell/stage1_find_marks.h
$SCRIPTPATH/src/westmere/stage1_find_marks.h
$SCRIPTPATH/src/stage1_find_marks.cpp
$SCRIPTPATH/src/stringparsing.h
$SCRIPTPATH/src/arm64/stringparsing.h
$SCRIPTPATH/src/haswell/stringparsing.h
$SCRIPTPATH/src/westmere/stringparsing.h
+1 -1
View File
@@ -1,4 +1,4 @@
/* auto-generated on Fri Aug 23 11:02:39 DST 2019. Do not edit! */
/* auto-generated on Sun Oct 6 13:23:03 DST 2019. Do not edit! */
#include <iostream>
#include "simdjson.h"
+1634 -1447
View File
File diff suppressed because it is too large Load Diff
+132 -56
View File
@@ -1,4 +1,4 @@
/* auto-generated on Fri Aug 23 11:02:39 DST 2019. Do not edit! */
/* auto-generated on Sun Oct 6 13:23:03 DST 2019. Do not edit! */
/* begin file include/simdjson/simdjson_version.h */
// /include/simdjson/simdjson_version.h automatically generated by release.py,
// do not change by hand
@@ -419,6 +419,17 @@ const std::string &error_message(const int);
#define SIMDJSON_PADDING 32
#endif
#if defined(__GNUC__)
// Marks a block with a name so that MCA analysis can see it.
#define BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
#define END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
#define DEBUG_BLOCK(name, block) BEGIN_DEBUG_BLOCK(name); block; END_DEBUG_BLOCK(name);
#else
#define BEGIN_DEBUG_BLOCK(name)
#define END_DEBUG_BLOCK(name)
#define DEBUG_BLOCK(name, block)
#endif
#ifndef _MSC_VER
// Implemented using Labels as Values which works in GCC and CLANG (and maybe
// also in Intel's compiler), but won't work in MSVC.
@@ -36231,9 +36242,9 @@ public:
// return false if the tape is likely wrong (e.g., you did not parse a valid
// JSON).
WARN_UNUSED
bool print_json(std::ostream &os);
bool print_json(std::ostream &os) const;
WARN_UNUSED
bool dump_raw_tape(std::ostream &os);
bool dump_raw_tape(std::ostream &os) const;
// all nodes are stored on the tape using a 64-bit word.
//
@@ -36259,6 +36270,11 @@ public:
tape[current_loc++] = *(reinterpret_cast<uint64_t *>(&i));
}
really_inline void write_tape_u64(uint64_t i) {
write_tape(0, 'u');
tape[current_loc++] = i;
}
really_inline void write_tape_double(double d) {
write_tape(0, 'd');
static_assert(sizeof(d) == sizeof(tape[current_loc]), "mismatch size");
@@ -36266,7 +36282,7 @@ public:
// tape[current_loc++] = *((uint64_t *)&d);
}
really_inline uint32_t get_current_loc() { return current_loc; }
really_inline uint32_t get_current_loc() const { return current_loc; }
really_inline void annotate_previous_loc(uint32_t saved_loc, uint64_t val) {
tape[saved_loc] |= val;
@@ -36333,12 +36349,11 @@ inline void dumpbits32_always(uint32_t v, const std::string &msg) {
#include <cstring>
#include <iostream>
#include <limits>
#include <iterator>
#include <limits>
namespace simdjson {
template <size_t max_depth>
class ParsedJson::BasicIterator {
template <size_t max_depth> class ParsedJson::BasicIterator {
// might throw InvalidJSON if ParsedJson is invalid
public:
explicit BasicIterator(ParsedJson &pj_);
@@ -36379,6 +36394,14 @@ public:
return static_cast<int64_t>(pj->tape[location + 1]);
}
// get the value as uint64
inline uint64_t get_unsigned_integer() const {
if (location + 1 >= tape_length) {
return 0; // default value in case of error
}
return pj->tape[location + 1];
}
// get the string value at this node (NULL ended); valid only if we're at "
// note that tabs, and line endings are escaped in the returned value (see
// print_with_escapes) return value is valid UTF-8 It may contain NULL chars
@@ -36418,10 +36441,26 @@ public:
inline bool is_string() const { return get_type() == '"'; }
// Returns true if the current type of node is an signed integer.
// You can get its value with `get_integer()`.
inline bool is_integer() const { return get_type() == 'l'; }
// Returns true if the current type of node is an unsigned integer.
// You can get its value with `get_unsigned_integer()`.
//
// NOTE:
// Only a large value, which is out of range of a 64-bit signed integer, is
// represented internally as an unsigned node. On the other hand, a typical
// positive integer, such as 1, 42, or 1000000, is as a signed node.
// Be aware this function returns false for a signed node.
inline bool is_unsigned_integer() const { return get_type() == 'u'; }
inline bool is_double() const { return get_type() == 'd'; }
inline bool is_number() const {
return is_integer() || is_unsigned_integer() || is_double();
}
inline bool is_true() const { return get_type() == 't'; }
inline bool is_false() const { return get_type() == 'f'; }
@@ -36438,7 +36477,7 @@ public:
// (in case of repeated keys, this only finds the first one).
// We seek the key using C's strcmp so if your JSON strings contain
// NULL chars, this would trigger a false positive: if you expect that
// to be the case, take extra precautions.
// to be the case, take extra precautions.
// Furthermore, we do the comparison character-by-character
// without taking into account Unicode equivalence.
inline bool move_to_key(const char *key);
@@ -36513,6 +36552,9 @@ public:
// Thus, given [true, null, {"a":1}, [1,2]], we would visit ], }, null, true
// when starting at the end of the scope. At the object ({) or at the array
// ([), you can issue a "down" to visit their content.
// Performance warning: This function is implemented by starting again
// from the beginning of the scope and scanning forward. You should expect
// it to be relatively slow.
inline bool prev();
// Moves back to either the containing array or object (type { or [) from
@@ -36555,21 +36597,28 @@ private:
};
template <size_t max_depth>
WARN_UNUSED
bool ParsedJson::BasicIterator<max_depth>::is_ok() const { return location < tape_length; }
WARN_UNUSED bool ParsedJson::BasicIterator<max_depth>::is_ok() const {
return location < tape_length;
}
// useful for debuging purposes
template <size_t max_depth>
size_t ParsedJson::BasicIterator<max_depth>::get_tape_location() const { return location; }
size_t ParsedJson::BasicIterator<max_depth>::get_tape_location() const {
return location;
}
// useful for debuging purposes
template <size_t max_depth>
size_t ParsedJson::BasicIterator<max_depth>::get_tape_length() const { return tape_length; }
size_t ParsedJson::BasicIterator<max_depth>::get_tape_length() const {
return tape_length;
}
// returns the current depth (start at 1 with 0 reserved for the fictitious root
// node)
template <size_t max_depth>
size_t ParsedJson::BasicIterator<max_depth>::get_depth() const { return depth; }
size_t ParsedJson::BasicIterator<max_depth>::get_depth() const {
return depth;
}
// A scope is a series of nodes at the same depth, typically it is either an
// object ({) or an array ([). The root node has type 'r'.
@@ -36593,8 +36642,8 @@ bool ParsedJson::BasicIterator<max_depth>::move_forward() {
} else if ((current_type == ']') || (current_type == '}')) {
// Leaving a scope.
depth--;
} else if ((current_type == 'd') || (current_type == 'l')) {
// d and l types use 2 locations on the tape, not just one.
} else if (is_number()) {
// these types use 2 locations on the tape, not just one.
location += 1;
}
@@ -36630,7 +36679,8 @@ bool ParsedJson::BasicIterator<max_depth>::move_to_key(const char *key) {
}
template <size_t max_depth>
bool ParsedJson::BasicIterator<max_depth>::move_to_key(const char *key, uint32_t length) {
bool ParsedJson::BasicIterator<max_depth>::move_to_key(const char *key,
uint32_t length) {
if (down()) {
do {
assert(is_string());
@@ -36664,29 +36714,31 @@ bool ParsedJson::BasicIterator<max_depth>::move_to_index(uint32_t index) {
return false;
}
template <size_t max_depth>
bool ParsedJson::BasicIterator<max_depth>::prev() {
if (location - 1 < depth_index[depth].start_of_scope) {
return false;
template <size_t max_depth> bool ParsedJson::BasicIterator<max_depth>::prev() {
size_t target_location = location;
to_start_scope();
size_t npos = location;
if (target_location == npos) {
return false; // we were already at the start
}
location -= 1;
current_val = pj->tape[location];
current_type = (current_val >> 56);
if ((current_type == ']') || (current_type == '}')) {
// we need to jump
size_t new_location = (current_val & JSON_VALUE_MASK);
if (new_location < depth_index[depth].start_of_scope) {
return false; // shoud never happen
size_t oldnpos;
// we have that npos < target_location here
do {
oldnpos = npos;
if ((current_type == '[') || (current_type == '{')) {
// we need to jump
npos = (current_val & JSON_VALUE_MASK);
} else {
npos = npos + ((current_type == 'd' || current_type == 'l') ? 2 : 1);
}
location = new_location;
current_val = pj->tape[location];
current_type = (current_val >> 56);
}
} while (npos < target_location);
location = oldnpos;
current_val = pj->tape[location];
current_type = current_val >> 56;
return true;
}
template <size_t max_depth>
bool ParsedJson::BasicIterator<max_depth>::up() {
template <size_t max_depth> bool ParsedJson::BasicIterator<max_depth>::up() {
if (depth == 1) {
return false; // don't allow moving back to root
}
@@ -36699,8 +36751,7 @@ bool ParsedJson::BasicIterator<max_depth>::up() {
return true;
}
template <size_t max_depth>
bool ParsedJson::BasicIterator<max_depth>::down() {
template <size_t max_depth> bool ParsedJson::BasicIterator<max_depth>::down() {
if (location + 1 >= tape_length) {
return false;
}
@@ -36728,14 +36779,13 @@ void ParsedJson::BasicIterator<max_depth>::to_start_scope() {
current_type = (current_val >> 56);
}
template <size_t max_depth>
bool ParsedJson::BasicIterator<max_depth>::next() {
template <size_t max_depth> bool ParsedJson::BasicIterator<max_depth>::next() {
size_t npos;
if ((current_type == '[') || (current_type == '{')) {
// we need to jump
npos = (current_val & JSON_VALUE_MASK);
} else {
npos = location + ((current_type == 'd' || current_type == 'l') ? 2 : 1);
npos = location + (is_number() ? 2 : 1);
}
uint64_t next_val = pj->tape[npos];
uint8_t next_type = (next_val >> 56);
@@ -36777,14 +36827,17 @@ ParsedJson::BasicIterator<max_depth>::BasicIterator(ParsedJson &pj_)
}
template <size_t max_depth>
ParsedJson::BasicIterator<max_depth>::BasicIterator(const BasicIterator &o) noexcept
: pj(o.pj), depth(o.depth), location(o.location), tape_length(o.tape_length),
current_type(o.current_type), current_val(o.current_val) {
ParsedJson::BasicIterator<max_depth>::BasicIterator(
const BasicIterator &o) noexcept
: pj(o.pj), depth(o.depth), location(o.location),
tape_length(o.tape_length), current_type(o.current_type),
current_val(o.current_val) {
memcpy(depth_index, o.depth_index, (depth + 1) * sizeof(depth_index[0]));
}
template <size_t max_depth>
ParsedJson::BasicIterator<max_depth> &ParsedJson::BasicIterator<max_depth>::operator =(const BasicIterator &o) noexcept {
ParsedJson::BasicIterator<max_depth> &ParsedJson::BasicIterator<max_depth>::
operator=(const BasicIterator &o) noexcept {
pj = o.pj;
depth = o.depth;
location = o.location;
@@ -36796,7 +36849,8 @@ ParsedJson::BasicIterator<max_depth> &ParsedJson::BasicIterator<max_depth>::oper
}
template <size_t max_depth>
bool ParsedJson::BasicIterator<max_depth>::print(std::ostream &os, bool escape_strings) const {
bool ParsedJson::BasicIterator<max_depth>::print(std::ostream &os,
bool escape_strings) const {
if (!is_ok()) {
return false;
}
@@ -36816,6 +36870,9 @@ bool ParsedJson::BasicIterator<max_depth>::print(std::ostream &os, bool escape_s
case 'l': // we have a long int
os << get_integer();
break;
case 'u':
os << get_unsigned_integer();
break;
case 'd':
os << get_double();
break;
@@ -36841,7 +36898,8 @@ bool ParsedJson::BasicIterator<max_depth>::print(std::ostream &os, bool escape_s
}
template <size_t max_depth>
bool ParsedJson::BasicIterator<max_depth>::move_to(const char *pointer, uint32_t length) {
bool ParsedJson::BasicIterator<max_depth>::move_to(const char *pointer,
uint32_t length) {
char *new_pointer = nullptr;
if (pointer[0] == '#') {
// Converting fragment representation to string representation
@@ -37041,6 +37099,7 @@ int find_structural_bits(const char *buf, size_t len, simdjson::ParsedJson &pj)
#ifdef JSON_TEST_NUMBERS // for unit testing
void found_invalid_number(const uint8_t *buf);
void found_integer(int64_t result, const uint8_t *buf);
void found_unsigned_integer(uint64_t result, const uint8_t *buf);
void found_float(double result, const uint8_t *buf);
#endif
@@ -37401,27 +37460,44 @@ static never_inline bool parse_large_integer(const uint8_t *const buf,
}
if (negative) {
if (i > 0x8000000000000000) {
// overflows!
// overflows!
#ifdef JSON_TEST_NUMBERS // for unit testing
found_invalid_number(buf + offset);
#endif
return false; // overflow
} else if (i == 0x8000000000000000) {
// In two's complement, we cannot represent 0x8000000000000000
// as a positive signed integer, but the negative version is
// possible.
constexpr int64_t signed_answer = INT64_MIN;
pj.write_tape_s64(signed_answer);
#ifdef JSON_TEST_NUMBERS // for unit testing
found_integer(signed_answer, buf + offset);
#endif
} else {
// we can negate safely
int64_t signed_answer = -static_cast<int64_t>(i);
pj.write_tape_s64(signed_answer);
#ifdef JSON_TEST_NUMBERS // for unit testing
found_integer(signed_answer, buf + offset);
#endif
}
} else {
if (i >= 0x8000000000000000) {
// overflows!
// we have a positive integer, the contract is that
// we try to represent it as a signed integer and only
// fallback on unsigned integers if absolutely necessary.
if(i < 0x8000000000000000) {
#ifdef JSON_TEST_NUMBERS // for unit testing
found_invalid_number(buf + offset);
found_integer(i, buf + offset);
#endif
return false; // overflow
pj.write_tape_s64(i);
} else {
#ifdef JSON_TEST_NUMBERS // for unit testing
found_unsigned_integer(i, buf + offset);
#endif
pj.write_tape_u64(i);
}
}
int64_t signed_answer =
negative ? -static_cast<int64_t>(i) : static_cast<int64_t>(i);
pj.write_tape_s64(signed_answer);
#ifdef JSON_TEST_NUMBERS // for unit testing
found_integer(signed_answer, buf + offset);
#endif
return is_structural_or_whitespace(*p);
}
-6
View File
@@ -34,7 +34,6 @@ set(SIMDJSON_SRC
# Load headers and sources
set(SIMDJSON_SRC_HEADERS
arm64/architecture.h
arm64/simd_input.h
arm64/simdutf8check.h
arm64/stage1_find_marks.h
@@ -44,21 +43,16 @@ set(SIMDJSON_SRC_HEADERS
generic/stage1_find_marks.h
generic/stage2_build_tape.h
generic/stringparsing.h
haswell/architecture.h
haswell/simd_input.h
haswell/simdutf8check.h
haswell/stage1_find_marks.h
haswell/stage2_build_tape.h
haswell/stringparsing.h
westmere/architecture.h
westmere/simd_input.h
westmere/simdutf8check.h
westmere/stage1_find_marks.h
westmere/stage2_build_tape.h
westmere/stringparsing.h
simd_input.h
simdutf8check.h
stringparsing.h
)
add_library(${SIMDJSON_LIB_NAME} ${SIMDJSON_LIB_TYPE} ${SIMDJSON_SRC} ${SIMDJSON_INCLUDE} ${SIMDJSON_SRC_HEADERS})
-18
View File
@@ -1,18 +0,0 @@
#ifndef SIMDJSON_ARM64_ARCHITECTURE_H
#define SIMDJSON_ARM64_ARCHITECTURE_H
#include "simdjson/portability.h"
#ifdef IS_ARM64
#include "simdjson/simdjson.h"
namespace simdjson::arm64 {
static const Architecture ARCHITECTURE = Architecture::ARM64;
} // namespace simdjson::arm64
#endif // IS_ARM64
#endif // SIMDJSON_ARM64_ARCHITECTURE_H
+10 -15
View File
@@ -1,7 +1,9 @@
#ifndef SIMDJSON_ARM64_SIMD_INPUT_H
#define SIMDJSON_ARM64_SIMD_INPUT_H
#include "../simd_input.h"
#include "simdjson/common_defs.h"
#include "simdjson/portability.h"
#include "simdjson/simdjson.h"
#ifdef IS_ARM64
@@ -32,14 +34,7 @@ really_inline uint64_t neon_movemask_bulk(uint8x16_t p0, uint8x16_t p1,
return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
}
} // namespace simdjson::arm64
namespace simdjson {
using namespace simdjson::arm64;
template <>
struct simd_input<Architecture::ARM64> {
struct simd_input {
const uint8x16_t chunks[4];
really_inline simd_input()
@@ -65,8 +60,8 @@ struct simd_input<Architecture::ARM64> {
}
template <typename F>
really_inline simd_input<Architecture::ARM64> map(F const& map_chunk) const {
return simd_input<Architecture::ARM64>(
really_inline simd_input map(F const& map_chunk) const {
return simd_input(
map_chunk(this->chunks[0]),
map_chunk(this->chunks[1]),
map_chunk(this->chunks[2]),
@@ -75,8 +70,8 @@ struct simd_input<Architecture::ARM64> {
}
template <typename F>
really_inline simd_input<Architecture::ARM64> map(simd_input<Architecture::ARM64> b, F const& map_chunk) const {
return simd_input<Architecture::ARM64>(
really_inline simd_input map(simd_input b, F const& map_chunk) const {
return simd_input(
map_chunk(this->chunks[0], b.chunks[0]),
map_chunk(this->chunks[1], b.chunks[1]),
map_chunk(this->chunks[2], b.chunks[2]),
@@ -95,7 +90,7 @@ struct simd_input<Architecture::ARM64> {
return neon_movemask_bulk(this->chunks[0], this->chunks[1], this->chunks[2], this->chunks[3]);
}
really_inline simd_input<Architecture::ARM64> bit_or(const uint8_t m) const {
really_inline simd_input bit_or(const uint8_t m) const {
const uint8x16_t mask = vmovq_n_u8(m);
return this->map( [&](auto a) {
return vorrq_u8(a, mask);
@@ -118,7 +113,7 @@ struct simd_input<Architecture::ARM64> {
}; // struct simd_input
} // namespace simdjson
} // namespace simdjson::arm64
#endif // IS_ARM64
#endif // SIMDJSON_ARM64_SIMD_INPUT_H
+110 -134
View File
@@ -4,10 +4,12 @@
#ifndef SIMDJSON_ARM64_SIMDUTF8CHECK_H
#define SIMDJSON_ARM64_SIMDUTF8CHECK_H
// TODO this is different from IS_ARM64 in portability.h, which we use in other places ...
#if defined(_ARM_NEON) || defined(__aarch64__) || \
(defined(_MSC_VER) && defined(_M_ARM64))
#include "../simdutf8check.h"
#include "simdjson/simdjson.h"
#include "arm64/simd_input.h"
#include <arm_neon.h>
#include <cinttypes>
#include <cstddef>
@@ -33,15 +35,6 @@
*/
namespace simdjson::arm64 {
// all byte values must be no larger than 0xF4
static inline void check_smaller_than_0xF4(int8x16_t current_bytes,
int8x16_t *has_error) {
// unsigned, saturates to 0 below max
*has_error = vorrq_s8(
*has_error, vreinterpretq_s8_u8(vqsubq_u8(
vreinterpretq_u8_s8(current_bytes), vdupq_n_u8(0xF4))));
}
static const int8_t _nibbles[] = {
1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
@@ -50,55 +43,6 @@ static const int8_t _nibbles[] = {
4, // 1111, next should be 0 (not checked here)
};
static inline int8x16_t continuation_lengths(int8x16_t high_nibbles) {
return vqtbl1q_s8(vld1q_s8(_nibbles), vreinterpretq_u8_s8(high_nibbles));
}
static inline int8x16_t carry_continuations(int8x16_t initial_lengths,
int8x16_t previous_carries) {
int8x16_t right1 = vreinterpretq_s8_u8(vqsubq_u8(
vreinterpretq_u8_s8(vextq_s8(previous_carries, initial_lengths, 16 - 1)),
vdupq_n_u8(1)));
int8x16_t sum = vaddq_s8(initial_lengths, right1);
int8x16_t right2 = vreinterpretq_s8_u8(
vqsubq_u8(vreinterpretq_u8_s8(vextq_s8(previous_carries, sum, 16 - 2)),
vdupq_n_u8(2)));
return vaddq_s8(sum, right2);
}
static inline void check_continuations(int8x16_t initial_lengths,
int8x16_t carries,
int8x16_t *has_error) {
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
uint8x16_t overunder = vceqq_u8(vcgtq_s8(carries, initial_lengths),
vcgtq_s8(initial_lengths, vdupq_n_s8(0)));
*has_error = vorrq_s8(*has_error, vreinterpretq_s8_u8(overunder));
}
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
static inline void check_first_continuation_max(int8x16_t current_bytes,
int8x16_t off1_current_bytes,
int8x16_t *has_error) {
uint8x16_t maskED = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xED));
uint8x16_t maskF4 = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xF4));
uint8x16_t badfollowED =
vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x9F)), maskED);
uint8x16_t badfollowF4 =
vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x8F)), maskF4);
*has_error = vorrq_s8(
*has_error, vreinterpretq_s8_u8(vorrq_u8(badfollowED, badfollowF4)));
}
static const int8_t _initial_mins[] = {
-128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, // 10xx => false
@@ -115,108 +59,140 @@ static const int8_t _second_mins[] = {
(int8_t)0x90,
};
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
static inline void check_overlong(int8x16_t current_bytes,
int8x16_t off1_current_bytes,
int8x16_t hibits, int8x16_t previous_hibits,
int8x16_t *has_error) {
int8x16_t off1_hibits = vextq_s8(previous_hibits, hibits, 16 - 1);
int8x16_t initial_mins =
vqtbl1q_s8(vld1q_s8(_initial_mins), vreinterpretq_u8_s8(off1_hibits));
uint8x16_t initial_under = vcgtq_s8(initial_mins, off1_current_bytes);
int8x16_t second_mins =
vqtbl1q_s8(vld1q_s8(_second_mins), vreinterpretq_u8_s8(off1_hibits));
uint8x16_t second_under = vcgtq_s8(second_mins, current_bytes);
*has_error = vorrq_s8(
*has_error, vreinterpretq_s8_u8(vandq_u8(initial_under, second_under)));
}
struct processed_utf_bytes {
int8x16_t raw_bytes;
int8x16_t high_nibbles;
int8x16_t carried_continuations;
};
static inline void count_nibbles(int8x16_t bytes,
struct processed_utf_bytes *answer) {
answer->raw_bytes = bytes;
answer->high_nibbles =
vreinterpretq_s8_u8(vshrq_n_u8(vreinterpretq_u8_s8(bytes), 4));
}
struct utf8_checker {
int8x16_t has_error{vdupq_n_s8(0)};
processed_utf_bytes previous{vdupq_n_s8(0), vdupq_n_s8(0), vdupq_n_s8(0)};
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
static inline struct processed_utf_bytes
check_utf8_bytes(int8x16_t current_bytes, struct processed_utf_bytes *previous,
int8x16_t *has_error) {
struct processed_utf_bytes pb;
count_nibbles(current_bytes, &pb);
really_inline void add_errors(int8x16_t errors) {
this->has_error = vorrq_s8(this->has_error, errors);
}
check_smaller_than_0xF4(current_bytes, has_error);
// all byte values must be no larger than 0xF4
really_inline void check_smaller_than_0xF4(int8x16_t current_bytes) {
// unsigned, saturates to 0 below max
this->add_errors( vreinterpretq_s8_u8(vqsubq_u8(
vreinterpretq_u8_s8(current_bytes), vdupq_n_u8(0xF4))) );
}
int8x16_t initial_lengths = continuation_lengths(pb.high_nibbles);
really_inline int8x16_t continuation_lengths(int8x16_t high_nibbles) {
return vqtbl1q_s8(vld1q_s8(_nibbles), vreinterpretq_u8_s8(high_nibbles));
}
pb.carried_continuations =
carry_continuations(initial_lengths, previous->carried_continuations);
really_inline int8x16_t carry_continuations(int8x16_t initial_lengths) {
int8x16_t right1 = vreinterpretq_s8_u8(vqsubq_u8(
vreinterpretq_u8_s8(vextq_s8(this->previous.carried_continuations, initial_lengths, 16 - 1)),
vdupq_n_u8(1)));
int8x16_t sum = vaddq_s8(initial_lengths, right1);
check_continuations(initial_lengths, pb.carried_continuations, has_error);
int8x16_t right2 = vreinterpretq_s8_u8(
vqsubq_u8(vreinterpretq_u8_s8(vextq_s8(this->previous.carried_continuations, sum, 16 - 2)),
vdupq_n_u8(2)));
return vaddq_s8(sum, right2);
}
int8x16_t off1_current_bytes =
vextq_s8(previous->raw_bytes, pb.raw_bytes, 16 - 1);
check_first_continuation_max(current_bytes, off1_current_bytes, has_error);
really_inline void check_continuations(int8x16_t initial_lengths, int8x16_t carries) {
check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles,
previous->high_nibbles, has_error);
return pb;
}
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
uint8x16_t overunder = vceqq_u8(vcgtq_s8(carries, initial_lengths),
vcgtq_s8(initial_lengths, vdupq_n_s8(0)));
// Checks that all bytes are ascii
really_inline bool check_ascii_neon(simd_input<Architecture::ARM64> in) {
// checking if the most significant bit is always equal to 0.
uint8x16_t high_bit = vdupq_n_u8(0x80);
uint8x16_t any_bits_on = in.reduce([&](auto a, auto b) {
return vorrq_u8(a, b);
});
uint8x16_t high_bit_on = vandq_u8(any_bits_on, high_bit);
uint64x2_t v64 = vreinterpretq_u64_u8(high_bit_on);
uint32x2_t v32 = vqmovn_u64(v64);
uint64x1_t result = vreinterpret_u64_u32(v32);
return vget_lane_u64(result, 0) == 0;
}
this->add_errors( vreinterpretq_s8_u8(overunder) );
}
} // namespace simdjson::arm64
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
really_inline void check_first_continuation_max(int8x16_t current_bytes, int8x16_t off1_current_bytes) {
uint8x16_t maskED = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xED));
uint8x16_t maskF4 = vceqq_s8(off1_current_bytes, vdupq_n_s8(0xF4));
namespace simdjson {
uint8x16_t badfollowED = vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x9F)), maskED);
uint8x16_t badfollowF4 = vandq_u8(vcgtq_s8(current_bytes, vdupq_n_s8(0x8F)), maskF4);
using namespace simdjson::arm64;
this->add_errors( vreinterpretq_s8_u8(vorrq_u8(badfollowED, badfollowF4)) );
}
template <>
struct utf8_checker<Architecture::ARM64> {
int8x16_t has_error{};
processed_utf_bytes previous{};
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
really_inline void check_overlong(int8x16_t current_bytes,
int8x16_t off1_current_bytes,
int8x16_t high_nibbles) {
int8x16_t off1_high_nibbles = vextq_s8(this->previous.high_nibbles, high_nibbles, 16 - 1);
int8x16_t initial_mins =
vqtbl1q_s8(vld1q_s8(_initial_mins), vreinterpretq_u8_s8(off1_high_nibbles));
really_inline void check_next_input(simd_input<Architecture::ARM64> in) {
uint8x16_t initial_under = vcgtq_s8(initial_mins, off1_current_bytes);
int8x16_t second_mins = vqtbl1q_s8(vld1q_s8(_second_mins), vreinterpretq_u8_s8(off1_high_nibbles));
uint8x16_t second_under = vcgtq_s8(second_mins, current_bytes);
this->add_errors( vreinterpretq_s8_u8(vandq_u8(initial_under, second_under)) );
}
really_inline int8x16_t count_nibbles(int8x16_t bytes) {
return vreinterpretq_s8_u8(vshrq_n_u8(vreinterpretq_u8_s8(bytes), 4));
}
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
really_inline void check_utf8_bytes(int8x16_t current_bytes) {
struct processed_utf_bytes pb;
pb.raw_bytes = current_bytes;
pb.high_nibbles = this->count_nibbles(current_bytes);
this->check_smaller_than_0xF4(current_bytes);
int8x16_t initial_lengths = this->continuation_lengths(pb.high_nibbles);
pb.carried_continuations = this->carry_continuations(initial_lengths);
this->check_continuations(initial_lengths, pb.carried_continuations);
int8x16_t off1_current_bytes = vextq_s8(this->previous.raw_bytes, pb.raw_bytes, 16 - 1);
this->check_first_continuation_max(current_bytes, off1_current_bytes);
this->check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles);
this->previous = pb;
}
// Checks that all bytes are ascii
really_inline bool check_ascii_neon(simd_input in) {
// checking if the most significant bit is always equal to 0.
uint8x16_t high_bit = vdupq_n_u8(0x80);
uint8x16_t any_bits_on = in.reduce([&](auto a, auto b) {
return vorrq_u8(a, b);
});
uint8x16_t high_bit_on = vandq_u8(any_bits_on, high_bit);
uint64x2_t v64 = vreinterpretq_u64_u8(high_bit_on);
uint32x2_t v32 = vqmovn_u64(v64);
uint64x1_t result = vreinterpret_u64_u32(v32);
return vget_lane_u64(result, 0) == 0;
}
really_inline void check_next_input(simd_input in) {
if (check_ascii_neon(in)) {
// All bytes are ascii. Therefore the byte that was just before must be
// ascii too. We only check the byte that was just before simd_input. Nines
// are arbitrary values.
const int8x16_t verror =
(int8x16_t){9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1};
this->has_error =
vorrq_s8(vreinterpretq_s8_u8(
vcgtq_s8(this->previous.carried_continuations, verror)),
this->has_error);
this->add_errors(vreinterpretq_s8_u8(
vcgtq_s8(this->previous.carried_continuations, verror)));
} else {
// it is not ascii so we have to do heavy work
in.each([&](auto _in) {
this->previous = check_utf8_bytes(vreinterpretq_s8_u8(_in), &(this->previous), &(this->has_error));
this->check_utf8_bytes(vreinterpretq_s8_u8(_in));
});
}
}
@@ -231,6 +207,6 @@ struct utf8_checker<Architecture::ARM64> {
}; // struct utf8_checker
} // namespace simdjson
} // namespace simdjson::arm64
#endif
#endif
+1 -2
View File
@@ -5,7 +5,6 @@
#ifdef IS_ARM64
#include "arm64/architecture.h"
#include "arm64/simd_input.h"
#include "arm64/simdutf8check.h"
#include "simdjson/stage1_find_marks.h"
@@ -22,7 +21,7 @@ really_inline uint64_t compute_quote_mask(const uint64_t quote_bits) {
}
really_inline void find_whitespace_and_operators(
const simd_input<ARCHITECTURE> in,
const simd_input in,
uint64_t &whitespace, uint64_t &op) {
const uint8x16_t low_nibble_mask =
(uint8x16_t){16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0};
-1
View File
@@ -6,7 +6,6 @@
#ifdef IS_ARM64
#include "simdjson/stage2_build_tape.h"
#include "arm64/architecture.h"
#include "arm64/stringparsing.h"
namespace simdjson::arm64 {
+16 -3
View File
@@ -1,14 +1,27 @@
#ifndef SIMDJSON_ARM64_STRINGPARSING_H
#define SIMDJSON_ARM64_STRINGPARSING_H
#include "../stringparsing.h"
#ifdef IS_ARM64
#include "arm64/architecture.h"
#include "simdjson/common_defs.h"
#include "simdjson/jsoncharutils.h"
#include "simdjson/parsedjson.h"
#ifdef JSON_TEST_STRINGS
void found_string(const uint8_t *buf, const uint8_t *parsed_begin,
const uint8_t *parsed_end);
void found_bad_string(const uint8_t *buf);
#endif
namespace simdjson::arm64 {
// Holds backslashes and quotes locations.
struct parse_string_helper {
uint32_t bs_bits;
uint32_t quote_bits;
really_inline uint32_t bytes_processed() const { return sizeof(uint8x16_t)*2; }
};
really_inline parse_string_helper find_bs_bits_and_quote_bits(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
+6 -6
View File
@@ -89,7 +89,7 @@ really_inline ErrorValues detect_errors_on_eof(
//
// Backslash sequences outside of quotes will be detected in stage 2.
//
really_inline uint64_t find_strings(const simd_input<ARCHITECTURE> in, uint64_t &prev_escaped, uint64_t &prev_in_string) {
really_inline uint64_t find_strings(const simd_input in, uint64_t &prev_escaped, uint64_t &prev_in_string) {
const uint64_t backslash = in.eq('\\');
const uint64_t escaped = follows_odd_sequence_of(backslash, prev_escaped);
const uint64_t quote = in.eq('"') & ~escaped;
@@ -128,7 +128,7 @@ really_inline uint64_t invalid_string_bytes(const uint64_t unescaped, const uint
// contents of a string the same as content outside. Errors and structurals inside the string or on
// the trailing quote will need to be removed later when the correct string information is known.
//
really_inline uint64_t find_potential_structurals(const simd_input<ARCHITECTURE> in, uint64_t &prev_primitive) {
really_inline uint64_t find_potential_structurals(const simd_input in, uint64_t &prev_primitive) {
// These use SIMD so let's kick them off before running the regular 64-bit stuff ...
uint64_t whitespace, op;
find_whitespace_and_operators(in, whitespace, op);
@@ -172,12 +172,12 @@ really_inline void find_structural_bits_128(
uint64_t &prev_primitive,
uint64_t &prev_structurals,
uint64_t &unescaped_chars_error,
utf8_checker<ARCHITECTURE> &utf8_state) {
utf8_checker &utf8_state) {
//
// Load up all 128 bytes into SIMD registers
//
simd_input<ARCHITECTURE> in_1(buf);
simd_input<ARCHITECTURE> in_2(buf+64);
simd_input in_1(buf);
simd_input in_2(buf+64);
//
// Find the strings and potential structurals (operators / primitives).
@@ -216,7 +216,7 @@ int find_structural_bits(const uint8_t *buf, size_t len, simdjson::ParsedJson &p
return simdjson::CAPACITY;
}
uint32_t *base_ptr = pj.structural_indexes;
utf8_checker<ARCHITECTURE> utf8_state;
utf8_checker utf8_state;
// Whether the first character of the next iteration is escaped.
uint64_t prev_escaped = 0ULL;
+69 -9
View File
@@ -3,6 +3,72 @@
// We assume the file in which it is include already includes
// "stringparsing.h" (this simplifies amalgation)
// begin copypasta
// These chars yield themselves: " \ /
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
// u not handled in this table as it's complex
static const uint8_t escape_map[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
// handle a unicode codepoint
// write appropriate values into dest
// src will advance 6 bytes or 12 bytes
// dest will advance a variable amount (return via pointer)
// return true if the unicode codepoint was valid
// We work in little-endian then swap at write time
WARN_UNUSED
really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
uint8_t **dst_ptr) {
// hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
// conversion isn't valid; we defer the check for this to inside the
// multilingual plane check
uint32_t code_point = hex_to_u32_nocheck(*src_ptr + 2);
*src_ptr += 6;
// check for low surrogate for characters outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
return false;
}
uint32_t code_point_2 = hex_to_u32_nocheck(*src_ptr + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
*src_ptr += 6;
}
size_t offset = codepoint_to_utf8(code_point, *dst_ptr);
*dst_ptr += offset;
return offset > 0;
}
WARN_UNUSED really_inline bool parse_string(UNUSED const uint8_t *buf,
UNUSED size_t len, ParsedJson &pj,
UNUSED const uint32_t depth,
@@ -12,8 +78,7 @@ WARN_UNUSED really_inline bool parse_string(UNUSED const uint8_t *buf,
uint8_t *dst = pj.current_string_buf_loc + sizeof(uint32_t);
const uint8_t *const start_of_string = dst;
while (1) {
parse_string_helper helper =
find_bs_bits_and_quote_bits(src, dst);
parse_string_helper helper = find_bs_bits_and_quote_bits(src, dst);
if (((helper.bs_bits - 1) & helper.quote_bits) != 0) {
/* we encountered quotes first. Move dst to point to quotes and exit
*/
@@ -71,13 +136,8 @@ WARN_UNUSED really_inline bool parse_string(UNUSED const uint8_t *buf,
} else {
/* they are the same. Since they can't co-occur, it means we
* encountered neither. */
if constexpr (ARCHITECTURE == Architecture::WESTMERE) {
src += 16;
dst += 16;
} else {
src += 32;
dst += 32;
}
src += helper.bytes_processed();
dst += helper.bytes_processed();
}
}
/* can't be reached */
-20
View File
@@ -1,20 +0,0 @@
#ifndef SIMDJSON_HASWELL_ARCHITECTURE_H
#define SIMDJSON_HASWELL_ARCHITECTURE_H
#include "simdjson/portability.h"
#ifdef IS_X86_64
#include "simdjson/simdjson.h"
namespace simdjson::haswell {
static const Architecture ARCHITECTURE = Architecture::HASWELL;
} // namespace simdjson::haswell
#endif // IS_X86_64
#endif // SIMDJSON_HASWELL_ARCHITECTURE_H
+11 -10
View File
@@ -1,15 +1,16 @@
#ifndef SIMDJSON_HASWELL_SIMD_INPUT_H
#define SIMDJSON_HASWELL_SIMD_INPUT_H
#include "../simd_input.h"
#include "simdjson/common_defs.h"
#include "simdjson/portability.h"
#include "simdjson/simdjson.h"
#ifdef IS_X86_64
TARGET_HASWELL
namespace simdjson {
namespace simdjson::haswell {
template <>
struct simd_input<Architecture::HASWELL> {
struct simd_input {
const __m256i chunks[2];
really_inline simd_input() : chunks{__m256i(), __m256i()} {}
@@ -31,16 +32,16 @@ struct simd_input<Architecture::HASWELL> {
}
template <typename F>
really_inline simd_input<Architecture::HASWELL> map(F const& map_chunk) const {
return simd_input<Architecture::HASWELL>(
really_inline simd_input map(F const& map_chunk) const {
return simd_input(
map_chunk(this->chunks[0]),
map_chunk(this->chunks[1])
);
}
template <typename F>
really_inline simd_input<Architecture::HASWELL> map(const simd_input<Architecture::HASWELL> b, F const& map_chunk) const {
return simd_input<Architecture::HASWELL>(
really_inline simd_input map(const simd_input b, F const& map_chunk) const {
return simd_input(
map_chunk(this->chunks[0], b.chunks[0]),
map_chunk(this->chunks[1], b.chunks[1])
);
@@ -57,7 +58,7 @@ struct simd_input<Architecture::HASWELL> {
return r_lo | (r_hi << 32);
}
really_inline simd_input<Architecture::HASWELL> bit_or(const uint8_t m) const {
really_inline simd_input bit_or(const uint8_t m) const {
const __m256i mask = _mm256_set1_epi8(m);
return this->map( [&](auto a) {
return _mm256_or_si256(a, mask);
@@ -80,7 +81,7 @@ struct simd_input<Architecture::HASWELL> {
}; // struct simd_input
} // namespace simdjson
} // namespace simdjson::haswell
UNTARGET_REGION
#endif // IS_X86_64
+167 -183
View File
@@ -2,7 +2,8 @@
#define SIMDJSON_HASWELL_SIMDUTF8CHECK_H
#include "simdjson/portability.h"
#include "../simdutf8check.h"
#include "simdjson/simdjson.h"
#include "haswell/simd_input.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
@@ -38,212 +39,195 @@ static inline __m256i push_last_2bytes_of_a_to_b(__m256i a, __m256i b) {
return _mm256_alignr_epi8(b, _mm256_permute2x128_si256(a, b, 0x21), 14);
}
// all byte values must be no larger than 0xF4
static inline void avx_check_smaller_than_0xF4(__m256i current_bytes,
__m256i *has_error) {
// unsigned, saturates to 0 below max
*has_error = _mm256_or_si256(
*has_error, _mm256_subs_epu8(current_bytes, _mm256_set1_epi8(0xF4u)));
}
static inline __m256i avx_continuation_lengths(__m256i high_nibbles) {
return _mm256_shuffle_epi8(
_mm256_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4, // 1111, next should be 0 (not checked here)
1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4 // 1111, next should be 0 (not checked here)
),
high_nibbles);
}
static inline __m256i avx_carry_continuations(__m256i initial_lengths,
__m256i previous_carries) {
__m256i right1 = _mm256_subs_epu8(
push_last_byte_of_a_to_b(previous_carries, initial_lengths),
_mm256_set1_epi8(1));
__m256i sum = _mm256_add_epi8(initial_lengths, right1);
__m256i right2 = _mm256_subs_epu8(
push_last_2bytes_of_a_to_b(previous_carries, sum), _mm256_set1_epi8(2));
return _mm256_add_epi8(sum, right2);
}
static inline void avx_check_continuations(__m256i initial_lengths,
__m256i carries,
__m256i *has_error) {
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
__m256i overunder = _mm256_cmpeq_epi8(
_mm256_cmpgt_epi8(carries, initial_lengths),
_mm256_cmpgt_epi8(initial_lengths, _mm256_setzero_si256()));
*has_error = _mm256_or_si256(*has_error, overunder);
}
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
static inline void avx_check_first_continuation_max(__m256i current_bytes,
__m256i off1_current_bytes,
__m256i *has_error) {
__m256i maskED =
_mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xEDu));
__m256i maskF4 =
_mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xF4u));
__m256i badfollowED = _mm256_and_si256(
_mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x9Fu)), maskED);
__m256i badfollowF4 = _mm256_and_si256(
_mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x8Fu)), maskF4);
*has_error =
_mm256_or_si256(*has_error, _mm256_or_si256(badfollowED, badfollowF4));
}
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
static inline void avx_check_overlong(__m256i current_bytes,
__m256i off1_current_bytes,
__m256i hibits, __m256i previous_hibits,
__m256i *has_error) {
__m256i off1_hibits = push_last_byte_of_a_to_b(previous_hibits, hibits);
__m256i initial_mins = _mm256_shuffle_epi8(
_mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u, // 1111
-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u), // 1111
off1_hibits);
__m256i initial_under = _mm256_cmpgt_epi8(initial_mins, off1_current_bytes);
__m256i second_mins = _mm256_shuffle_epi8(
_mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u, // 1111
-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u), // 1111
off1_hibits);
__m256i second_under = _mm256_cmpgt_epi8(second_mins, current_bytes);
*has_error = _mm256_or_si256(*has_error,
_mm256_and_si256(initial_under, second_under));
}
struct avx_processed_utf_bytes {
struct processed_utf_bytes {
__m256i raw_bytes;
__m256i high_nibbles;
__m256i carried_continuations;
};
static inline void avx_count_nibbles(__m256i bytes,
struct avx_processed_utf_bytes *answer) {
answer->raw_bytes = bytes;
answer->high_nibbles =
_mm256_and_si256(_mm256_srli_epi16(bytes, 4), _mm256_set1_epi8(0x0F));
}
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
static inline struct avx_processed_utf_bytes
avx_check_utf8_bytes(__m256i current_bytes,
struct avx_processed_utf_bytes *previous,
__m256i *has_error) {
struct avx_processed_utf_bytes pb {};
avx_count_nibbles(current_bytes, &pb);
avx_check_smaller_than_0xF4(current_bytes, has_error);
__m256i initial_lengths = avx_continuation_lengths(pb.high_nibbles);
pb.carried_continuations =
avx_carry_continuations(initial_lengths, previous->carried_continuations);
avx_check_continuations(initial_lengths, pb.carried_continuations, has_error);
__m256i off1_current_bytes =
push_last_byte_of_a_to_b(previous->raw_bytes, pb.raw_bytes);
avx_check_first_continuation_max(current_bytes, off1_current_bytes,
has_error);
avx_check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles,
previous->high_nibbles, has_error);
return pb;
}
}; // namespace simdjson::haswell
UNTARGET_REGION // haswell
TARGET_HASWELL
namespace simdjson {
using namespace simdjson::haswell;
template <>
struct utf8_checker<Architecture::HASWELL> {
struct utf8_checker {
__m256i has_error;
avx_processed_utf_bytes previous;
processed_utf_bytes previous;
utf8_checker() {
has_error = _mm256_setzero_si256();
previous.raw_bytes = _mm256_setzero_si256();
previous.high_nibbles = _mm256_setzero_si256();
previous.carried_continuations = _mm256_setzero_si256();
utf8_checker() :
has_error{_mm256_setzero_si256()},
previous{_mm256_setzero_si256(), _mm256_setzero_si256(), _mm256_setzero_si256()} {}
really_inline void add_errors(__m256i errors) {
this->has_error = _mm256_or_si256(this->has_error, errors);
}
really_inline void check_next_input(simd_input<Architecture::HASWELL> in) {
// all byte values must be no larger than 0xF4
really_inline void check_smaller_than_0xF4(__m256i current_bytes) {
// unsigned, saturates to 0 below max
this->add_errors( _mm256_subs_epu8(current_bytes, _mm256_set1_epi8(0xF4u)) );
}
really_inline __m256i continuation_lengths(__m256i high_nibbles) {
return _mm256_shuffle_epi8(
_mm256_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4, // 1111, next should be 0 (not checked here)
1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4), // 1111, next should be 0 (not checked here)
high_nibbles);
}
really_inline __m256i carry_continuations(__m256i initial_lengths) {
__m256i right1 = _mm256_subs_epu8(
push_last_byte_of_a_to_b(this->previous.carried_continuations, initial_lengths),
_mm256_set1_epi8(1));
__m256i sum = _mm256_add_epi8(initial_lengths, right1);
__m256i right2 = _mm256_subs_epu8(
push_last_2bytes_of_a_to_b(this->previous.carried_continuations, sum), _mm256_set1_epi8(2));
return _mm256_add_epi8(sum, right2);
}
really_inline void check_continuations(__m256i initial_lengths, __m256i carries) {
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
// (carries > current) == (current > 0)
__m256i overunder = _mm256_cmpeq_epi8(
_mm256_cmpgt_epi8(carries, initial_lengths),
_mm256_cmpgt_epi8(initial_lengths, _mm256_setzero_si256()));
this->add_errors( overunder );
}
really_inline void check_carried_continuations() {
this->add_errors(
_mm256_cmpgt_epi8(this->previous.carried_continuations,
_mm256_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 1))
);
}
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
really_inline void check_first_continuation_max(__m256i current_bytes,
__m256i off1_current_bytes) {
__m256i maskED =
_mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xEDu));
__m256i maskF4 =
_mm256_cmpeq_epi8(off1_current_bytes, _mm256_set1_epi8(0xF4u));
__m256i badfollowED = _mm256_and_si256(
_mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x9Fu)), maskED);
__m256i badfollowF4 = _mm256_and_si256(
_mm256_cmpgt_epi8(current_bytes, _mm256_set1_epi8(0x8Fu)), maskF4);
this->add_errors( _mm256_or_si256(badfollowED, badfollowF4) );
}
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
really_inline void check_overlong(__m256i current_bytes,
__m256i off1_current_bytes,
__m256i high_nibbles) {
__m256i off1_high_nibbles = push_last_byte_of_a_to_b(this->previous.high_nibbles, high_nibbles);
__m256i initial_mins = _mm256_shuffle_epi8(
_mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u, // 1111
-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u), // 1111
off1_high_nibbles);
__m256i initial_under = _mm256_cmpgt_epi8(initial_mins, off1_current_bytes);
__m256i second_mins = _mm256_shuffle_epi8(
_mm256_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u, // 1111
-128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u), // 1111
off1_high_nibbles);
__m256i second_under = _mm256_cmpgt_epi8(second_mins, current_bytes);
this->add_errors( _mm256_and_si256(initial_under, second_under) );
}
really_inline void count_nibbles(__m256i bytes, struct processed_utf_bytes *answer) {
answer->raw_bytes = bytes;
answer->high_nibbles = _mm256_and_si256(_mm256_srli_epi16(bytes, 4), _mm256_set1_epi8(0x0F));
}
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
really_inline void check_utf8_bytes(__m256i current_bytes) {
struct processed_utf_bytes pb {};
this->count_nibbles(current_bytes, &pb);
this->check_smaller_than_0xF4(current_bytes);
__m256i initial_lengths = this->continuation_lengths(pb.high_nibbles);
pb.carried_continuations = this->carry_continuations(initial_lengths);
this->check_continuations(initial_lengths, pb.carried_continuations);
__m256i off1_current_bytes =
push_last_byte_of_a_to_b(this->previous.raw_bytes, pb.raw_bytes);
this->check_first_continuation_max(current_bytes, off1_current_bytes);
this->check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles);
this->previous = pb;
}
really_inline void check_next_input(__m256i in) {
__m256i high_bit = _mm256_set1_epi8(0x80u);
if (likely(_mm256_testz_si256(in, high_bit) == 1)) {
this->check_carried_continuations();
} else {
this->check_utf8_bytes(in);
}
}
really_inline void check_next_input(simd_input in) {
__m256i high_bit = _mm256_set1_epi8(0x80u);
__m256i any_bits_on = in.reduce([&](auto a, auto b) {
return _mm256_or_si256(a, b);
});
if (likely(_mm256_testz_si256(any_bits_on, high_bit) == 1)) {
// it is ascii, we just check continuation
this->has_error = _mm256_or_si256(
_mm256_cmpgt_epi8(this->previous.carried_continuations,
_mm256_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 1)),
this->has_error);
// it is ascii, we just check carried continuations.
this->check_carried_continuations();
} else {
// it is not ascii so we have to do heavy work
in.each([&](auto _in) {
this->previous = avx_check_utf8_bytes(_in, &(this->previous), &(this->has_error));
});
in.each([&](auto _in) { check_utf8_bytes(_in); });
}
}
really_inline ErrorValues errors() {
return _mm256_testz_si256(this->has_error, this->has_error) == 0
? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
return _mm256_testz_si256(this->has_error, this->has_error) == 0 ? simdjson::UTF8_ERROR : simdjson::SUCCESS;
}
}; // struct utf8_checker
}; // namespace simdjson
}; // namespace simdjson::haswell
UNTARGET_REGION // haswell
#endif // IS_X86_64
#endif
#endif // SIMDJSON_HASWELL_SIMDUTF8CHECK_H
+1 -2
View File
@@ -5,7 +5,6 @@
#ifdef IS_X86_64
#include "haswell/architecture.h"
#include "haswell/simd_input.h"
#include "haswell/simdutf8check.h"
#include "simdjson/stage1_find_marks.h"
@@ -22,7 +21,7 @@ really_inline uint64_t compute_quote_mask(const uint64_t quote_bits) {
}
really_inline void find_whitespace_and_operators(
const simd_input<ARCHITECTURE> in,
const simd_input in,
uint64_t &whitespace, uint64_t &op) {
#ifdef SIMDJSON_NAIVE_STRUCTURAL
-1
View File
@@ -6,7 +6,6 @@
#ifdef IS_X86_64
#include "simdjson/stage2_build_tape.h"
#include "haswell/architecture.h"
#include "haswell/stringparsing.h"
TARGET_HASWELL
+16 -3
View File
@@ -1,15 +1,28 @@
#ifndef SIMDJSON_HASWELL_STRINGPARSING_H
#define SIMDJSON_HASWELL_STRINGPARSING_H
#include "../stringparsing.h"
#ifdef IS_X86_64
#include "haswell/architecture.h"
#include "simdjson/common_defs.h"
#include "simdjson/jsoncharutils.h"
#include "simdjson/parsedjson.h"
#ifdef JSON_TEST_STRINGS
void found_string(const uint8_t *buf, const uint8_t *parsed_begin,
const uint8_t *parsed_end);
void found_bad_string(const uint8_t *buf);
#endif
TARGET_HASWELL
namespace simdjson::haswell {
// Holds backslashes and quotes locations.
struct parse_string_helper {
uint32_t bs_bits;
uint32_t quote_bits;
really_inline uint32_t bytes_processed() const { return sizeof(__m256i); }
};
really_inline parse_string_helper find_bs_bits_and_quote_bits(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding
-35
View File
@@ -1,35 +0,0 @@
#ifndef SIMDJSON_SIMD_INPUT_H
#define SIMDJSON_SIMD_INPUT_H
#include "simdjson/common_defs.h"
#include "simdjson/portability.h"
#include "simdjson/simdjson.h"
namespace simdjson {
template <Architecture T>
struct simd_input {
simd_input(const uint8_t *ptr);
// Run an operation on each chunk.
template <typename F>
really_inline void each(F const& each_chunk);
// Map through each simd register in this input, producing another simd_input.
template <typename F>
really_inline simd_input<T> map(F const& map_chunk);
// Map through each simd register across two inputs, producing a single simd_input.
template <typename F>
really_inline simd_input<T> map(simd_input<T> b, F const& map_chunk);
// Run a horizontal operation like "sum" across the whole input
// template <typename F>
// really_inline simd<T> reduce(F const& map_chunk);
// turn this bytemask (usually the result of a simd comparison operation) into a bitmask.
uint64_t to_bitmask();
// a straightforward comparison of a mask against input.
uint64_t eq(uint8_t m);
// find all values less than or equal than the content of maxval (using unsigned arithmetic)
uint64_t lteq(uint8_t m);
}; // struct simd_input
} // namespace simdjson
#endif
-20
View File
@@ -1,20 +0,0 @@
#ifndef SIMDJSON_SIMDUTF8CHECK_H
#define SIMDJSON_SIMDUTF8CHECK_H
#include "simdjson/simdjson.h"
#include "simd_input.h"
namespace simdjson {
// Checks UTF8, chunk by chunk.
template <Architecture T>
struct utf8_checker {
// Process the next chunk of input.
void check_next_input(simd_input<T> in);
// Find out what (if any) errors have occurred
ErrorValues errors();
};
} // namespace simdjson
#endif // SIMDJSON_SIMDUTF8CHECK_H
-90
View File
@@ -1,90 +0,0 @@
#ifndef SIMDJSON_STRINGPARSING_H
#define SIMDJSON_STRINGPARSING_H
#include "simdjson/common_defs.h"
#include "simdjson/jsoncharutils.h"
#include "simdjson/parsedjson.h"
#ifdef JSON_TEST_STRINGS
void found_string(const uint8_t *buf, const uint8_t *parsed_begin,
const uint8_t *parsed_end);
void found_bad_string(const uint8_t *buf);
#endif
namespace simdjson {
// begin copypasta
// These chars yield themselves: " \ /
// b -> backspace, f -> formfeed, n -> newline, r -> cr, t -> horizontal tab
// u not handled in this table as it's complex
static const uint8_t escape_map[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2f,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0, 0, // 0x5.
0, 0, 0x08, 0, 0, 0, 0x0c, 0, 0, 0, 0, 0, 0, 0, 0x0a, 0, // 0x6.
0, 0, 0x0d, 0, 0x09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7.
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
// handle a unicode codepoint
// write appropriate values into dest
// src will advance 6 bytes or 12 bytes
// dest will advance a variable amount (return via pointer)
// return true if the unicode codepoint was valid
// We work in little-endian then swap at write time
WARN_UNUSED
really_inline bool handle_unicode_codepoint(const uint8_t **src_ptr,
uint8_t **dst_ptr) {
// hex_to_u32_nocheck fills high 16 bits of the return value with 1s if the
// conversion isn't valid; we defer the check for this to inside the
// multilingual plane check
uint32_t code_point = hex_to_u32_nocheck(*src_ptr + 2);
*src_ptr += 6;
// check for low surrogate for characters outside the Basic
// Multilingual Plane.
if (code_point >= 0xd800 && code_point < 0xdc00) {
if (((*src_ptr)[0] != '\\') || (*src_ptr)[1] != 'u') {
return false;
}
uint32_t code_point_2 = hex_to_u32_nocheck(*src_ptr + 2);
// if the first code point is invalid we will get here, as we will go past
// the check for being outside the Basic Multilingual plane. If we don't
// find a \u immediately afterwards we fail out anyhow, but if we do,
// this check catches both the case of the first code point being invalid
// or the second code point being invalid.
if ((code_point | code_point_2) >> 16) {
return false;
}
code_point =
(((code_point - 0xd800) << 10) | (code_point_2 - 0xdc00)) + 0x10000;
*src_ptr += 6;
}
size_t offset = codepoint_to_utf8(code_point, *dst_ptr);
*dst_ptr += offset;
return offset > 0;
}
// Holds backslashes and quotes locations.
struct parse_string_helper {
uint32_t bs_bits;
uint32_t quote_bits;
};
} // namespace simdjson
#endif // SIMDJSON_STRINGPARSING_H
-19
View File
@@ -1,19 +0,0 @@
#ifndef SIMDJSON_WESTMERE_ARCHITECTURE_H
#define SIMDJSON_WESTMERE_ARCHITECTURE_H
#include "simdjson/portability.h"
#ifdef IS_X86_64
#include "simdjson/simdjson.h"
namespace simdjson::westmere {
static const Architecture ARCHITECTURE = Architecture::WESTMERE;
} // namespace simdjson::westmere
#endif // IS_X86_64
#endif // SIMDJSON_WESTMERE_ARCHITECTURE_H
+11 -10
View File
@@ -1,15 +1,16 @@
#ifndef SIMDJSON_WESTMERE_SIMD_INPUT_H
#define SIMDJSON_WESTMERE_SIMD_INPUT_H
#include "../simd_input.h"
#include "simdjson/common_defs.h"
#include "simdjson/portability.h"
#include "simdjson/simdjson.h"
#ifdef IS_X86_64
TARGET_WESTMERE
namespace simdjson {
namespace simdjson::westmere {
template <>
struct simd_input<Architecture::WESTMERE> {
struct simd_input {
const __m128i chunks[4];
really_inline simd_input()
@@ -35,8 +36,8 @@ struct simd_input<Architecture::WESTMERE> {
}
template <typename F>
really_inline simd_input<Architecture::WESTMERE> map(F const& map_chunk) const {
return simd_input<Architecture::WESTMERE>(
really_inline simd_input map(F const& map_chunk) const {
return simd_input(
map_chunk(this->chunks[0]),
map_chunk(this->chunks[1]),
map_chunk(this->chunks[2]),
@@ -45,8 +46,8 @@ struct simd_input<Architecture::WESTMERE> {
}
template <typename F>
really_inline simd_input<Architecture::WESTMERE> map(const simd_input<Architecture::WESTMERE> b, F const& map_chunk) const {
return simd_input<Architecture::WESTMERE>(
really_inline simd_input map(const simd_input b, F const& map_chunk) const {
return simd_input(
map_chunk(this->chunks[0], b.chunks[0]),
map_chunk(this->chunks[1], b.chunks[1]),
map_chunk(this->chunks[2], b.chunks[2]),
@@ -69,7 +70,7 @@ struct simd_input<Architecture::WESTMERE> {
return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
}
really_inline simd_input<Architecture::WESTMERE> bit_or(const uint8_t m) const {
really_inline simd_input bit_or(const uint8_t m) const {
const __m128i mask = _mm_set1_epi8(m);
return this->map( [&](auto a) {
return _mm_or_si128(a, mask);
@@ -92,7 +93,7 @@ struct simd_input<Architecture::WESTMERE> {
}; // struct simd_input
} // namespace simdjson
} // namespace simdjson::westmere
UNTARGET_REGION
#endif // IS_X86_64
+134 -147
View File
@@ -2,7 +2,8 @@
#define SIMDJSON_WESTMERE_SIMDUTF8CHECK_H
#include "simdjson/portability.h"
#include "simdutf8check.h"
#include "simdjson/simdjson.h"
#include "westmere/simd_input.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
@@ -31,184 +32,170 @@
TARGET_WESTMERE
namespace simdjson::westmere {
// all byte values must be no larger than 0xF4
static inline void check_smaller_than_0xF4(__m128i current_bytes,
__m128i *has_error) {
// unsigned, saturates to 0 below max
*has_error = _mm_or_si128(*has_error,
_mm_subs_epu8(current_bytes, _mm_set1_epi8(0xF4u)));
}
static inline __m128i continuation_lengths(__m128i high_nibbles) {
return _mm_shuffle_epi8(
_mm_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4), // 1111, next should be 0 (not checked here)
high_nibbles);
}
static inline __m128i carry_continuations(__m128i initial_lengths,
__m128i previous_carries) {
__m128i right1 =
_mm_subs_epu8(_mm_alignr_epi8(initial_lengths, previous_carries, 16 - 1),
_mm_set1_epi8(1));
__m128i sum = _mm_add_epi8(initial_lengths, right1);
__m128i right2 = _mm_subs_epu8(_mm_alignr_epi8(sum, previous_carries, 16 - 2),
_mm_set1_epi8(2));
return _mm_add_epi8(sum, right2);
}
static inline void check_continuations(__m128i initial_lengths, __m128i carries,
__m128i *has_error) {
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
__m128i overunder =
_mm_cmpeq_epi8(_mm_cmpgt_epi8(carries, initial_lengths),
_mm_cmpgt_epi8(initial_lengths, _mm_setzero_si128()));
*has_error = _mm_or_si128(*has_error, overunder);
}
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
static inline void check_first_continuation_max(__m128i current_bytes,
__m128i off1_current_bytes,
__m128i *has_error) {
__m128i maskED = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xEDu));
__m128i maskF4 = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xF4u));
__m128i badfollowED = _mm_and_si128(
_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x9Fu)), maskED);
__m128i badfollowF4 = _mm_and_si128(
_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x8Fu)), maskF4);
*has_error = _mm_or_si128(*has_error, _mm_or_si128(badfollowED, badfollowF4));
}
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
static inline void check_overlong(__m128i current_bytes,
__m128i off1_current_bytes, __m128i hibits,
__m128i previous_hibits, __m128i *has_error) {
__m128i off1_hibits = _mm_alignr_epi8(hibits, previous_hibits, 16 - 1);
__m128i initial_mins = _mm_shuffle_epi8(
_mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u),
off1_hibits);
__m128i initial_under = _mm_cmpgt_epi8(initial_mins, off1_current_bytes);
__m128i second_mins = _mm_shuffle_epi8(
_mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u),
off1_hibits);
__m128i second_under = _mm_cmpgt_epi8(second_mins, current_bytes);
*has_error =
_mm_or_si128(*has_error, _mm_and_si128(initial_under, second_under));
}
struct processed_utf_bytes {
__m128i raw_bytes;
__m128i high_nibbles;
__m128i carried_continuations;
};
static inline void count_nibbles(__m128i bytes,
struct processed_utf_bytes *answer) {
answer->raw_bytes = bytes;
answer->high_nibbles =
_mm_and_si128(_mm_srli_epi16(bytes, 4), _mm_set1_epi8(0x0F));
}
struct utf8_checker {
__m128i has_error{_mm_setzero_si128()};
processed_utf_bytes previous{_mm_setzero_si128(), _mm_setzero_si128(), _mm_setzero_si128()};
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
static struct processed_utf_bytes
check_utf8_bytes(__m128i current_bytes, struct processed_utf_bytes *previous,
__m128i *has_error) {
struct processed_utf_bytes pb;
count_nibbles(current_bytes, &pb);
really_inline void add_errors(__m128i errors) {
this->has_error = _mm_or_si128(errors, this->has_error);
}
check_smaller_than_0xF4(current_bytes, has_error);
// all byte values must be no larger than 0xF4
really_inline void check_smaller_than_0xF4(__m128i current_bytes) {
// unsigned, saturates to 0 below max
this->add_errors( _mm_subs_epu8(current_bytes, _mm_set1_epi8(0xF4u)) );
}
__m128i initial_lengths = continuation_lengths(pb.high_nibbles);
really_inline __m128i continuation_lengths(__m128i high_nibbles) {
return _mm_shuffle_epi8(
_mm_setr_epi8(1, 1, 1, 1, 1, 1, 1, 1, // 0xxx (ASCII)
0, 0, 0, 0, // 10xx (continuation)
2, 2, // 110x
3, // 1110
4), // 1111, next should be 0 (not checked here)
high_nibbles);
}
pb.carried_continuations =
carry_continuations(initial_lengths, previous->carried_continuations);
really_inline __m128i carry_continuations(__m128i initial_lengths) {
check_continuations(initial_lengths, pb.carried_continuations, has_error);
__m128i right1 =
_mm_subs_epu8(_mm_alignr_epi8(initial_lengths, this->previous.carried_continuations, 16 - 1),
_mm_set1_epi8(1));
__m128i sum = _mm_add_epi8(initial_lengths, right1);
__m128i off1_current_bytes =
_mm_alignr_epi8(pb.raw_bytes, previous->raw_bytes, 16 - 1);
check_first_continuation_max(current_bytes, off1_current_bytes, has_error);
__m128i right2 = _mm_subs_epu8(_mm_alignr_epi8(sum, this->previous.carried_continuations, 16 - 2),
_mm_set1_epi8(2));
return _mm_add_epi8(sum, right2);
}
check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles,
previous->high_nibbles, has_error);
return pb;
}
really_inline void check_continuations(__m128i initial_lengths, __m128i carries) {
} // namespace simdjson::westmere
UNTARGET_REGION
// overlap || underlap
// carry > length && length > 0 || !(carry > length) && !(length > 0)
// (carries > length) == (lengths > 0)
__m128i overunder =
_mm_cmpeq_epi8(_mm_cmpgt_epi8(carries, initial_lengths),
_mm_cmpgt_epi8(initial_lengths, _mm_setzero_si128()));
TARGET_WESTMERE
namespace simdjson {
this->add_errors( overunder );
}
using namespace simdjson::westmere;
// when 0xED is found, next byte must be no larger than 0x9F
// when 0xF4 is found, next byte must be no larger than 0x8F
// next byte must be continuation, ie sign bit is set, so signed < is ok
really_inline void check_first_continuation_max(__m128i current_bytes, __m128i off1_current_bytes) {
__m128i maskED = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xEDu));
__m128i maskF4 = _mm_cmpeq_epi8(off1_current_bytes, _mm_set1_epi8(0xF4u));
template <>
struct utf8_checker<Architecture::WESTMERE> {
__m128i has_error = _mm_setzero_si128();
processed_utf_bytes previous{
_mm_setzero_si128(), // raw_bytes
_mm_setzero_si128(), // high_nibbles
_mm_setzero_si128() // carried_continuations
};
__m128i badfollowED = _mm_and_si128(
_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x9Fu)), maskED);
__m128i badfollowF4 = _mm_and_si128(
_mm_cmpgt_epi8(current_bytes, _mm_set1_epi8(0x8Fu)), maskF4);
really_inline void check_next_input(simd_input<Architecture::WESTMERE> in) {
this->add_errors( _mm_or_si128(badfollowED, badfollowF4) );
}
// map off1_hibits => error condition
// hibits off1 cur
// C => < C2 && true
// E => < E1 && < A0
// F => < F1 && < 90
// else false && false
really_inline void check_overlong(__m128i current_bytes,
__m128i off1_current_bytes, __m128i high_nibbles) {
__m128i off1_hibits = _mm_alignr_epi8(high_nibbles, this->previous.high_nibbles, 16 - 1);
__m128i initial_mins = _mm_shuffle_epi8(
_mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, // 10xx => false
0xC2u, -128, // 110x
0xE1u, // 1110
0xF1u),
off1_hibits);
__m128i initial_under = _mm_cmpgt_epi8(initial_mins, off1_current_bytes);
__m128i second_mins = _mm_shuffle_epi8(
_mm_setr_epi8(-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, // 10xx => false
127, 127, // 110x => true
0xA0u, // 1110
0x90u),
off1_hibits);
__m128i second_under = _mm_cmpgt_epi8(second_mins, current_bytes);
this->add_errors( _mm_and_si128(initial_under, second_under) );
}
really_inline void count_nibbles(__m128i bytes, struct processed_utf_bytes *answer) {
answer->raw_bytes = bytes;
answer->high_nibbles = _mm_and_si128(_mm_srli_epi16(bytes, 4), _mm_set1_epi8(0x0F));
}
// check whether the current bytes are valid UTF-8
// at the end of the function, previous gets updated
really_inline void check_utf8_bytes(__m128i current_bytes) {
struct processed_utf_bytes pb;
this->count_nibbles(current_bytes, &pb);
this->check_smaller_than_0xF4(current_bytes);
__m128i initial_lengths = this->continuation_lengths(pb.high_nibbles);
pb.carried_continuations = this->carry_continuations(initial_lengths);
this->check_continuations(initial_lengths, pb.carried_continuations);
__m128i off1_current_bytes =
_mm_alignr_epi8(pb.raw_bytes, this->previous.raw_bytes, 16 - 1);
this->check_first_continuation_max(current_bytes, off1_current_bytes);
this->check_overlong(current_bytes, off1_current_bytes, pb.high_nibbles);
this->previous = pb;
}
really_inline void check_carried_continuations() {
this->has_error = _mm_cmpgt_epi8(this->previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 1));
}
really_inline void check_next_input(__m128i in) {
__m128i high_bit = _mm_set1_epi8(0x80u);
if (_mm_testz_si128( in, high_bit) == 1) {
// it is ascii, we just check continuations
this->check_carried_continuations();
} else {
// it is not ascii so we have to do heavy work
this->check_utf8_bytes(in);
}
}
really_inline void check_next_input(simd_input in) {
__m128i high_bit = _mm_set1_epi8(0x80u);
__m128i any_bits_on = in.reduce([&](auto a, auto b) {
return _mm_or_si128(a, b);
});
if ((_mm_testz_si128( any_bits_on, high_bit)) == 1) {
// it is ascii, we just check continuation
this->has_error =
_mm_or_si128(_mm_cmpgt_epi8(this->previous.carried_continuations,
_mm_setr_epi8(9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 1)),
this->has_error);
if (_mm_testz_si128(any_bits_on, high_bit) == 1) {
// it is ascii, we just check continuations
this->check_carried_continuations();
} else {
// it is not ascii so we have to do heavy work
in.each([&](auto _in) {
this->previous = check_utf8_bytes(_in, &(this->previous), &(this->has_error));
});
in.each([&](auto _in) { this->check_utf8_bytes(_in); });
}
}
really_inline ErrorValues errors() {
return _mm_testz_si128(this->has_error, this->has_error) == 0
? simdjson::UTF8_ERROR
: simdjson::SUCCESS;
return _mm_testz_si128(this->has_error, this->has_error) == 0 ? simdjson::UTF8_ERROR : simdjson::SUCCESS;
}
}; // struct utf8_checker
} // namespace simdjson
} // namespace simdjson::westmere
UNTARGET_REGION // westmere
#endif // IS_X86_64
+1 -2
View File
@@ -5,7 +5,6 @@
#ifdef IS_X86_64
#include "westmere/architecture.h"
#include "westmere/simd_input.h"
#include "westmere/simdutf8check.h"
#include "simdjson/stage1_find_marks.h"
@@ -19,7 +18,7 @@ really_inline uint64_t compute_quote_mask(const uint64_t quote_bits) {
}
really_inline void find_whitespace_and_operators(
const simd_input<ARCHITECTURE> in,
const simd_input in,
uint64_t &whitespace, uint64_t &op) {
const __m128i operator_table =
-1
View File
@@ -6,7 +6,6 @@
#ifdef IS_X86_64
#include "simdjson/stage2_build_tape.h"
#include "westmere/architecture.h"
#include "westmere/stringparsing.h"
TARGET_WESTMERE
+16 -3
View File
@@ -1,15 +1,28 @@
#ifndef SIMDJSON_WESTMERE_STRINGPARSING_H
#define SIMDJSON_WESTMERE_STRINGPARSING_H
#include "../stringparsing.h"
#ifdef IS_X86_64
#include "westmere/architecture.h"
#include "simdjson/common_defs.h"
#include "simdjson/jsoncharutils.h"
#include "simdjson/parsedjson.h"
#ifdef JSON_TEST_STRINGS
void found_string(const uint8_t *buf, const uint8_t *parsed_begin,
const uint8_t *parsed_end);
void found_bad_string(const uint8_t *buf);
#endif
TARGET_WESTMERE
namespace simdjson::westmere {
// Holds backslashes and quotes locations.
struct parse_string_helper {
uint32_t bs_bits;
uint32_t quote_bits;
really_inline uint32_t bytes_processed() const { return sizeof(__m128i); }
};
really_inline parse_string_helper find_bs_bits_and_quote_bits(const uint8_t *src, uint8_t *dst) {
// this can read up to 31 bytes beyond the buffer size, but we require
// SIMDJSON_PADDING of padding