mirror of
https://github.com/simdjson/simdjson
synced 2026-06-08 17:27:07 +00:00
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:
+107
-58
@@ -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 ..
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
File diff suppressed because it is too large
Load Diff
+132
-56
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#ifdef IS_ARM64
|
||||
|
||||
#include "simdjson/stage2_build_tape.h"
|
||||
#include "arm64/architecture.h"
|
||||
#include "arm64/stringparsing.h"
|
||||
|
||||
namespace simdjson::arm64 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#ifdef IS_X86_64
|
||||
|
||||
#include "simdjson/stage2_build_tape.h"
|
||||
#include "haswell/architecture.h"
|
||||
#include "haswell/stringparsing.h"
|
||||
|
||||
TARGET_HASWELL
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#ifdef IS_X86_64
|
||||
|
||||
#include "simdjson/stage2_build_tape.h"
|
||||
#include "westmere/architecture.h"
|
||||
#include "westmere/stringparsing.h"
|
||||
|
||||
TARGET_WESTMERE
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user