1#include "prism/internal/strpbrk.h"
7#include "prism/internal/bit.h"
8#include "prism/internal/diagnostic.h"
9#include "prism/internal/encoding.h"
10#include "prism/internal/parser.h"
20pm_strpbrk_invalid_multibyte_character(
pm_parser_t *parser, uint32_t start, uint32_t length) {
21 pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->error_list, start, length, PM_ERR_INVALID_MULTIBYTE_CHARACTER, parser->start[start]);
28pm_strpbrk_explicit_encoding_set(
pm_parser_t *parser, uint32_t start, uint32_t length) {
29 if (parser->explicit_encoding != NULL) {
30 if (parser->explicit_encoding == parser->encoding) {
32 }
else if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
35 pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->error_list, start, length, PM_ERR_MIXED_ENCODING, parser->encoding->name);
38 assert(
false &&
"unreachable");
42 parser->explicit_encoding = parser->encoding;
61#if defined(PRISM_HAS_NEON) || defined(PRISM_HAS_SSSE3) || defined(PRISM_HAS_SWAR)
74pm_strpbrk_cache_update(
pm_parser_t *parser,
const uint8_t *charset) {
78 if (memcmp(parser->strpbrk_cache.charset, charset,
sizeof(parser->strpbrk_cache.charset)) == 0)
return;
80 memset(parser->strpbrk_cache.low_lut, 0,
sizeof(parser->strpbrk_cache.low_lut));
81 memset(parser->strpbrk_cache.high_lut, 0,
sizeof(parser->strpbrk_cache.high_lut));
82 memset(parser->strpbrk_cache.table, 0,
sizeof(parser->strpbrk_cache.table));
88 parser->strpbrk_cache.low_lut[0x00] |= (uint8_t) (1 << 0);
89 parser->strpbrk_cache.high_lut[0x00] = (uint8_t) (1 << 0);
90 parser->strpbrk_cache.table[0] |= (uint64_t) 1;
92 size_t charset_len = 0;
93 for (
const uint8_t *c = charset; *c !=
'\0'; c++) {
94 parser->strpbrk_cache.low_lut[*c & 0x0F] |= (uint8_t) (1 << (*c >> 4));
95 parser->strpbrk_cache.high_lut[*c >> 4] = (uint8_t) (1 << (*c >> 4));
96 parser->strpbrk_cache.table[*c >> 6] |= (uint64_t) 1 << (*c & 0x3F);
101 memcpy(parser->strpbrk_cache.charset, charset, charset_len + 1);
102 memset(parser->strpbrk_cache.charset + charset_len + 1, 0,
sizeof(parser->strpbrk_cache.charset) - charset_len - 1);
107#if defined(PRISM_HAS_NEON)
111scan_strpbrk_ascii(
pm_parser_t *parser,
const uint8_t *source,
size_t maximum,
const uint8_t *charset,
size_t *index) {
112 pm_strpbrk_cache_update(parser, charset);
114 uint8x16_t low_lut = vld1q_u8(parser->strpbrk_cache.low_lut);
115 uint8x16_t high_lut = vld1q_u8(parser->strpbrk_cache.high_lut);
116 uint8x16_t mask_0f = vdupq_n_u8(0x0F);
117 uint8x16_t mask_80 = vdupq_n_u8(0x80);
121 while (idx + 16 <= maximum) {
122 uint8x16_t v = vld1q_u8(source + idx);
126 if (vmaxvq_u8(vandq_u8(v, mask_80)) != 0)
break;
128 uint8x16_t lo_class = vqtbl1q_u8(low_lut, vandq_u8(v, mask_0f));
129 uint8x16_t hi_class = vqtbl1q_u8(high_lut, vshrq_n_u8(v, 4));
130 uint8x16_t matched = vtstq_u8(lo_class, hi_class);
132 if (vmaxvq_u8(matched) == 0) {
138 uint64_t lo64 = vgetq_lane_u64(vreinterpretq_u64_u8(matched), 0);
140 *index = idx + pm_ctzll(lo64) / 8;
143 uint64_t hi64 = vgetq_lane_u64(vreinterpretq_u64_u8(matched), 1);
144 *index = idx + 8 + pm_ctzll(hi64) / 8;
149 while (idx < maximum && source[idx] < 0x80) {
150 uint8_t
byte = source[idx];
151 if (parser->strpbrk_cache.table[
byte >> 6] & ((uint64_t) 1 << (
byte & 0x3F))) {
162#elif defined(PRISM_HAS_SSSE3)
163#include <tmmintrin.h>
166scan_strpbrk_ascii(
pm_parser_t *parser,
const uint8_t *source,
size_t maximum,
const uint8_t *charset,
size_t *index) {
167 pm_strpbrk_cache_update(parser, charset);
169 __m128i low_lut = _mm_loadu_si128((
const __m128i *) parser->strpbrk_cache.low_lut);
170 __m128i high_lut = _mm_loadu_si128((
const __m128i *) parser->strpbrk_cache.high_lut);
171 __m128i mask_0f = _mm_set1_epi8(0x0F);
175 while (idx + 16 <= maximum) {
176 __m128i v = _mm_loadu_si128((
const __m128i *) (source + idx));
179 if (_mm_movemask_epi8(v) != 0)
break;
183 __m128i lo_class = _mm_shuffle_epi8(low_lut, _mm_and_si128(v, mask_0f));
184 __m128i hi_class = _mm_shuffle_epi8(high_lut, _mm_and_si128(_mm_srli_epi16(v, 4), mask_0f));
185 __m128i matched = _mm_and_si128(lo_class, hi_class);
188 int mask = _mm_movemask_epi8(_mm_cmpeq_epi8(matched, _mm_setzero_si128()));
190 if (mask == 0xFFFF) {
197 *index = idx + pm_ctzll((uint64_t) (~mask & 0xFFFF));
202 while (idx < maximum && source[idx] < 0x80) {
203 uint8_t
byte = source[idx];
204 if (parser->strpbrk_cache.table[
byte >> 6] & ((uint64_t) 1 << (
byte & 0x3F))) {
215#elif defined(PRISM_HAS_SWAR)
218scan_strpbrk_ascii(
pm_parser_t *parser,
const uint8_t *source,
size_t maximum,
const uint8_t *charset,
size_t *index) {
219 pm_strpbrk_cache_update(parser, charset);
221 static const uint64_t highs = 0x8080808080808080ULL;
224 while (idx + 8 <= maximum) {
226 memcpy(&word, source + idx, 8);
229 if (word & highs)
break;
232 for (
size_t j = 0; j < 8; j++) {
233 uint8_t
byte = source[idx + j];
234 if (parser->strpbrk_cache.table[
byte >> 6] & ((uint64_t) 1 << (
byte & 0x3F))) {
244 while (idx < maximum && source[idx] < 0x80) {
245 uint8_t
byte = source[idx];
246 if (parser->strpbrk_cache.table[
byte >> 6] & ((uint64_t) 1 << (
byte & 0x3F))) {
271pm_strpbrk_utf8(
pm_parser_t *parser,
const uint8_t *source,
const uint8_t *charset,
size_t index,
size_t maximum,
bool validate) {
272 while (index < maximum) {
273 if (strchr((
const char *) charset, source[index]) != NULL) {
274 return source + index;
277 if (source[index] < 0x80) {
280 size_t width = pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index));
284 }
else if (!validate) {
291 const size_t start = index;
295 }
while (index < maximum && pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
297 pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
309pm_strpbrk_ascii_8bit(
pm_parser_t *parser,
const uint8_t *source,
const uint8_t *charset,
size_t index,
size_t maximum,
bool validate) {
310 while (index < maximum) {
311 if (strchr((
const char *) charset, source[index]) != NULL) {
312 return source + index;
315 if (validate && source[index] >= 0x80) pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), 1);
326pm_strpbrk_multi_byte(
pm_parser_t *parser,
const uint8_t *source,
const uint8_t *charset,
size_t index,
size_t maximum,
bool validate) {
329 while (index < maximum) {
330 if (strchr((
const char *) charset, source[index]) != NULL) {
331 return source + index;
334 if (source[index] < 0x80) {
337 size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
338 if (validate) pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), (uint32_t) width);
342 }
else if (!validate) {
349 const size_t start = index;
353 }
while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
355 pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
368pm_strpbrk_single_byte(
pm_parser_t *parser,
const uint8_t *source,
const uint8_t *charset,
size_t index,
size_t maximum,
bool validate) {
371 while (index < maximum) {
372 if (strchr((
const char *) charset, source[index]) != NULL) {
373 return source + index;
376 if (source[index] < 0x80 || !validate) {
379 size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
380 pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), (uint32_t) width);
389 const size_t start = index;
393 }
while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
395 pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
423pm_strpbrk(
pm_parser_t *parser,
const uint8_t *source,
const uint8_t *charset, ptrdiff_t length,
bool validate) {
424 if (length <= 0)
return NULL;
426 size_t maximum = (size_t) length;
428 if (scan_strpbrk_ascii(parser, source, maximum, charset, &index))
return source + index;
430 if (!parser->encoding_changed) {
431 return pm_strpbrk_utf8(parser, source, charset, index, maximum, validate);
432 }
else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) {
433 return pm_strpbrk_ascii_8bit(parser, source, charset, index, maximum, validate);
434 }
else if (parser->encoding->multibyte) {
435 return pm_strpbrk_multi_byte(parser, source, charset, index, maximum, validate);
437 return pm_strpbrk_single_byte(parser, source, charset, index, maximum, validate);
#define PRISM_INLINE
Old Visual Studio versions do not support the inline keyword, so we need to define it to be __inline.
#define PRISM_UNUSED
GCC will warn if you specify a function or parameter that is unused at runtime.