prism 2.0.0.rc1-x86_64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. checksums.yaml +7 -0
  2. data/BSDmakefile +58 -0
  3. data/CHANGELOG.md +870 -0
  4. data/CODE_OF_CONDUCT.md +76 -0
  5. data/CONTRIBUTING.md +58 -0
  6. data/LICENSE.md +7 -0
  7. data/Makefile +145 -0
  8. data/README.md +81 -0
  9. data/config.yml +4752 -0
  10. data/docs/build_system.md +119 -0
  11. data/docs/configuration.md +69 -0
  12. data/docs/cruby_compilation.md +27 -0
  13. data/docs/design.md +53 -0
  14. data/docs/encoding.md +121 -0
  15. data/docs/fuzzing.md +81 -0
  16. data/docs/heredocs.md +36 -0
  17. data/docs/javascript.md +133 -0
  18. data/docs/local_variable_depth.md +229 -0
  19. data/docs/mapping.md +117 -0
  20. data/docs/parser_translation.md +24 -0
  21. data/docs/parsing_rules.md +22 -0
  22. data/docs/releasing.md +147 -0
  23. data/docs/relocation.md +32 -0
  24. data/docs/ripper_translation.md +63 -0
  25. data/docs/ruby_api.md +45 -0
  26. data/docs/ruby_parser_translation.md +19 -0
  27. data/docs/serialization.md +230 -0
  28. data/docs/testing.md +72 -0
  29. data/ext/prism/api_node.c +7005 -0
  30. data/ext/prism/extconf.rb +126 -0
  31. data/ext/prism/extension.c +1783 -0
  32. data/ext/prism/extension.h +19 -0
  33. data/include/prism/arena.h +37 -0
  34. data/include/prism/ast.h +10371 -0
  35. data/include/prism/buffer.h +52 -0
  36. data/include/prism/comments.h +55 -0
  37. data/include/prism/compiler/accel.h +24 -0
  38. data/include/prism/compiler/align.h +36 -0
  39. data/include/prism/compiler/assume.h +25 -0
  40. data/include/prism/compiler/exported.h +24 -0
  41. data/include/prism/compiler/fallthrough.h +22 -0
  42. data/include/prism/compiler/filesystem.h +32 -0
  43. data/include/prism/compiler/flex_array.h +19 -0
  44. data/include/prism/compiler/force_inline.h +21 -0
  45. data/include/prism/compiler/format.h +25 -0
  46. data/include/prism/compiler/inline.h +17 -0
  47. data/include/prism/compiler/nodiscard.h +22 -0
  48. data/include/prism/compiler/nonnull.h +18 -0
  49. data/include/prism/compiler/unused.h +18 -0
  50. data/include/prism/constant_pool.h +81 -0
  51. data/include/prism/diagnostic.h +93 -0
  52. data/include/prism/errors_format.h +53 -0
  53. data/include/prism/excludes.h +29 -0
  54. data/include/prism/integer.h +41 -0
  55. data/include/prism/internal/allocator.h +68 -0
  56. data/include/prism/internal/allocator_debug.h +88 -0
  57. data/include/prism/internal/arena.h +108 -0
  58. data/include/prism/internal/bit.h +42 -0
  59. data/include/prism/internal/buffer.h +91 -0
  60. data/include/prism/internal/char.h +145 -0
  61. data/include/prism/internal/comments.h +53 -0
  62. data/include/prism/internal/constant_pool.h +152 -0
  63. data/include/prism/internal/diagnostic.h +389 -0
  64. data/include/prism/internal/encoding.h +242 -0
  65. data/include/prism/internal/integer.h +68 -0
  66. data/include/prism/internal/isinf.h +16 -0
  67. data/include/prism/internal/line_offset_list.h +34 -0
  68. data/include/prism/internal/list.h +62 -0
  69. data/include/prism/internal/magic_comments.h +23 -0
  70. data/include/prism/internal/memchr.h +15 -0
  71. data/include/prism/internal/node.h +38 -0
  72. data/include/prism/internal/options.h +232 -0
  73. data/include/prism/internal/parser.h +984 -0
  74. data/include/prism/internal/regexp.h +26 -0
  75. data/include/prism/internal/serialize.h +39 -0
  76. data/include/prism/internal/source.h +72 -0
  77. data/include/prism/internal/static_literals.h +100 -0
  78. data/include/prism/internal/stringy.h +35 -0
  79. data/include/prism/internal/strncasecmp.h +18 -0
  80. data/include/prism/internal/strpbrk.h +33 -0
  81. data/include/prism/internal/tokens.h +11 -0
  82. data/include/prism/json.h +32 -0
  83. data/include/prism/line_offset_list.h +61 -0
  84. data/include/prism/magic_comments.h +35 -0
  85. data/include/prism/node.h +94 -0
  86. data/include/prism/options.h +354 -0
  87. data/include/prism/parser.h +404 -0
  88. data/include/prism/prettyprint.h +31 -0
  89. data/include/prism/serialize.h +115 -0
  90. data/include/prism/source.h +155 -0
  91. data/include/prism/stream.h +28 -0
  92. data/include/prism/string_query.h +63 -0
  93. data/include/prism/stringy.h +72 -0
  94. data/include/prism/version.h +38 -0
  95. data/include/prism.h +131 -0
  96. data/lib/prism/3.3/prism.so +0 -0
  97. data/lib/prism/3.4/prism.so +0 -0
  98. data/lib/prism/4.0/prism.so +0 -0
  99. data/lib/prism/compiler.rb +815 -0
  100. data/lib/prism/desugar_compiler.rb +463 -0
  101. data/lib/prism/dispatcher.rb +2383 -0
  102. data/lib/prism/dot_visitor.rb +4848 -0
  103. data/lib/prism/dsl.rb +1369 -0
  104. data/lib/prism/ffi.rb +740 -0
  105. data/lib/prism/inspect_visitor.rb +2425 -0
  106. data/lib/prism/lex_compat.rb +953 -0
  107. data/lib/prism/mutation_compiler.rb +780 -0
  108. data/lib/prism/node.rb +33204 -0
  109. data/lib/prism/node_ext.rb +504 -0
  110. data/lib/prism/node_find.rb +215 -0
  111. data/lib/prism/parse_result/errors.rb +72 -0
  112. data/lib/prism/parse_result/newlines.rb +423 -0
  113. data/lib/prism/parse_result.rb +1221 -0
  114. data/lib/prism/pattern.rb +314 -0
  115. data/lib/prism/polyfill/append_as_bytes.rb +15 -0
  116. data/lib/prism/polyfill/byteindex.rb +13 -0
  117. data/lib/prism/polyfill/scan_byte.rb +14 -0
  118. data/lib/prism/polyfill/unpack1.rb +14 -0
  119. data/lib/prism/polyfill/warn.rb +36 -0
  120. data/lib/prism/reflection.rb +427 -0
  121. data/lib/prism/relocation.rb +567 -0
  122. data/lib/prism/serialize.rb +5308 -0
  123. data/lib/prism/string_query.rb +46 -0
  124. data/lib/prism/translation/parser/builder.rb +70 -0
  125. data/lib/prism/translation/parser/compiler.rb +2226 -0
  126. data/lib/prism/translation/parser/lexer.rb +769 -0
  127. data/lib/prism/translation/parser.rb +376 -0
  128. data/lib/prism/translation/parser_current.rb +26 -0
  129. data/lib/prism/translation/parser_versions.rb +36 -0
  130. data/lib/prism/translation/ripper/filter.rb +53 -0
  131. data/lib/prism/translation/ripper/lexer.rb +133 -0
  132. data/lib/prism/translation/ripper/sexp.rb +118 -0
  133. data/lib/prism/translation/ripper/shim.rb +7 -0
  134. data/lib/prism/translation/ripper.rb +4278 -0
  135. data/lib/prism/translation/ruby_parser.rb +1721 -0
  136. data/lib/prism/translation.rb +20 -0
  137. data/lib/prism/visitor.rb +1440 -0
  138. data/lib/prism.rb +168 -0
  139. data/prism.gemspec +234 -0
  140. data/rbi/generated/prism/compiler.rbi +489 -0
  141. data/rbi/generated/prism/desugar_compiler.rbi +306 -0
  142. data/rbi/generated/prism/dispatcher.rbi +1138 -0
  143. data/rbi/generated/prism/dot_visitor.rbi +618 -0
  144. data/rbi/generated/prism/dsl.rbi +751 -0
  145. data/rbi/generated/prism/inspect_visitor.rbi +503 -0
  146. data/rbi/generated/prism/lex_compat.rbi +162 -0
  147. data/rbi/generated/prism/mutation_compiler.rbi +464 -0
  148. data/rbi/generated/prism/node.rbi +13681 -0
  149. data/rbi/generated/prism/node_ext.rbi +259 -0
  150. data/rbi/generated/prism/node_find.rbi +79 -0
  151. data/rbi/generated/prism/parse_result/errors.rbi +21 -0
  152. data/rbi/generated/prism/parse_result/newlines.rbi +208 -0
  153. data/rbi/generated/prism/parse_result.rbi +764 -0
  154. data/rbi/generated/prism/pattern.rbi +134 -0
  155. data/rbi/generated/prism/reflection.rbi +89 -0
  156. data/rbi/generated/prism/relocation.rbi +329 -0
  157. data/rbi/generated/prism/serialize.rbi +181 -0
  158. data/rbi/generated/prism/string_query.rbi +36 -0
  159. data/rbi/generated/prism/translation.rbi +7 -0
  160. data/rbi/generated/prism/visitor.rbi +650 -0
  161. data/rbi/generated/prism.rbi +86 -0
  162. data/rbi/prism/translation/parser.rbi +11 -0
  163. data/rbi/prism/translation/parser_versions.rbi +28 -0
  164. data/rbi/prism/translation/ripper.rbi +15 -0
  165. data/rbi/rubyvm/node_find.rbi +14 -0
  166. data/sig/generated/prism/compiler.rbs +492 -0
  167. data/sig/generated/prism/desugar_compiler.rbs +305 -0
  168. data/sig/generated/prism/dispatcher.rbs +1292 -0
  169. data/sig/generated/prism/dot_visitor.rbs +635 -0
  170. data/sig/generated/prism/dsl.rbs +928 -0
  171. data/sig/generated/prism/inspect_visitor.rbs +509 -0
  172. data/sig/generated/prism/lex_compat.rbs +166 -0
  173. data/sig/generated/prism/mutation_compiler.rbs +464 -0
  174. data/sig/generated/prism/node.rbs +20160 -0
  175. data/sig/generated/prism/node_ext.rbs +290 -0
  176. data/sig/generated/prism/node_find.rbs +90 -0
  177. data/sig/generated/prism/parse_result/errors.rbs +22 -0
  178. data/sig/generated/prism/parse_result/newlines.rbs +223 -0
  179. data/sig/generated/prism/parse_result.rbs +859 -0
  180. data/sig/generated/prism/pattern.rbs +156 -0
  181. data/sig/generated/prism/reflection.rbs +101 -0
  182. data/sig/generated/prism/relocation.rbs +428 -0
  183. data/sig/generated/prism/serialize.rbs +195 -0
  184. data/sig/generated/prism/string_query.rbs +36 -0
  185. data/sig/generated/prism/translation.rbs +8 -0
  186. data/sig/generated/prism/visitor.rbs +1111 -0
  187. data/sig/generated/prism.rbs +84 -0
  188. data/sig/manifest.yaml +2 -0
  189. data/src/arena.c +117 -0
  190. data/src/buffer.c +380 -0
  191. data/src/char.c +289 -0
  192. data/src/comments.c +454 -0
  193. data/src/constant_pool.c +406 -0
  194. data/src/diagnostic.c +886 -0
  195. data/src/encoding.c +5345 -0
  196. data/src/errors_format.c +519 -0
  197. data/src/integer.c +681 -0
  198. data/src/json.c +5733 -0
  199. data/src/line_offset_list.c +100 -0
  200. data/src/list.c +24 -0
  201. data/src/memchr.c +37 -0
  202. data/src/node.c +4515 -0
  203. data/src/options.c +451 -0
  204. data/src/parser.c +349 -0
  205. data/src/prettyprint.c +9024 -0
  206. data/src/prism.c +23739 -0
  207. data/src/regexp.c +1706 -0
  208. data/src/serialize.c +2309 -0
  209. data/src/source.c +518 -0
  210. data/src/static_literals.c +685 -0
  211. data/src/string_query.c +166 -0
  212. data/src/stringy.c +91 -0
  213. data/src/strncasecmp.c +37 -0
  214. data/src/strpbrk.c +447 -0
  215. data/src/tokens.c +724 -0
  216. metadata +259 -0
data/src/strpbrk.c ADDED
@@ -0,0 +1,447 @@
1
+ #include "prism/internal/strpbrk.h"
2
+
3
+ #include "prism/compiler/accel.h"
4
+ #include "prism/compiler/inline.h"
5
+ #include "prism/compiler/unused.h"
6
+
7
+ #include "prism/internal/bit.h"
8
+ #include "prism/internal/diagnostic.h"
9
+ #include "prism/internal/encoding.h"
10
+ #include "prism/internal/parser.h"
11
+
12
+ #include <assert.h>
13
+ #include <stdbool.h>
14
+ #include <string.h>
15
+
16
+ /**
17
+ * Add an invalid multibyte character error to the parser.
18
+ */
19
+ static PRISM_INLINE void
20
+ pm_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]);
22
+ }
23
+
24
+ /**
25
+ * Set the explicit encoding for the parser to the current encoding.
26
+ */
27
+ static PRISM_INLINE void
28
+ pm_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) {
31
+ // Okay, we already locked to this encoding.
32
+ } else if (parser->explicit_encoding == PM_ENCODING_UTF_8_ENTRY) {
33
+ // Not okay, we already found a Unicode escape sequence and this
34
+ // conflicts.
35
+ pm_diagnostic_list_append_format(&parser->metadata_arena, &parser->error_list, start, length, PM_ERR_MIXED_ENCODING, parser->encoding->name);
36
+ } else {
37
+ // Should not be anything else.
38
+ assert(false && "unreachable");
39
+ }
40
+ }
41
+
42
+ parser->explicit_encoding = parser->encoding;
43
+ }
44
+
45
+ /**
46
+ * Scan forward through ASCII bytes looking for a byte that is in the given
47
+ * charset. Returns true if a match was found, storing its offset in *index.
48
+ * Returns false if no match was found, storing the number of ASCII bytes
49
+ * consumed in *index (so the caller can skip past them).
50
+ *
51
+ * All charset characters must be ASCII (< 0x80). The scanner stops at non-ASCII
52
+ * bytes, returning control to the caller's encoding-aware loop.
53
+ *
54
+ * Up to three optimized implementations are selected at compile time, with a
55
+ * no-op fallback for unsupported platforms:
56
+ * 1. NEON — processes 16 bytes per iteration on aarch64.
57
+ * 2. SSE2 — processes 16 bytes per iteration on x86-64.
58
+ * 3. SWAR — little-endian fallback, processes 8 bytes per iteration.
59
+ */
60
+
61
+ #if defined(PRISM_HAS_NEON) || defined(PRISM_HAS_SSE2) || defined(PRISM_HAS_SWAR)
62
+
63
+ /**
64
+ * Update the cached strpbrk lookup tables if the charset has changed. The
65
+ * parser caches the last charset's precomputed tables so that repeated calls
66
+ * with the same breakpoints (the common case during string/regex/list lexing)
67
+ * skip table construction entirely.
68
+ *
69
+ * Builds three structures:
70
+ * - low_lut/high_lut: nibble-based lookup tables for SIMD matching (NEON)
71
+ * - table: 256-bit bitmap for scalar fallback matching (all platforms)
72
+ */
73
+ static PRISM_INLINE void
74
+ pm_strpbrk_cache_update(pm_parser_t *parser, const uint8_t *charset) {
75
+ // The cache key is the full charset buffer (PM_STRPBRK_CACHE_SIZE bytes).
76
+ // Since it is always NUL-padded, a fixed-size comparison covers both
77
+ // content and length.
78
+ if (memcmp(parser->strpbrk_cache.charset, charset, sizeof(parser->strpbrk_cache.charset)) == 0) return;
79
+
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));
83
+
84
+ // Always include NUL in the tables. The slow path uses strchr, which
85
+ // always matches NUL (it finds the C string terminator), so NUL is
86
+ // effectively always a breakpoint. Replicating that here lets the fast
87
+ // scanner handle NUL at full speed instead of bailing to the slow path.
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;
91
+
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);
97
+ charset_len++;
98
+ }
99
+
100
+ // Store the new charset key, NUL-padded to the full buffer size.
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);
103
+ }
104
+
105
+ #endif
106
+
107
+ #if defined(PRISM_HAS_NEON)
108
+ #include <arm_neon.h>
109
+
110
+ static PRISM_INLINE bool
111
+ scan_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);
113
+
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);
118
+
119
+ size_t idx = 0;
120
+
121
+ while (idx + 16 <= maximum) {
122
+ uint8x16_t v = vld1q_u8(source + idx);
123
+
124
+ // If any byte has the high bit set, we have non-ASCII data.
125
+ // Return to let the caller's encoding-aware loop handle it.
126
+ if (vmaxvq_u8(vandq_u8(v, mask_80)) != 0) break;
127
+
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);
131
+
132
+ if (vmaxvq_u8(matched) == 0) {
133
+ idx += 16;
134
+ continue;
135
+ }
136
+
137
+ // Find the position of the first matching byte.
138
+ uint64_t lo64 = vgetq_lane_u64(vreinterpretq_u64_u8(matched), 0);
139
+ if (lo64 != 0) {
140
+ *index = idx + pm_ctzll(lo64) / 8;
141
+ return true;
142
+ }
143
+ uint64_t hi64 = vgetq_lane_u64(vreinterpretq_u64_u8(matched), 1);
144
+ *index = idx + 8 + pm_ctzll(hi64) / 8;
145
+ return true;
146
+ }
147
+
148
+ // Scalar tail for remaining < 16 ASCII bytes.
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))) {
152
+ *index = idx;
153
+ return true;
154
+ }
155
+ idx++;
156
+ }
157
+
158
+ *index = idx;
159
+ return false;
160
+ }
161
+
162
+ #elif defined(PRISM_HAS_SSE2)
163
+ #include <emmintrin.h>
164
+
165
+ static PRISM_INLINE bool
166
+ scan_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);
168
+
169
+ /*
170
+ * SSE2 has no byte shuffle, so the nibble lookup used by NEON is not
171
+ * available. Instead, each charset byte is broadcast into its own vector
172
+ * and compared against the input directly. Charsets are short, so this is
173
+ * a handful of compares per 16 bytes.
174
+ */
175
+ __m128i needles[sizeof(parser->strpbrk_cache.charset)];
176
+ size_t needles_size = 0;
177
+
178
+ for (size_t i = 0; i < sizeof(parser->strpbrk_cache.charset) && parser->strpbrk_cache.charset[i] != '\0'; i++) {
179
+ needles[needles_size++] = _mm_set1_epi8((char) parser->strpbrk_cache.charset[i]);
180
+ }
181
+
182
+ const __m128i zero = _mm_setzero_si128();
183
+ size_t idx = 0;
184
+
185
+ while (idx + 16 <= maximum) {
186
+ __m128i v = _mm_loadu_si128((const __m128i *) (source + idx));
187
+
188
+ /* If any byte has the high bit set, stop. */
189
+ if (_mm_movemask_epi8(v) != 0) break;
190
+
191
+ /* NUL is always a breakpoint, matching pm_strpbrk_cache_update. */
192
+ __m128i matched = _mm_cmpeq_epi8(v, zero);
193
+ for (size_t i = 0; i < needles_size; i++) {
194
+ matched = _mm_or_si128(matched, _mm_cmpeq_epi8(v, needles[i]));
195
+ }
196
+
197
+ int mask = _mm_movemask_epi8(matched);
198
+
199
+ if (mask == 0) {
200
+ idx += 16;
201
+ continue;
202
+ }
203
+
204
+ /* Find the first matching byte. */
205
+ *index = idx + pm_ctzll((uint64_t) (mask & 0xFFFF));
206
+ return true;
207
+ }
208
+
209
+ /* Scalar tail. */
210
+ while (idx < maximum && source[idx] < 0x80) {
211
+ uint8_t byte = source[idx];
212
+ if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) {
213
+ *index = idx;
214
+ return true;
215
+ }
216
+ idx++;
217
+ }
218
+
219
+ *index = idx;
220
+ return false;
221
+ }
222
+
223
+ #elif defined(PRISM_HAS_SWAR)
224
+
225
+ static PRISM_INLINE bool
226
+ scan_strpbrk_ascii(pm_parser_t *parser, const uint8_t *source, size_t maximum, const uint8_t *charset, size_t *index) {
227
+ pm_strpbrk_cache_update(parser, charset);
228
+
229
+ static const uint64_t highs = 0x8080808080808080ULL;
230
+ size_t idx = 0;
231
+
232
+ while (idx + 8 <= maximum) {
233
+ uint64_t word;
234
+ memcpy(&word, source + idx, 8);
235
+
236
+ // Bail on any non-ASCII byte.
237
+ if (word & highs) break;
238
+
239
+ // Check each byte against the charset table.
240
+ for (size_t j = 0; j < 8; j++) {
241
+ uint8_t byte = source[idx + j];
242
+ if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) {
243
+ *index = idx + j;
244
+ return true;
245
+ }
246
+ }
247
+
248
+ idx += 8;
249
+ }
250
+
251
+ // Scalar tail.
252
+ while (idx < maximum && source[idx] < 0x80) {
253
+ uint8_t byte = source[idx];
254
+ if (parser->strpbrk_cache.table[byte >> 6] & ((uint64_t) 1 << (byte & 0x3F))) {
255
+ *index = idx;
256
+ return true;
257
+ }
258
+ idx++;
259
+ }
260
+
261
+ *index = idx;
262
+ return false;
263
+ }
264
+
265
+ #else
266
+
267
+ static PRISM_INLINE bool
268
+ scan_strpbrk_ascii(PRISM_UNUSED pm_parser_t *parser, PRISM_UNUSED const uint8_t *source, PRISM_UNUSED size_t maximum, PRISM_UNUSED const uint8_t *charset, size_t *index) {
269
+ *index = 0;
270
+ return false;
271
+ }
272
+
273
+ #endif
274
+
275
+ /**
276
+ * This is the default path.
277
+ */
278
+ static PRISM_INLINE const uint8_t *
279
+ pm_strpbrk_utf8(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) {
280
+ while (index < maximum) {
281
+ if (strchr((const char *) charset, source[index]) != NULL) {
282
+ return source + index;
283
+ }
284
+
285
+ if (source[index] < 0x80) {
286
+ index++;
287
+ } else {
288
+ size_t width = pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index));
289
+
290
+ if (width > 0) {
291
+ index += width;
292
+ } else if (!validate) {
293
+ index++;
294
+ } else {
295
+ // At this point we know we have an invalid multibyte character.
296
+ // We'll walk forward as far as we can until we find the next
297
+ // valid character so that we don't spam the user with a ton of
298
+ // the same kind of error.
299
+ const size_t start = index;
300
+
301
+ do {
302
+ index++;
303
+ } while (index < maximum && pm_encoding_utf_8_char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
304
+
305
+ pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
306
+ }
307
+ }
308
+ }
309
+
310
+ return NULL;
311
+ }
312
+
313
+ /**
314
+ * This is the path when the encoding is ASCII-8BIT.
315
+ */
316
+ static PRISM_INLINE const uint8_t *
317
+ pm_strpbrk_ascii_8bit(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) {
318
+ while (index < maximum) {
319
+ if (strchr((const char *) charset, source[index]) != NULL) {
320
+ return source + index;
321
+ }
322
+
323
+ if (validate && source[index] >= 0x80) pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), 1);
324
+ index++;
325
+ }
326
+
327
+ return NULL;
328
+ }
329
+
330
+ /**
331
+ * This is the slow path that does care about the encoding.
332
+ */
333
+ static PRISM_INLINE const uint8_t *
334
+ pm_strpbrk_multi_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) {
335
+ const pm_encoding_t *encoding = parser->encoding;
336
+
337
+ while (index < maximum) {
338
+ if (strchr((const char *) charset, source[index]) != NULL) {
339
+ return source + index;
340
+ }
341
+
342
+ if (source[index] < 0x80) {
343
+ index++;
344
+ } else {
345
+ size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
346
+ if (validate) pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), (uint32_t) width);
347
+
348
+ if (width > 0) {
349
+ index += width;
350
+ } else if (!validate) {
351
+ index++;
352
+ } else {
353
+ // At this point we know we have an invalid multibyte character.
354
+ // We'll walk forward as far as we can until we find the next
355
+ // valid character so that we don't spam the user with a ton of
356
+ // the same kind of error.
357
+ const size_t start = index;
358
+
359
+ do {
360
+ index++;
361
+ } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
362
+
363
+ pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
364
+ }
365
+ }
366
+ }
367
+
368
+ return NULL;
369
+ }
370
+
371
+ /**
372
+ * This is the fast path that does not care about the encoding because we know
373
+ * the encoding only supports single-byte characters.
374
+ */
375
+ static PRISM_INLINE const uint8_t *
376
+ pm_strpbrk_single_byte(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, size_t index, size_t maximum, bool validate) {
377
+ const pm_encoding_t *encoding = parser->encoding;
378
+
379
+ while (index < maximum) {
380
+ if (strchr((const char *) charset, source[index]) != NULL) {
381
+ return source + index;
382
+ }
383
+
384
+ if (source[index] < 0x80 || !validate) {
385
+ index++;
386
+ } else {
387
+ size_t width = encoding->char_width(source + index, (ptrdiff_t) (maximum - index));
388
+ pm_strpbrk_explicit_encoding_set(parser, (uint32_t) (source - parser->start), (uint32_t) width);
389
+
390
+ if (width > 0) {
391
+ index += width;
392
+ } else {
393
+ // At this point we know we have an invalid multibyte character.
394
+ // We'll walk forward as far as we can until we find the next
395
+ // valid character so that we don't spam the user with a ton of
396
+ // the same kind of error.
397
+ const size_t start = index;
398
+
399
+ do {
400
+ index++;
401
+ } while (index < maximum && encoding->char_width(source + index, (ptrdiff_t) (maximum - index)) == 0);
402
+
403
+ pm_strpbrk_invalid_multibyte_character(parser, (uint32_t) ((source + start) - parser->start), (uint32_t) (index - start));
404
+ }
405
+ }
406
+ }
407
+
408
+ return NULL;
409
+ }
410
+
411
+ /**
412
+ * Here we have rolled our own version of strpbrk. The standard library strpbrk
413
+ * has undefined behavior when the source string is not null-terminated. We want
414
+ * to support strings that are not null-terminated because pm_parse does not
415
+ * have the contract that the string is null-terminated. (This is desirable
416
+ * because it means the extension can call pm_parse with the result of a call to
417
+ * mmap).
418
+ *
419
+ * The standard library strpbrk also does not support passing a maximum length
420
+ * to search. We want to support this for the reason mentioned above, but we
421
+ * also don't want it to stop on null bytes. Ruby actually allows null bytes
422
+ * within strings, comments, regular expressions, etc. So we need to be able to
423
+ * skip past them.
424
+ *
425
+ * Finally, we want to support encodings wherein the charset could contain
426
+ * characters that are trailing bytes of multi-byte characters. For example, in
427
+ * Shift_JIS, the backslash character can be a trailing byte. In that case we
428
+ * need to take a slower path and iterate one multi-byte character at a time.
429
+ */
430
+ const uint8_t *
431
+ pm_strpbrk(pm_parser_t *parser, const uint8_t *source, const uint8_t *charset, ptrdiff_t length, bool validate) {
432
+ if (length <= 0) return NULL;
433
+
434
+ size_t maximum = (size_t) length;
435
+ size_t index = 0;
436
+ if (scan_strpbrk_ascii(parser, source, maximum, charset, &index)) return source + index;
437
+
438
+ if (!parser->encoding_changed) {
439
+ return pm_strpbrk_utf8(parser, source, charset, index, maximum, validate);
440
+ } else if (parser->encoding == PM_ENCODING_ASCII_8BIT_ENTRY) {
441
+ return pm_strpbrk_ascii_8bit(parser, source, charset, index, maximum, validate);
442
+ } else if (parser->encoding->multibyte) {
443
+ return pm_strpbrk_multi_byte(parser, source, charset, index, maximum, validate);
444
+ } else {
445
+ return pm_strpbrk_single_byte(parser, source, charset, index, maximum, validate);
446
+ }
447
+ }