Compare commits

...

17 Commits

Author SHA1 Message Date
Francisco Geiman Thiesen 9fb26db309 Fixing typo 2024-11-27 12:06:42 -08:00
Francisco Geiman Thiesen 058907cffe Attempt at fixing failing serialization tests. (#2292) 2024-11-27 09:00:20 -05:00
Daniel Lemire 6c0b2a4af2 Merge branch 'json_builder_init' of https://github.com/simdjson/simdjson into json_builder_init 2024-11-26 18:27:19 -05:00
Daniel Lemire a0fb1400b8 another missing store 2024-11-26 18:26:56 -05:00
Daniel Lemire c5174b2213 missing store 2024-11-26 18:20:22 -05:00
Daniel Lemire 94214d1a9a minor fix 2024-11-25 21:12:13 -05:00
Daniel Lemire db5323a972 tweaking the approach 2024-11-25 21:10:25 -05:00
Daniel Lemire a14b63e654 fix typo 2024-11-22 19:43:14 -05:00
Daniel Lemire e32b2a744f tweaking 2024-11-22 19:35:12 -05:00
Daniel Lemire 19ebe94962 adding tests. we still specialized write_string_escaped 2024-11-21 16:34:12 -05:00
Daniel Lemire c61063ebfc dropping vs arm (missing support) 2024-11-21 14:27:21 -05:00
Daniel Lemire 19214fdd8e minor edits 2024-11-20 20:24:27 -05:00
Daniel Lemire b297927fe7 update 2024-11-05 17:36:30 -05:00
Daniel Lemire a309515c2f more later 2024-10-31 19:56:45 -04:00
Daniel Lemire ec03d814e7 tweak 2024-10-31 16:31:09 -04:00
Daniel Lemire 15434adff2 moving the files back to ondemand for now. 2024-10-30 01:10:00 -04:00
Daniel Lemire 6b3e55fc92 Initial work on JSON builder 2024-10-28 20:27:26 -04:00
42 changed files with 2849 additions and 33 deletions
-1
View File
@@ -10,7 +10,6 @@ jobs:
fail-fast: false
matrix:
include:
- {arch: ARM}
- {arch: ARM64}
- {arch: ARM64EC}
steps:
+1
View File
@@ -23,6 +23,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace arm64
+13 -2
View File
@@ -134,6 +134,12 @@ namespace {
tmp = vpaddq_u8(tmp, tmp);
return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
simdjson_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
};
@@ -210,7 +216,7 @@ namespace {
// Bit-specific operations
simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return vtstq_u8(*this, bits); }
simdjson_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; }
simdjson_inline bool any_bits_set_anywhere() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
template<int N>
simdjson_inline simd8<uint8_t> shr() const { return vshrq_n_u8(*this, N); }
@@ -223,7 +229,12 @@ namespace {
return lookup_table.apply_lookup_16_to(*this);
}
// Returns 4-bit out of each byte, alternating between the high 4 bits and low
// bits result it is 64 bit.
simdjson_inline uint64_t to_bitmask64() const {
return vget_lane_u64(
vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
}
// Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
// Passing a 0 value for mask would be equivalent to writing out every byte to output.
// Only the first 16 - count_ones(mask) bytes of the result are significant but 16 bytes
@@ -46,6 +46,32 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits) / 4; }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask64()
};
}
} // unnamed namespace
} // namespace arm64
} // namespace simdjson
+2 -1
View File
@@ -49,7 +49,8 @@ enum error_code {
SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value.
OUT_OF_BOUNDS, ///< Attempted to access location outside of document.
TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input
NUM_ERROR_CODES
OUT_OF_CAPACITY, ///< The capacity was exceeded, we cannot allocate enough memory.
NUM_ERROR_CODES ///< Placeholder for end of error code list.
};
/**
@@ -26,6 +26,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace fallback
@@ -29,6 +29,24 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
return { src[0] };
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 1;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits; }
simdjson_inline int escape_index() { return 0; }
bool escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
dst[0] = src[0];
return { (src[0] == '\\') || (src[0] == '"') || (src[0] < 32) };
}
} // unnamed namespace
} // namespace fallback
} // namespace simdjson
@@ -45,4 +45,8 @@
#include "simdjson/generic/ondemand/token_iterator-inl.h"
#include "simdjson/generic/ondemand/value_iterator-inl.h"
// JSON builder, ideally they should not be part of the ondemand directory
// but it is convenient for now to have them here.
#include "simdjson/generic/ondemand/json_string_builder.h"
#include "simdjson/generic/ondemand/json_string_builder-inl.h"
@@ -0,0 +1,281 @@
/**
* This file is part of the builder API. It is temporarily in the ondemand directory
* but we will move it to a builder directory later.
*/
#include <type_traits>
#ifndef SIMDJSON_GENERIC_BUILDER_INL_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_BUILDER_INL_H
#include "simdjson/generic/builder/json_string_builder.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
simdjson_inline string_builder::string_builder(size_t initial_capacity) :
buffer(new (std::nothrow) char[initial_capacity]),
position(0), capacity(buffer.get() != nullptr ? initial_capacity : 0),
is_valid(buffer.get() != nullptr) {}
simdjson_inline bool string_builder::capacity_check(size_t upcoming_bytes) {
// We use the convention that when is_valid is false, then the capacity and
// the position are 0.
// Most of the time, this function will return true.
if (simdjson_likely(upcoming_bytes <= capacity - position)) { return true; }
// check for overflow, most of the time there is no overflow
if (simdjson_likely(position + upcoming_bytes < position)) { return false; }
// We will rarely get here.
grow_buffer((std::max)(capacity * 2, position + upcoming_bytes));
// If the buffer allocation failed, we set is_valid to false.
return is_valid;
}
simdjson_inline void string_builder::grow_buffer(size_t desired_capacity) {
if (!is_valid) { return; }
std::unique_ptr<char[]> new_buffer(new (std::nothrow) char[desired_capacity]);
if (new_buffer.get() == nullptr) {
set_valid(false);
return;
}
std::memcpy(new_buffer.get(), buffer.get(), position);
buffer.swap(new_buffer);
capacity = desired_capacity;
}
simdjson_inline void string_builder::set_valid(bool valid) noexcept {
if(!valid) {
is_valid = false;
capacity = 0;
position = 0;
buffer.reset();
} else {
is_valid = true;
}
}
simdjson_inline size_t string_builder::size() const noexcept {
return position;
}
simdjson_inline void string_builder::append(char c) noexcept {
if(capacity_check(1)) {
buffer.get()[position++] = c;
}
}
simdjson_inline void string_builder::append_null() noexcept {
constexpr char null_literal[] = "null";
constexpr size_t null_len = sizeof(null_literal) - 1;
if(capacity_check(null_len)) {
std::memcpy(buffer.get() + position, null_literal, null_len);
position += null_len;
}
}
simdjson_inline void string_builder::clear() noexcept {
position = 0;
// if it was invalid, we should try to repair it
if(!is_valid) {
capacity = 0;
buffer.reset();
is_valid = true;
}
}
namespace internal {
// We could specialize further for 32-bit integers.
int int_log2(uint32_t x) { return (63 - leading_zeroes(x | 1)); }
int fast_digit_count_32(uint32_t x) {
static uint64_t table[] = {
4294967296, 8589934582, 8589934582, 8589934582, 12884901788,
12884901788, 12884901788, 17179868184, 17179868184, 17179868184,
21474826480, 21474826480, 21474826480, 21474826480, 25769703776,
25769703776, 25769703776, 30063771072, 30063771072, 30063771072,
34349738368, 34349738368, 34349738368, 34349738368, 38554705664,
38554705664, 38554705664, 41949672960, 41949672960, 41949672960,
42949672960, 42949672960};
return uint32_t((x + table[int_log2(x)]) >> 32);
}
int int_log2(uint64_t x) { return 63 - leading_zeroes(x | 1); }
int digit_count_64(uint64_t x) {
static uint64_t table[] = {9,
99,
999,
9999,
99999,
999999,
9999999,
99999999,
999999999,
9999999999,
99999999999,
999999999999,
9999999999999,
99999999999999,
999999999999999ULL,
9999999999999999ULL,
99999999999999999ULL,
999999999999999999ULL,
9999999999999999999ULL};
int y = (19 * int_log2(x) >> 6);
y += x > table[y];
return y + 1;
}
template<typename number_type,
typename = typename std::enable_if<std::is_unsigned<number_type>::value>::type>
simdjson_inline size_t digit_count(number_type v) noexcept {
static_assert(sizeof(number_type) == 8
|| sizeof(number_type) == 4
|| sizeof(number_type) == 2
|| sizeof(number_type) == 1, "We only support 8-bit, 16-bit, 32-bit and 64-bit numbers");
if (sizeof(number_type) <= 4) {
return fast_digit_count_32(v);
} else {
return digit_count_64(v);
}
}
} // internal
template<typename number_type, typename>
simdjson_inline void string_builder::append(number_type v) noexcept {
static_assert(std::is_same<number_type, bool>::value
|| std::is_integral<number_type>::value || std::is_floating_point<number_type>::value, "Unsupported number type");
// If C++17 is available, we can 'if constexpr' here.
if constexpr (std::is_same<number_type, bool>::value) {
if (v) {
constexpr char true_literal[] = "true";
constexpr size_t true_len = sizeof(true_literal) - 1;
if(capacity_check(true_len)) {
std::memcpy(buffer.get() + position, true_literal, true_len);
position += true_len;
}
} else {
constexpr char false_literal[] = "false";
constexpr size_t false_len = sizeof(false_literal) - 1;
if(capacity_check(false_len)) {
std::memcpy(buffer.get() + position, false_literal, false_len);
position += false_len;
}
}
} else if constexpr (std::is_unsigned<number_type>::value) {
constexpr size_t max_number_size = 20;
if(capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
unsigned_type pv = static_cast<unsigned_type>(v);
size_t dc = internal::digit_count(pv);
char *write_pointer = buffer.get() + position + dc - 1;
// optimization opportunity: if v is large, we can do better.
while(pv >= 10) {
*write_pointer-- = char('0' + (pv % 10));
pv /= 10;
}
*write_pointer = char('0' + pv);
position += dc;
}
} else if constexpr (std::is_integral<number_type>::value) {
constexpr size_t max_number_size = 20;
if(capacity_check(max_number_size)) {
using unsigned_type = typename std::make_unsigned<number_type>::type;
bool negative = v < 0;
unsigned_type pv = static_cast<unsigned_type>(negative ? -v : v);
size_t dc = internal::digit_count(pv);
if(negative) {
buffer.get()[position++] = '-';
}
char *write_pointer = buffer.get() + position + dc - 1;
// optimization opportunity: if v is large, we can do better.
while(pv >= 10) {
*write_pointer-- = char('0' + (pv % 10));
pv /= 10;
}
*write_pointer = char('0' + pv);
position += dc;
}
} else if constexpr (std::is_floating_point<number_type>::value) {
constexpr size_t max_number_size = 24;
if(capacity_check(max_number_size)) {
// We could specialize for float.
char *end = simdjson::internal::to_chars(buffer.get() + position, nullptr, double(v));
position = end - buffer.get();
}
}
}
simdjson_inline void string_builder::escape_and_append(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if(capacity_check(6 * input.size())) {
position += simdjson::write_string_escaped(input, buffer.get() + position);
}
}
simdjson_inline void string_builder::escape_and_append_with_quotes(std::string_view input) noexcept {
// escaping might turn a control character into \x00xx so 6 characters.
if(capacity_check(2 + 6 * input.size())) {
buffer.get()[position++] = '"';
position += simdjson::write_string_escaped(input, buffer.get() + position);
buffer.get()[position++] = '"';
}
}
simdjson_inline void string_builder::append_raw(const char *c) noexcept {
if(capacity_check(1)) {
buffer.get()[position++] = *c;
}
}
simdjson_inline void string_builder::append_raw(std::string_view input) noexcept {
if(capacity_check(input.size())) {
std::memcpy(buffer.get() + position, input.data(), input.size());
position += input.size();
}
}
simdjson_inline void string_builder::append_raw(const char *str, size_t len) noexcept {
if(capacity_check(len)) {
std::memcpy(buffer.get() + position, str, len);
position += len;
}
}
#if SIMDJSON_EXCEPTIONS
simdjson_inline string_builder::operator std::string() const noexcept(false) {
return std::string(std::string_view());
}
simdjson_inline string_builder::operator std::string_view() const noexcept(false) {
return view();
}
#endif
simdjson_inline simdjson_result<std::string_view> string_builder::view() const noexcept {
if (!is_valid) { return simdjson::OUT_OF_CAPACITY; }
return std::string_view(buffer.get(), position);
}
simdjson_inline simdjson_result<const char *> string_builder::c_str() noexcept {
if(capacity_check(1)) {
buffer.get()[position] = '\0';
return buffer.get();
}
return simdjson::OUT_OF_CAPACITY;
}
simdjson_inline bool string_builder::validate_unicode() const noexcept {
return simdjson::validate_utf8(buffer.get(), position);
}
} // namespace builder
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BUILDER_INL_H
@@ -0,0 +1,167 @@
/**
* This file is part of the builder API. It is temporarily in the ondemand directory
* but we will move it to a builder directory later.
*/
#ifndef SIMDJSON_GENERIC_BUILDER_H
#ifndef SIMDJSON_CONDITIONAL_INCLUDE
#define SIMDJSON_GENERIC_BUILDER_H
#include "simdjson/generic/implementation_simdjson_result_base.h"
#endif // SIMDJSON_CONDITIONAL_INCLUDE
namespace simdjson {
namespace SIMDJSON_IMPLEMENTATION {
namespace builder {
/**
* A builder for JSON strings representing documents. This is a low-level
* builder that is not meant to be used directly by end-users. Though it
* supports atomic types (Booleans, strings), it does not support composed
* types (arrays and objects).
*
* Ultimately, this class should support kernel-specific optimizations. E.g.,
* it may make use of SIMD instructions to escape strings faster.
*/
class string_builder {
public:
simdjson_inline string_builder(size_t initial_capacity = 1024);
/**
* Append number (includes Booleans). Booleans are mapped to the strings
* false and true. Numbers are converted to strings abiding by the JSON standard.
* Floating-point numbers are converted to the shortest string that 'correctly'
* represents the number.
*/
template<typename number_type,
typename = typename std::enable_if<std::is_arithmetic<number_type>::value>::type>
simdjson_inline void append(number_type v) noexcept;
/**
* Append character c.
*/
simdjson_inline void append(char c) noexcept;
/**
* Append the string 'null'.
*/
simdjson_inline void append_null() noexcept;
/**
* Clear the content.
*/
simdjson_inline void clear() noexcept;
/**
* Append the std::string_view, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append(std::string_view input) noexcept;
/**
* Append the std::string_view surrounded by double quotes, after escaping it.
* There is no UTF-8 validation.
*/
simdjson_inline void escape_and_append_with_quotes(std::string_view input) noexcept;
/**
* Append the C string directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *c) noexcept;
/**
* Append the std::string_view directly, without escaping.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(std::string_view input) noexcept;
/**
* Append len characters from str.
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.
* Throws if memory allocation failed
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string() const noexcept(false);
/**
* Creates an std::string_view from the written JSON buffer.
* Throws if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content if needed.
*/
simdjson_inline operator std::string_view() const noexcept(false);
#endif
/**
* Returns a view on the written JSON buffer. Returns an error
* if memory allocation failed.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<std::string_view> view() const noexcept;
/**
* Appends the null character to the buffer and returns
* a pointer to the beginning of the written JSON buffer.
* Returns an error if memory allocation failed.
* The result is null-terminated.
*
* The result may not be valid UTF-8 if some of your content was not valid UTF-8.
* Use validate_unicode() to check the content.
*/
simdjson_inline simdjson_result<const char *> c_str() noexcept;
/**
* Return true if the content is valid UTF-8.
*/
simdjson_inline bool validate_unicode() const noexcept;
/**
* Returns the current size of the written JSON buffer.
* If an error occurred, returns 0.
*/
simdjson_inline size_t size() const noexcept;
private:
/**
* Returns true if we can write at least upcoming_bytes bytes.
* The underlying buffer is reallocated if needed. It is designed
* to be called before writing to the buffer. It should be fast.
*/
simdjson_inline bool capacity_check(size_t upcoming_bytes);
/**
* Grow the buffer to at least desired_capacity bytes.
* If the allocation fails, is_valid is set to false. We expect
* that this function would not be repeatedly called.
*/
simdjson_inline void grow_buffer(size_t desired_capacity);
/**
* We use this helper function to make sure that is_valid is kept consistent.
*/
simdjson_inline void set_valid(bool valid) noexcept;
std::unique_ptr<char[]> buffer{};
size_t position{0};
size_t capacity{0};
bool is_valid{true};
};
}
}
} // namespace simdjson
#endif // SIMDJSON_GENERIC_BUILDER_H
@@ -28,6 +28,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace haswell
@@ -41,6 +41,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 32;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace haswell
} // namespace simdjson
@@ -28,6 +28,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace icelake
-1
View File
@@ -77,7 +77,6 @@ namespace simd {
friend simdjson_really_inline uint64_t operator==(const simd8<T> lhs, const simd8<T> rhs) {
return _mm512_cmpeq_epi8_mask(lhs, rhs);
}
static const int SIZE = sizeof(base<T>::value);
template<int N=1>
@@ -41,6 +41,35 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 64;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(uint64_t(escape_bits)); }
__mmask64 escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
__mmask64 is_quote = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('"'));
__mmask64 is_backslash = _mm512_cmpeq_epi8_mask(v, _mm512_set1_epi8('\\'));
__mmask64 is_control = _mm512_cmplt_epi8_mask(v, _mm512_set1_epi8(32));
return {
(is_backslash | is_quote | is_control)
};
}
} // unnamed namespace
} // namespace icelake
} // namespace simdjson
+17
View File
@@ -26,6 +26,15 @@ simdjson_inline simdjson_warn_unused bool validate_utf8(const std::string_view s
return validate_utf8(sv.data(), sv.size());
}
/**
* Write the string to the output buffer while escaping double-quote, backlash and ascii control characters.
*
* @param input the string_view to escape
* @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes.
* @return number of bytes written
*/
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept;
/**
* Validate the UTF-8 string.
*
@@ -127,6 +136,14 @@ public:
*/
simdjson_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0;
/**
* Write the string to the output buffer while escaping double-quote, backlash and ascii control characters.
*
* @param input the string_view to escape
* @param out output buffer (for escaped string): to be safe, it should have 6 * input.size() allocated bytes.
* @return number of bytes written
*/
simdjson_warn_unused virtual size_t write_string_escaped(const std::string_view input, char *out) const noexcept = 0;
protected:
/** @private Construct an implementation with the given name and description. For subclasses. */
simdjson_inline implementation(
+1
View File
@@ -23,6 +23,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace lasx
@@ -40,6 +40,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask()
};
}
} // unnamed namespace
} // namespace lasx
} // namespace simdjson
+1
View File
@@ -23,6 +23,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace lsx
+25
View File
@@ -46,6 +46,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask()
};
}
} // unnamed namespace
} // namespace lsx
} // namespace simdjson
+4
View File
@@ -8,6 +8,10 @@ namespace simdjson {
* @copydoc simdjson::builtin::ondemand
*/
namespace ondemand = builtin::ondemand;
/**
* @copydoc simdjson::builtin::builder
*/
namespace builder = builtin::builder;
} // namespace simdjson
#endif // SIMDJSON_ONDEMAND_H
+1
View File
@@ -32,6 +32,7 @@ public:
size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf,
size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace ppc64
@@ -58,6 +58,31 @@ backslash_and_quote::copy_and_find(const uint8_t *src, uint8_t *dst) {
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
(is_backslash | is_quote | is_control).to_bitmask()
};
}
} // unnamed namespace
} // namespace ppc64
} // namespace simdjson
@@ -24,6 +24,7 @@ public:
) const noexcept final;
simdjson_warn_unused error_code minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept final;
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final;
};
} // namespace westmere
@@ -40,6 +40,31 @@ simdjson_inline backslash_and_quote backslash_and_quote::copy_and_find(const uin
};
}
struct escaping {
static constexpr uint32_t BYTES_PROCESSED = 16;
simdjson_inline static escaping copy_and_find(const uint8_t *src, uint8_t *dst);
simdjson_inline bool has_escape() { return escape_bits != 0; }
simdjson_inline int escape_index() { return trailing_zeroes(escape_bits); }
uint64_t escape_bits;
}; // struct escaping
simdjson_inline escaping escaping::copy_and_find(const uint8_t *src, uint8_t *dst) {
static_assert(SIMDJSON_PADDING >= (BYTES_PROCESSED - 1), "escaping finder must process fewer than SIMDJSON_PADDING bytes");
simd8<uint8_t> v(src);
v.store(dst);
simd8<bool> is_quote = (v == '"');
simd8<bool> is_backslash = (v == '\\');
simd8<bool> is_control = (v < 32);
return {
uint64_t((is_backslash | is_quote | is_control).to_bitmask())
};
}
} // unnamed namespace
} // namespace westmere
} // namespace simdjson
+63 -16
View File
@@ -10,12 +10,41 @@ import os
import re
import shutil
import datetime
import json
from typing import Dict, List, Optional, Set, TextIO, Union, cast
# Check for Python 3, this does not actually work.
if sys.version_info < (3, 0):
sys.stdout.write("Sorry, requires Python 3.x or better\n")
sys.exit(1)
rules = """
We refer your to the HACKING.md file for more information on how the project is organized.
To help understand the error, here are the rules for including files in simdjson:
All implementation-specific files, including arm64.h, arm64/implementation.h and
arm64/ondemand.h, must be within SIMDJSON_CONDITIONAL_INCLUDE blocks.
Top-level headers must not be included in any SIMDJSON_CONDITIONAL_INCLUDE block.
Generic files must be included only in amalgamator files (arm64.h,
arm64/implementation.h, arm64/ondemand.h, generic/amalgamated.h).
We fail if an implementation-specific file is included more than once in the same block.
We fail if a generic file is included more than once per implementation in the same block.
Tip: generally, "file" will search the including file's source directory first, then
the search paths while <file> does it the other way around.
We prefer to use <> in simdjson headers to avoid accidentally including a file from the
wrong directory.
The amalgamate.py script checks that all files are included.
"""
SCRIPTPATH = os.path.dirname(os.path.abspath(sys.argv[0]))
PROJECTPATH = os.path.dirname(SCRIPTPATH)
print(f"SCRIPTPATH={SCRIPTPATH} PROJECTPATH={PROJECTPATH}")
@@ -62,6 +91,22 @@ class SimdjsonFile:
def __str__(self):
return self.include_path
def dump(self):
return {
'root': self.root,
'include_path': self.include_path,
'includes': [include.include_path for include in self.includes],
'included_from': [included_from.include_path for included_from in self.included_from],
'editor_only_includes': [editor_only_include.include_path for editor_only_include in self.editor_only_includes],
'editor_only_included_from': [editor_only_included_from.include_path for editor_only_included_from in self.editor_only_included_from],
'processed': self.processed,
'dependency_file': self.dependency_file.include_path if self.dependency_file else None,
'is_amalgamator': self.is_amalgamator,
'implementation': self.implementation,
}
def json(self):
return json.dumps(self.dump(), indent=4, sort_keys=True, ensure_ascii=False)
def __repr__(self):
return self.include_path
@@ -162,20 +207,21 @@ class SimdjsonFile:
def add_include(self, include: 'SimdjsonFile'):
if self.is_conditional_include:
assert include.is_conditional_include, f"{self} cannot include {include} without #ifndef SIMDJSON_CONDITIONAL_INCLUDE."
# If I have a dependency file, I can only include something that has a dependency file.
assert include.is_conditional_include, f"{self} cannot include {include} without #ifndef SIMDJSON_CONDITIONAL_INCLUDE. {rules}"
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
# if include.amalgamator_file:
# assert include.amalgamator_file == self, f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
else:
assert include.is_amalgamator or not include.is_conditional_include, f"{self} cannot include {include} because it is an amalgamated file."
assert include.is_amalgamator or not include.is_conditional_include, f"{self} cannot include {include} because it is an amalgamated file. {rules}"
self.includes.append(include)
include.included_from.add(self)
def add_editor_only_include(self, include: 'SimdjsonFile'):
assert self.is_conditional_include, f"Cannot use #ifndef SIMDJSON_CONDITIONAL_INCLUDE in {self} because it is not an amalgamated file."
assert self.is_conditional_include, f"Cannot use #ifndef SIMDJSON_CONDITIONAL_INCLUDE in {self} because it is not an amalgamated file. {rules}"
if not include.is_conditional_include:
assert self.dependency_file, f"{self} cannot include {include} without #ifndef SIMDJSON_CONDITIONAL_INCLUDE."
assert self.dependency_file, f"{self} cannot include {include} without #ifndef SIMDJSON_CONDITIONAL_INCLUDE. {rules}"
# TODO make sure we only include amalgamated files that are guaranteed to be included with us (or before us)
# elif include.amalgamator_file:
# assert self.is_amalgamated_before(self.amalgamator_file), f"{self} cannot include {include}: it should be included from {include.amalgamator_file} instead."
@@ -190,11 +236,11 @@ class SimdjsonFile:
if file.dependency_file == self:
for editor_only_include in file.editor_only_includes:
if not editor_only_include.is_conditional_include:
assert editor_only_include in self.includes, f"{file} includes {editor_only_include}, but it is not included from {self}. It must be added to {self}."
assert editor_only_include in self.includes, f"{file} includes {editor_only_include}, but it is not included from {self}. It must be added to {self}. {rules}"
if editor_only_include in extra_include_set:
extra_include_set.remove(editor_only_include)
assert len(extra_include_set) == 0, f"{self} unnecessarily includes {extra_include_set}. They are not included in the corresponding amalgamated files."
assert len(extra_include_set) == 0, f"{self} unnecessarily includes {extra_include_set}. They are not included in the corresponding amalgamated files. {rules}"
class SimdjsonRepository:
def __init__(self, project_path: str, relative_roots: List[RelativeRoot]):
@@ -320,6 +366,7 @@ class Amalgamator:
assert not self.editor_only_region
with open(file.absolute_path, 'r') as fid2:
print(f"including: {file}")
for line in fid2:
line = line.rstrip('\n')
@@ -329,9 +376,9 @@ class Amalgamator:
# Ignore lines inside #ifndef SIMDJSON_CONDITIONAL_INCLUDE
if re.search(r'^#ifndef\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line):
assert file.is_conditional_include, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE but is not an amalgamated file!"
assert self.in_conditional_include_block, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE without a prior #define SIMDJSON_CONDITIONAL_INCLUDE: {self.include_stack}"
assert not self.editor_only_region, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE twice in a row"
assert file.is_conditional_include, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE but is not an amalgamated file! {rules}"
assert self.in_conditional_include_block, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE without a prior #define SIMDJSON_CONDITIONAL_INCLUDE: {self.include_stack} {rules}"
assert not self.editor_only_region, f"{file} uses #ifndef SIMDJSON_CONDITIONAL_INCLUDE twice in a row {rules}"
self.editor_only_region = True
# Handle ignored lines (and ending ignore blocks)
@@ -349,7 +396,7 @@ class Amalgamator:
self.editor_only_region = False
continue
assert not end_ignore, f"{file} has #endif // SIMDJSON_CONDITIONAL_INCLUDE without #ifndef SIMDJSON_CONDITIONAL_INCLUDE"
assert not end_ignore, f"{file} has #endif // SIMDJSON_CONDITIONAL_INCLUDE without #ifndef SIMDJSON_CONDITIONAL_INCLUDE {rules}"
# Handle #include lines
included = re.search(r'^#include\s+["<]([^">]*)[">]', line)
@@ -376,26 +423,26 @@ class Amalgamator:
self.implementation = None
elif re.search(r'\bSIMDJSON_IMPLEMENTATION\b', line) and file.include_path != IMPLEMENTATION_DETECTION_H:
# copy the line, with SIMDJSON_IMPLEMENTATION replace to what it is currently defined to
assert self.implementation, f"Use of SIMDJSON_IMPLEMENTATION while not defined in {file}: {line}"
assert self.implementation, f"Use of SIMDJSON_IMPLEMENTATION while not defined in {file}: {line}\n{rules}"
line = re.sub(r'\bSIMDJSON_IMPLEMENTATION\b',self.implementation,line)
# Handle defining and undefining SIMDJSON_CONDITIONAL_INCLUDE
defined = re.search(r'^#define\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line)
if defined:
assert not file.is_conditional_include, "SIMDJSON_CONDITIONAL_INCLUDE defined in amalgamated file {file}! Not allowed."
assert not self.in_conditional_include_block, f"{file} redefines SIMDJSON_CONDITIONAL_INCLUDE"
assert not file.is_conditional_include, "SIMDJSON_CONDITIONAL_INCLUDE defined in amalgamated file {file}! Not allowed. {rules}"
assert not self.in_conditional_include_block, f"{file} redefines SIMDJSON_CONDITIONAL_INCLUDE {rules}"
self.in_conditional_include_block = True
self.found_includes_per_conditional_block.clear()
self.write(f'/* defining SIMDJSON_CONDITIONAL_INCLUDE */')
elif re.search(r'^#undef\s+SIMDJSON_CONDITIONAL_INCLUDE\s*$', line):
assert not file.is_conditional_include, "SIMDJSON_CONDITIONAL_INCLUDE undefined in amalgamated file {file}! Not allowed."
assert self.in_conditional_include_block, f"{file} undefines SIMDJSON_CONDITIONAL_INCLUDE without defining it"
assert not file.is_conditional_include, "SIMDJSON_CONDITIONAL_INCLUDE undefined in amalgamated file {file}! Not allowed. {rules}"
assert self.in_conditional_include_block, f"{file} undefines SIMDJSON_CONDITIONAL_INCLUDE without defining it {rules}"
self.write(f'/* undefining SIMDJSON_CONDITIONAL_INCLUDE */')
self.in_conditional_include_block = False
self.write(line)
assert not self.editor_only_region, f"{file} ended without #endif // SIMDJSON_CONDITIONAL_INCLUDE"
assert not self.editor_only_region, f"{file} ended without #endif // SIMDJSON_CONDITIONAL_INCLUDE {rules}"
self.write(f"/* end file {self.file_to_str(file)} */")
+3 -2
View File
@@ -1,4 +1,4 @@
/* auto-generated on 2024-10-10 22:17:29 -0400. Do not edit! */
/* auto-generated on 2024-10-28 20:27:26 -0400. Do not edit! */
/* including simdjson.cpp: */
/* begin file simdjson.cpp */
#define SIMDJSON_SRC_SIMDJSON_CPP
@@ -2443,7 +2443,8 @@ enum error_code {
SCALAR_DOCUMENT_AS_VALUE, ///< A scalar document is treated as a value.
OUT_OF_BOUNDS, ///< Attempted to access location outside of document.
TRAILING_CONTENT, ///< Unexpected trailing content in the JSON input
NUM_ERROR_CODES
OUT_OF_CAPACITY, ///< The capacity was exceeded, we cannot allocate enough memory.
NUM_ERROR_CODES ///< Placeholder for end of error code list.
};
/**
+1711 -10
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -164,6 +164,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return arm64::stringparsing::write_string_escaped(input, out);
}
} // namespace arm64
} // namespace simdjson
+4
View File
@@ -402,6 +402,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return fallback::stringparsing::write_string_escaped(input, out);
}
} // namespace fallback
} // namespace simdjson
+95
View File
@@ -236,6 +236,101 @@ simdjson_warn_unused simdjson_inline uint8_t *parse_wobbly_string(const uint8_t
}
}
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept {
// We are making the following assumption: most strings will either be very short or they will not
// need escaping.
size_t i = 0;
size_t pos = 0;
if(input.size() >= escaping::BYTES_PROCESSED) {
auto vec_processing = [input,out]() -> size_t {
size_t i = 0;
size_t pos = 0;
for(;input.size() - i >= escaping::BYTES_PROCESSED; i += escaping::BYTES_PROCESSED) {
escaping vinput = escaping::copy_and_find(reinterpret_cast<const uint8_t *>(input.data()) + i, reinterpret_cast<uint8_t *>(out) + pos);
if(vinput.has_escape()) {
return i + vinput.escape_index(); // We have a character that needs escaping
}
pos += escaping::BYTES_PROCESSED;
}
if(i == input.size()) { return input.size(); }
// We virtually backtrack so we can load a full vector register
i = input.size() - escaping::BYTES_PROCESSED;
pos = i;
escaping vinput = escaping::copy_and_find(reinterpret_cast<const uint8_t *>(input.data()) + i, reinterpret_cast<uint8_t *>(out) + pos);
if(vinput.has_escape()) {
return i + vinput.escape_index(); // We have a character that needs escaping
}
return input.size();
};
i = vec_processing();
pos = i;
if(i == input.size()) { return pos; }
// Here we only continue if there was a character that needed escaping.
}
static std::string_view control_chars[] = {
"\\x0000", "\\x0001", "\\x0002", "\\x0003", "\\x0004", "\\x0005", "\\x0006",
"\\x0007", "\\x0008", "\\t", "\\n", "\\x000b", "\\f", "\\r",
"\\x000e", "\\x000f", "\\x0010", "\\x0011", "\\x0012", "\\x0013", "\\x0014",
"\\x0015", "\\x0016", "\\x0017", "\\x0018", "\\x0019", "\\x001a", "\\x001b",
"\\x001c", "\\x001d", "\\x001e", "\\x001f"};
static std::array<uint8_t, 256> json_quotable_character =
[]() constexpr {
std::array<uint8_t, 256> result{};
for (int i = 0; i < 32; i++) {
result[i] = 1;
}
for (int i : {'"', '\\'}) {
result[i] = 1;
}
return result;
}();
// The rest could possibly be vectorized, but consider that we expect most strings
// to be short or not to require escaping.
for (; i < input.size(); i++) {
uint8_t c = static_cast<uint8_t>(input[i]);
if(json_quotable_character[c]) {
switch (c) {
case '"':
out[pos++] = '\\';
out[pos++] = '"';
break;
case '\\':
out[pos++] = '\\';
out[pos++] = '\\';
break;
case '\b':
out[pos++] = '\\';
out[pos++] = 'b';
break;
case '\f':
out[pos++] = '\\';
out[pos++] = 'f';
break;
case '\n':
out[pos++] = '\\';
out[pos++] = 'n';
break;
case '\r':
out[pos++] = '\\';
out[pos++] = 'r';
break;
case '\t':
out[pos++] = '\\';
out[pos++] = 't';
break;
default:
control_chars[c].copy(out + pos, 6);
pos += 6;
}
} else {
out[pos++] = c;
}
}
return pos;
}
} // namespace stringparsing
} // unnamed namespace
} // namespace SIMDJSON_IMPLEMENTATION
+4
View File
@@ -161,6 +161,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return haswell::stringparsing::write_string_escaped(input, out);
}
} // namespace SIMDJSON_IMPLEMENTATION
} // namespace simdjson
+4
View File
@@ -207,6 +207,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return icelake::stringparsing::write_string_escaped(input, out);
}
} // namespace icelake
} // namespace simdjson
+9
View File
@@ -186,6 +186,9 @@ public:
simdjson_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override {
return set_best()->validate_utf8(buf, len);
}
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final {
return set_best()->write_string_escaped(input, out);
}
simdjson_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {}
private:
const implementation *set_best() const noexcept;
@@ -236,6 +239,9 @@ public:
simdjson_warn_unused error_code minify(const uint8_t *, size_t, uint8_t *, size_t &) const noexcept final override {
return UNSUPPORTED_ARCHITECTURE;
}
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) const noexcept final {
return 0;
}
simdjson_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override {
return false; // Just refuse to validate. Given that we have a fallback implementation
// it seems unlikely that unsupported_implementation will ever be used. If it is used,
@@ -319,6 +325,9 @@ simdjson_warn_unused error_code minify(const char *buf, size_t len, char *dst, s
simdjson_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept {
return get_active_implementation()->validate_utf8(buf, len);
}
simdjson_warn_unused size_t write_string_escaped(const std::string_view input, char *out) noexcept {
return get_active_implementation()->write_string_escaped(input, out);
}
const implementation * builtin_implementation() {
static const implementation * builtin_impl = get_available_implementations()[SIMDJSON_STRINGIFY(SIMDJSON_BUILTIN_IMPLEMENTATION)];
assert(builtin_impl);
+4
View File
@@ -124,6 +124,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return lasx::stringparsing::write_string_escaped(input, out);
}
} // namespace lasx
} // namespace simdjson
+4
View File
@@ -128,6 +128,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return lsx::stringparsing::write_string_escaped(input, out);
}
} // namespace lsx
} // namespace simdjson
+4
View File
@@ -134,6 +134,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return ppc64::stringparsing::write_string_escaped(input, out);
}
} // namespace ppc64
} // namespace simdjson
+4
View File
@@ -166,6 +166,10 @@ simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *
return stage2(_doc);
}
simdjson_warn_unused size_t implementation::write_string_escaped(const std::string_view input, char *out) const noexcept {
return westmere::stringparsing::write_string_escaped(input, out);
}
} // namespace westmere
} // namespace simdjson
+1
View File
@@ -26,3 +26,4 @@ endif()
add_cpp_test(checkimplementation LABELS other per_implementation)
add_subdirectory(compilation_failure_tests)
add_subdirectory(builder)
+12
View File
@@ -0,0 +1,12 @@
# All remaining tests link with simdjson proper
link_libraries(simdjson)
include_directories(..)
add_cpp_test(builder_string_builder_tests LABELS ondemand acceptance per_implementation)
# Copy the simdjson dll into the tests directory
if(MSVC AND BUILD_SHARED_LIBS)
add_custom_command(TARGET builder_string_builder_tests POST_BUILD # Adds a post-build event
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake -E copy_if_different..."
"$<TARGET_FILE:simdjson>" # <--this is in-file
"$<TARGET_FILE_DIR:builder_string_builder_tests>") # <--this is out-file path
endif(MSVC AND BUILD_SHARED_LIBS)
@@ -0,0 +1,178 @@
#include "simdjson.h"
#include "test_builder.h"
#include <string_view>
using namespace simdjson;
namespace builder_tests {
using namespace std;
#if SIMDJSON_EXCEPTIONS
bool string_convertion_except() {
TEST_START();
simdjson::ondemand::parser p;
simdjson::builder::string_builder sb;
sb.append('a');
std::string r(sb);
ASSERT_EQUAL(r, "a");
TEST_SUCCEED();
}
#endif
bool append_char() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append('a');
ASSERT_EQUAL(sb.size(), 1);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "a");
TEST_SUCCEED();
}
bool append_integer() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append(42);
ASSERT_EQUAL(sb.size(), 2);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "42");
TEST_SUCCEED();
}
bool append_float() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append(1.1);
ASSERT_EQUAL(sb.size(), 3);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "1.1");
TEST_SUCCEED();
}
bool append_null() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append_null();
ASSERT_EQUAL(sb.size(), 4);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "null");
TEST_SUCCEED();
}
bool clear() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append('a');
sb.clear();
ASSERT_EQUAL(sb.size(), 0);
TEST_SUCCEED();
}
bool escape_and_append() {
TEST_START();
simdjson::builder::string_builder sb;
sb.escape_and_append("Hello, \"world\"!");
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "Hello, \"world\"!");
TEST_SUCCEED();
}
bool escape_and_append_with_quotes() {
TEST_START();
simdjson::builder::string_builder sb;
sb.escape_and_append_with_quotes("Hello, \"world\"!");
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "\"Hello, \\\"world\\\"!\"");
TEST_SUCCEED();
}
bool append_raw() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append_raw("Test");
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "Test");
TEST_SUCCEED();
}
bool raw_with_length() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append_raw("Test String", 4);
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "Test");
TEST_SUCCEED();
}
bool string_convertion() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append('a');
std::string_view p;
auto result = sb.view().get(p);
ASSERT_EQUAL(result, SUCCESS);
ASSERT_EQUAL(p, "a");
TEST_SUCCEED();
}
bool unicode_validation() {
TEST_START();
simdjson::builder::string_builder sb;
sb.append('a');
ASSERT_TRUE(sb.validate_unicode());
TEST_SUCCEED();
}
bool buffer_growth() {
TEST_START();
simdjson::builder::string_builder sb;
for(int i = 0; i < 3; ++i) {
sb.append('a');
}
ASSERT_EQUAL(sb.size(), 3);
TEST_SUCCEED();
}
bool run() {
return
#if SIMDJSON_EXCEPTIONS
string_convertion_except() &&
#endif
append_char() &&
append_integer() &&
append_float() &&
append_null() &&
clear() &&
escape_and_append() &&
escape_and_append_with_quotes() &&
append_raw() &&
raw_with_length() &&
string_convertion() &&
buffer_growth() &&
unicode_validation() &&
true;
}
} // namespace twitter_tests
int main(int argc, char *argv[]) {
return test_main(argc, argv, builder_tests::run);
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef ONDEMAND_TEST_BUILDER_H
#define ONDEMAND_TEST_BUILDER_H
#include <iostream>
#include <unistd.h>
#include "simdjson.h"
#include "cast_tester.h"
#include "test_macros.h"
template<typename F>
int test_main(int argc, char *argv[], const F& test_function) {
std::cout << std::unitbuf;
int c;
while ((c = getopt(argc, argv, "a:")) != -1) {
switch (c) {
case 'a': {
const simdjson::implementation *impl = simdjson::get_available_implementations()[optarg];
if (!impl) {
std::fprintf(stderr, "Unsupported architecture value -a %s\n", optarg);
return EXIT_FAILURE;
}
simdjson::get_active_implementation() = impl;
break;
}
default:
std::fprintf(stderr, "Unexpected argument %c\n", c);
return EXIT_FAILURE;
}
}
// this is put here deliberately to check that the documentation is correct (README),
// should this fail to compile, you should update the documentation:
if (simdjson::get_active_implementation()->name() == "unsupported") {
std::printf("unsupported CPU\n");
std::abort();
}
// We want to know what we are testing.
std::cout << "builtin_implementation -- " << simdjson::builtin_implementation()->name() << std::endl;
std::cout << "------------------------------------------------------------" << std::endl;
std::cout << "Running tests." << std::endl;
if (test_function()) {
std::cout << "Success!" << std::endl;
return EXIT_SUCCESS;
} else {
std::cerr << "FAILED." << std::endl;
return EXIT_FAILURE;
}
}
#endif // ONDEMAND_TEST_BUILDER_H