1 | #include "simdjson/arm64/begin.h" |
2 | |
3 | // |
4 | // Stage 1 |
5 | // |
6 | namespace simdjson { |
7 | namespace SIMDJSON_IMPLEMENTATION { |
8 | namespace { |
9 | |
10 | using namespace simd; |
11 | |
12 | struct json_character_block { |
13 | static simdjson_inline json_character_block classify(const simd::simd8x64<uint8_t>& in); |
14 | |
15 | simdjson_inline uint64_t whitespace() const noexcept { return _whitespace; } |
16 | simdjson_inline uint64_t op() const noexcept { return _op; } |
17 | simdjson_inline uint64_t scalar() const noexcept { return ~(op() | whitespace()); } |
18 | |
19 | uint64_t _whitespace; |
20 | uint64_t _op; |
21 | }; |
22 | |
23 | simdjson_inline json_character_block json_character_block::classify(const simd::simd8x64<uint8_t>& in) { |
24 | // Functional programming causes trouble with Visual Studio. |
25 | // Keeping this version in comments since it is much nicer: |
26 | // auto v = in.map<uint8_t>([&](simd8<uint8_t> chunk) { |
27 | // auto nib_lo = chunk & 0xf; |
28 | // auto nib_hi = chunk.shr<4>(); |
29 | // auto shuf_lo = nib_lo.lookup_16<uint8_t>(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); |
30 | // auto shuf_hi = nib_hi.lookup_16<uint8_t>(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); |
31 | // return shuf_lo & shuf_hi; |
32 | // }); |
33 | const simd8<uint8_t> table1(16, 0, 0, 0, 0, 0, 0, 0, 0, 8, 12, 1, 2, 9, 0, 0); |
34 | const simd8<uint8_t> table2(8, 0, 18, 4, 0, 1, 0, 1, 0, 0, 0, 3, 2, 1, 0, 0); |
35 | |
36 | simd8x64<uint8_t> v( |
37 | (in.chunks[0] & 0xf).lookup_16(lookup_table: table1) & (in.chunks[0].shr<4>()).lookup_16(lookup_table: table2), |
38 | (in.chunks[1] & 0xf).lookup_16(lookup_table: table1) & (in.chunks[1].shr<4>()).lookup_16(lookup_table: table2), |
39 | (in.chunks[2] & 0xf).lookup_16(lookup_table: table1) & (in.chunks[2].shr<4>()).lookup_16(lookup_table: table2), |
40 | (in.chunks[3] & 0xf).lookup_16(lookup_table: table1) & (in.chunks[3].shr<4>()).lookup_16(lookup_table: table2) |
41 | ); |
42 | |
43 | |
44 | // We compute whitespace and op separately. If the code later only use one or the |
45 | // other, given the fact that all functions are aggressively inlined, we can |
46 | // hope that useless computations will be omitted. This is namely case when |
47 | // minifying (we only need whitespace). *However* if we only need spaces, |
48 | // it is likely that we will still compute 'v' above with two lookup_16: one |
49 | // could do it a bit cheaper. This is in contrast with the x64 implementations |
50 | // where we can, efficiently, do the white space and structural matching |
51 | // separately. One reason for this difference is that on ARM NEON, the table |
52 | // lookups either zero or leave unchanged the characters exceeding 0xF whereas |
53 | // on x64, the equivalent instruction (pshufb) automatically applies a mask, |
54 | // ignoring the 4 most significant bits. Thus the x64 implementation is |
55 | // optimized differently. This being said, if you use this code strictly |
56 | // just for minification (or just to identify the structural characters), |
57 | // there is a small untaken optimization opportunity here. We deliberately |
58 | // do not pick it up. |
59 | |
60 | uint64_t op = simd8x64<bool>( |
61 | v.chunks[0].any_bits_set(bits: 0x7), |
62 | v.chunks[1].any_bits_set(bits: 0x7), |
63 | v.chunks[2].any_bits_set(bits: 0x7), |
64 | v.chunks[3].any_bits_set(bits: 0x7) |
65 | ).to_bitmask(); |
66 | |
67 | uint64_t whitespace = simd8x64<bool>( |
68 | v.chunks[0].any_bits_set(bits: 0x18), |
69 | v.chunks[1].any_bits_set(bits: 0x18), |
70 | v.chunks[2].any_bits_set(bits: 0x18), |
71 | v.chunks[3].any_bits_set(bits: 0x18) |
72 | ).to_bitmask(); |
73 | |
74 | return { ._whitespace: whitespace, ._op: op }; |
75 | } |
76 | |
77 | simdjson_inline bool is_ascii(const simd8x64<uint8_t>& input) { |
78 | simd8<uint8_t> bits = input.reduce_or(); |
79 | return bits.max_val() < 0x80u; |
80 | } |
81 | |
82 | simdjson_unused simdjson_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) { |
83 | simd8<bool> is_second_byte = prev1 >= uint8_t(0xc0u); |
84 | simd8<bool> is_third_byte = prev2 >= uint8_t(0xe0u); |
85 | simd8<bool> is_fourth_byte = prev3 >= uint8_t(0xf0u); |
86 | // Use ^ instead of | for is_*_byte, because ^ is commutative, and the caller is using ^ as well. |
87 | // This will work fine because we only have to report errors for cases with 0-1 lead bytes. |
88 | // Multiple lead bytes implies 2 overlapping multibyte characters, and if that happens, there is |
89 | // guaranteed to be at least *one* lead byte that is part of only 1 other multibyte character. |
90 | // The error will be detected there. |
91 | return is_second_byte ^ is_third_byte ^ is_fourth_byte; |
92 | } |
93 | |
94 | simdjson_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) { |
95 | simd8<bool> is_third_byte = prev2 >= uint8_t(0xe0u); |
96 | simd8<bool> is_fourth_byte = prev3 >= uint8_t(0xf0u); |
97 | return is_third_byte ^ is_fourth_byte; |
98 | } |
99 | |
100 | } // unnamed namespace |
101 | } // namespace SIMDJSON_IMPLEMENTATION |
102 | } // namespace simdjson |
103 | |
104 | #include "generic/stage1/utf8_lookup4_algorithm.h" |
105 | #include "generic/stage1/json_structural_indexer.h" |
106 | #include "generic/stage1/utf8_validator.h" |
107 | |
108 | // |
109 | // Stage 2 |
110 | // |
111 | |
112 | #include "generic/stage2/stringparsing.h" |
113 | #include "generic/stage2/tape_builder.h" |
114 | |
115 | // |
116 | // Implementation-specific overrides |
117 | // |
118 | namespace simdjson { |
119 | namespace SIMDJSON_IMPLEMENTATION { |
120 | namespace { |
121 | namespace stage1 { |
122 | |
123 | simdjson_inline uint64_t json_string_scanner::find_escaped(uint64_t backslash) { |
124 | // On ARM, we don't short-circuit this if there are no backslashes, because the branch gives us no |
125 | // benefit and therefore makes things worse. |
126 | // if (!backslash) { uint64_t escaped = prev_escaped; prev_escaped = 0; return escaped; } |
127 | return find_escaped_branchless(backslash); |
128 | } |
129 | |
130 | } // namespace stage1 |
131 | } // unnamed namespace |
132 | |
133 | simdjson_warn_unused error_code implementation::minify(const uint8_t *buf, size_t len, uint8_t *dst, size_t &dst_len) const noexcept { |
134 | return arm64::stage1::json_minifier::minify<64>(buf, len, dst, dst_len); |
135 | } |
136 | |
137 | simdjson_warn_unused error_code dom_parser_implementation::stage1(const uint8_t *_buf, size_t _len, stage1_mode streaming) noexcept { |
138 | this->buf = _buf; |
139 | this->len = _len; |
140 | return arm64::stage1::json_structural_indexer::index<64>(buf, len, parser&: *this, partial: streaming); |
141 | } |
142 | |
143 | simdjson_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept { |
144 | return arm64::stage1::generic_validate_utf8(input: buf,length: len); |
145 | } |
146 | |
147 | simdjson_warn_unused error_code dom_parser_implementation::stage2(dom::document &_doc) noexcept { |
148 | return stage2::tape_builder::parse_document<false>(dom_parser&: *this, doc&: _doc); |
149 | } |
150 | |
151 | simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::document &_doc) noexcept { |
152 | return stage2::tape_builder::parse_document<true>(dom_parser&: *this, doc&: _doc); |
153 | } |
154 | |
155 | simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept { |
156 | return arm64::stringparsing::parse_string(src, dst, allow_replacement); |
157 | } |
158 | |
159 | simdjson_warn_unused uint8_t *dom_parser_implementation::parse_wobbly_string(const uint8_t *src, uint8_t *dst) const noexcept { |
160 | return arm64::stringparsing::parse_wobbly_string(src, dst); |
161 | } |
162 | |
163 | simdjson_warn_unused error_code dom_parser_implementation::parse(const uint8_t *_buf, size_t _len, dom::document &_doc) noexcept { |
164 | auto error = stage1(_buf, _len, streaming: stage1_mode::regular); |
165 | if (error) { return error; } |
166 | return stage2(_doc); |
167 | } |
168 | |
169 | } // namespace SIMDJSON_IMPLEMENTATION |
170 | } // namespace simdjson |
171 | |
172 | #include "simdjson/arm64/end.h" |
173 | |