leptris 1.9.391.0-arm-linux → 1.9.396.0-arm-linux
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.
- checksums.yaml +4 -4
- data/Rakefile +9 -3
- data/ext/leptris/native/native.c +30 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/ffi.rb +5 -0
- data/lib/leptris/xml/native.so +0 -0
- data/lib/leptris/xml/native_3_3.so +0 -0
- data/lib/leptris/xml.rb +25 -0
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +146 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/README.adoc +37 -0
- data/vendor-src/libleptris/src/CMakeLists.txt +12 -5
- data/vendor-src/libleptris/src/include/leptris/types.h +52 -0
- data/vendor-src/libleptris/src/include/leptris.h +6 -0
- data/vendor-src/libleptris/src/leptris/common/simd_text.c +61 -0
- data/vendor-src/libleptris/src/leptris/common/simd_text.h +13 -0
- data/vendor-src/libleptris/src/leptris/common/simd_text_avx2.c +156 -2
- data/vendor-src/libleptris/src/leptris/common/simd_text_neon.c +113 -0
- data/vendor-src/libleptris/src/leptris/core.c +31 -0
- data/vendor-src/libleptris/src/leptris/flat/direct_parse.c +19 -43
- data/vendor-src/libleptris/src/leptris/xpath/parser.c +11 -29
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +82 -8
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +36 -4
- data/vendor-src/libleptris/src/leptris/xsd/xsd_internal.h +8 -0
- data/vendor-src/libleptris/test/CMakeLists.txt +18 -0
- data/vendor-src/libleptris/test/abi/test_capabilities.cpp +112 -0
- data/vendor-src/libleptris/test/dom/test_entity_refs.cpp +111 -0
- data/vendor-src/libleptris/test/parser/test_encoding.cpp +115 -0
- data/vendor-src/libleptris/test/parser/test_utf8_simd.cpp +130 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +299 -3
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +5 -2
|
@@ -75,6 +75,14 @@ ptrdiff_t leptris_text_find3_avx2(const char* s, size_t len,
|
|
|
75
75
|
return -1;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/* MSVC has no __builtin_popcount; __popcnt32 maps to the POPCNT
|
|
79
|
+
* instruction the /arch:AVX2 target guarantees. */
|
|
80
|
+
#if defined(_MSC_VER)
|
|
81
|
+
# define LEPTRIS_POPCOUNT32(x) __popcnt(x)
|
|
82
|
+
#else
|
|
83
|
+
# define LEPTRIS_POPCOUNT32(x) __builtin_popcount(x)
|
|
84
|
+
#endif
|
|
85
|
+
|
|
78
86
|
/* Byte-population count: cmpeq → movemask → popcount per 32-byte
|
|
79
87
|
* chunk, 16-byte SSE step, scalar tail. Never reads past s+len. */
|
|
80
88
|
size_t leptris_text_count_char_avx2(const char* s, size_t len, char c) {
|
|
@@ -86,13 +94,13 @@ size_t leptris_text_count_char_avx2(const char* s, size_t len, char c) {
|
|
|
86
94
|
while (end - p >= 32) {
|
|
87
95
|
__m256i v = _mm256_loadu_si256((const __m256i*)p);
|
|
88
96
|
__m256i eq = _mm256_cmpeq_epi8(v, needle);
|
|
89
|
-
n += (size_t)
|
|
97
|
+
n += (size_t)LEPTRIS_POPCOUNT32((unsigned)_mm256_movemask_epi8(eq));
|
|
90
98
|
p += 32;
|
|
91
99
|
}
|
|
92
100
|
if (end - p >= 16) {
|
|
93
101
|
__m128i v = _mm_loadu_si128((const __m128i*)p);
|
|
94
102
|
__m128i eq = _mm_cmpeq_epi8(v, _mm256_castsi256_si128(needle));
|
|
95
|
-
n += (size_t)
|
|
103
|
+
n += (size_t)LEPTRIS_POPCOUNT32((unsigned)_mm_movemask_epi8(eq));
|
|
96
104
|
p += 16;
|
|
97
105
|
}
|
|
98
106
|
while (p < end) {
|
|
@@ -153,3 +161,149 @@ ptrdiff_t leptris_text_scan_run_avx2(const char* s, size_t len, char term,
|
|
|
153
161
|
return (p < end) ? (ptrdiff_t)(p - s) : -1;
|
|
154
162
|
}
|
|
155
163
|
}
|
|
164
|
+
|
|
165
|
+
/* ---- UTF-8 validation (leptris#1842 A5) -------------------------------
|
|
166
|
+
*
|
|
167
|
+
* Per-16-byte block, per-byte required ranges derived from the three
|
|
168
|
+
* preceding bytes (carried across blocks via palignr):
|
|
169
|
+
* must_cont(j) = start2(j-1) | start3(j-1) | start4(j-1)
|
|
170
|
+
* | start3(j-2) | start4(j-2) | start4(j-3)
|
|
171
|
+
* where startN(v) = v in [C2..DF | E0..EF | F0..F4] by length class.
|
|
172
|
+
* A must-cont byte must land in its opener's continuation range
|
|
173
|
+
* (E0 -> A0..BF, ED -> 80..9F, F0 -> 90..BF, F4 -> 80..8F, else
|
|
174
|
+
* 80..BF); a non-cont byte must be ASCII or a legal start. The tail
|
|
175
|
+
* (<16 bytes) runs the scalar DFA anchored at a character boundary
|
|
176
|
+
* (walk back at most 3 bytes). Verdicts identical to
|
|
177
|
+
* leptris_utf8_valid_scalar — pinned by the encoding suite.
|
|
178
|
+
*/
|
|
179
|
+
int leptris_utf8_valid_avx2(const char* src, size_t len) {
|
|
180
|
+
const unsigned char* p = (const unsigned char*)src;
|
|
181
|
+
if (len < 16) return leptris_utf8_valid_scalar(src, len);
|
|
182
|
+
|
|
183
|
+
const __m128i zero = _mm_setzero_si128();
|
|
184
|
+
const __m128i b80 = _mm_set1_epi8((char)0x80);
|
|
185
|
+
const __m128i bBF = _mm_set1_epi8((char)0xBF);
|
|
186
|
+
__m128i prev = zero; /* the previous block's 16 bytes */
|
|
187
|
+
|
|
188
|
+
size_t i = 0;
|
|
189
|
+
while (i + 16 <= len) {
|
|
190
|
+
__m128i cur = _mm_loadu_si128((const __m128i*)(p + i));
|
|
191
|
+
/* result[j] = byte(j-1): concat(a:b) shifted right leaves the
|
|
192
|
+
* PREVIOUS block's last lanes at the head — imm 15/14/13. */
|
|
193
|
+
__m128i b1 = _mm_alignr_epi8(cur, prev, 15);
|
|
194
|
+
__m128i b2 = _mm_alignr_epi8(cur, prev, 14);
|
|
195
|
+
__m128i b3 = _mm_alignr_epi8(cur, prev, 13);
|
|
196
|
+
|
|
197
|
+
/* start classes — the saturating idiom's operand order is the
|
|
198
|
+
* whole game: subs(v, lo)==0 tests v<=lo (saturate on
|
|
199
|
+
* underflow AND equality), so the LOWER bound must be
|
|
200
|
+
* subs(lo, v)==0 (0 iff lo<=v). subs(v, hi)==0 ⇔ v<=hi is
|
|
201
|
+
* already the right direction for the upper bound. */
|
|
202
|
+
#define IN_RANGE(v, lo, hi) \
|
|
203
|
+
(_mm_and_si128( \
|
|
204
|
+
_mm_cmpeq_epi8(_mm_subs_epu8(_mm_set1_epi8(lo), (v)), zero), \
|
|
205
|
+
_mm_cmpeq_epi8(_mm_subs_epu8((v), _mm_set1_epi8(hi)), zero)))
|
|
206
|
+
__m128i st2c = IN_RANGE(cur, (char)0xC2, (char)0xDF);
|
|
207
|
+
__m128i st3c = IN_RANGE(cur, (char)0xE0, (char)0xEF);
|
|
208
|
+
__m128i st4c = IN_RANGE(cur, (char)0xF0, (char)0xF4);
|
|
209
|
+
__m128i is_ascii =
|
|
210
|
+
_mm_cmpeq_epi8(_mm_and_si128(cur, b80), zero);
|
|
211
|
+
__m128i legal_start = _mm_or_si128(
|
|
212
|
+
st2c, _mm_or_si128(st3c, st4c));
|
|
213
|
+
|
|
214
|
+
__m128i st2b1 = IN_RANGE(b1, (char)0xC2, (char)0xDF);
|
|
215
|
+
__m128i st3b1 = IN_RANGE(b1, (char)0xE0, (char)0xEF);
|
|
216
|
+
__m128i st4b1 = IN_RANGE(b1, (char)0xF0, (char)0xF4);
|
|
217
|
+
__m128i st3b2 = IN_RANGE(b2, (char)0xE0, (char)0xEF);
|
|
218
|
+
__m128i st4b2 = IN_RANGE(b2, (char)0xF0, (char)0xF4);
|
|
219
|
+
__m128i st4b3 = IN_RANGE(b3, (char)0xF0, (char)0xF4);
|
|
220
|
+
#undef IN_RANGE
|
|
221
|
+
|
|
222
|
+
__m128i must_cont = _mm_or_si128(
|
|
223
|
+
_mm_or_si128(st2b1, _mm_or_si128(st3b1, st4b1)),
|
|
224
|
+
_mm_or_si128(_mm_or_si128(st3b2, st4b2), st4b3));
|
|
225
|
+
|
|
226
|
+
/* continuation range refinement from the opener (b1) */
|
|
227
|
+
const __m128i bA0 = _mm_set1_epi8((char)0xA0);
|
|
228
|
+
const __m128i b90 = _mm_set1_epi8((char)0x90);
|
|
229
|
+
const __m128i b9F = _mm_set1_epi8((char)0x9F);
|
|
230
|
+
const __m128i b8F = _mm_set1_epi8((char)0x8F);
|
|
231
|
+
const __m128i e0 = _mm_cmpeq_epi8(b1, _mm_set1_epi8((char)0xE0));
|
|
232
|
+
const __m128i ed = _mm_cmpeq_epi8(b1, _mm_set1_epi8((char)0xED));
|
|
233
|
+
const __m128i f0 = _mm_cmpeq_epi8(b1, _mm_set1_epi8((char)0xF0));
|
|
234
|
+
const __m128i f4 = _mm_cmpeq_epi8(b1, _mm_set1_epi8((char)0xF4));
|
|
235
|
+
__m128i lo = _mm_blendv_epi8(b80, bA0, e0);
|
|
236
|
+
lo = _mm_blendv_epi8(lo, b90, f0);
|
|
237
|
+
__m128i hi = _mm_blendv_epi8(bBF, b9F, ed);
|
|
238
|
+
hi = _mm_blendv_epi8(hi, b8F, f4);
|
|
239
|
+
|
|
240
|
+
/* cont-in-range — NEON's vcge/vcle pair translated to the
|
|
241
|
+
* saturating-subtraction idiom: subs(lo, cur) saturates to 0
|
|
242
|
+
* iff lo <= cur, subs(cur, hi) iff cur <= hi (the operand
|
|
243
|
+
* order is the whole game: subs(cur, lo) tests cur <= lo). */
|
|
244
|
+
__m128i cge = _mm_subs_epu8(lo, cur);
|
|
245
|
+
__m128i cle = _mm_subs_epu8(cur, hi);
|
|
246
|
+
__m128i cont_ok = _mm_and_si128(
|
|
247
|
+
_mm_cmpeq_epi8(cge, zero), _mm_cmpeq_epi8(cle, zero));
|
|
248
|
+
|
|
249
|
+
const __m128i ff = _mm_set1_epi8((char)0xFF);
|
|
250
|
+
__m128i not_must = _mm_xor_si128(must_cont, ff);
|
|
251
|
+
__m128i not_ascii = _mm_xor_si128(is_ascii, ff);
|
|
252
|
+
__m128i not_start = _mm_xor_si128(legal_start, ff);
|
|
253
|
+
/* error = must_cont & !cont_ok | !must_cont & !ascii &
|
|
254
|
+
* !legal_start (0x80..C1 and F5..FF fall out of the latter). */
|
|
255
|
+
__m128i block_error = _mm_or_si128(
|
|
256
|
+
_mm_and_si128(must_cont,
|
|
257
|
+
_mm_xor_si128(cont_ok, ff)),
|
|
258
|
+
_mm_and_si128(not_must,
|
|
259
|
+
_mm_and_si128(not_ascii, not_start)));
|
|
260
|
+
if (_mm_movemask_epi8(
|
|
261
|
+
_mm_cmpeq_epi8(_mm_and_si128(block_error, b80), b80)) != 0)
|
|
262
|
+
return 0;
|
|
263
|
+
prev = cur;
|
|
264
|
+
i += 16;
|
|
265
|
+
}
|
|
266
|
+
if (i >= len) {
|
|
267
|
+
/* The input ENDS at the block edge: the last sequence must
|
|
268
|
+
* be complete (the scalar DFA's "len - i <= n" rule). Walk
|
|
269
|
+
* back over at most 3 continuations; s2 lands ON the last
|
|
270
|
+
* continuation (or the final non-continuation byte). */
|
|
271
|
+
size_t s2 = i;
|
|
272
|
+
size_t conts = 0;
|
|
273
|
+
while (s2 > 0 && (p[s2 - 1] & 0xC0) == 0x80 && conts < 3) {
|
|
274
|
+
s2--;
|
|
275
|
+
conts++;
|
|
276
|
+
}
|
|
277
|
+
if (conts > 0) {
|
|
278
|
+
if (s2 == 0) return 0; /* continuations, no opener anywhere */
|
|
279
|
+
unsigned op = p[s2 - 1]; /* the opener BEFORE the run */
|
|
280
|
+
unsigned owed = (op >= 0xF0) ? 3
|
|
281
|
+
: (op >= 0xE0) ? 2
|
|
282
|
+
: (op >= 0xC2) ? 1
|
|
283
|
+
: 0;
|
|
284
|
+
if (owed > conts) return 0;
|
|
285
|
+
return 1;
|
|
286
|
+
}
|
|
287
|
+
/* the last byte is not a continuation: it must be ASCII */
|
|
288
|
+
unsigned op = p[i - 1];
|
|
289
|
+
unsigned owed = (op >= 0xF0) ? 3
|
|
290
|
+
: (op >= 0xE0) ? 2
|
|
291
|
+
: (op >= 0xC2) ? 1
|
|
292
|
+
: 0;
|
|
293
|
+
return owed == 0 ? 1 : 0;
|
|
294
|
+
}
|
|
295
|
+
/* Tail: scalar DFA anchored at a character boundary (at most 3
|
|
296
|
+
* bytes back from the SIMD end; a sequence is at most 4) — the
|
|
297
|
+
* anchored run also covers a sequence spanning the block edge.
|
|
298
|
+
* One more step back when the byte before the anchor is a start
|
|
299
|
+
* byte: an opener in the final block lane reaches into the tail
|
|
300
|
+
* (400k-case randomized cross-check vs the scalar oracle). */
|
|
301
|
+
size_t start = i;
|
|
302
|
+
while (start > 0 && (p[start] & 0xC0) == 0x80 && i - start < 3)
|
|
303
|
+
start--;
|
|
304
|
+
if (i - start < 3 && start > 0) {
|
|
305
|
+
unsigned char b = p[start - 1];
|
|
306
|
+
if (b >= 0xC2 && b <= 0xF4) start--;
|
|
307
|
+
}
|
|
308
|
+
return leptris_utf8_valid_scalar((const char*)(p + start), len - start);
|
|
309
|
+
}
|
|
@@ -380,3 +380,116 @@ ptrdiff_t leptris_text_scan_run_neon(const char* s, size_t len, char term,
|
|
|
380
380
|
return (p < end) ? (ptrdiff_t)(p - s) : -1;
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
|
+
|
|
384
|
+
/* ---- UTF-8 validation (leptris#1842 A5) -------------------------------
|
|
385
|
+
* Same derivation as the SSE core in simd_text_avx2.c; NEON bytes are
|
|
386
|
+
* unsigned, so ranges are direct vcge/vcle pairs. */
|
|
387
|
+
int leptris_utf8_valid_neon(const char* src, size_t len) {
|
|
388
|
+
const unsigned char* p = (const unsigned char*)src;
|
|
389
|
+
if (len < 16) return leptris_utf8_valid_scalar(src, len);
|
|
390
|
+
|
|
391
|
+
const uint8x16_t zero = vdupq_n_u8(0);
|
|
392
|
+
const uint8x16_t ff = vdupq_n_u8(0xFF);
|
|
393
|
+
const uint8x16_t b80 = vdupq_n_u8(0x80);
|
|
394
|
+
const uint8x16_t bBF = vdupq_n_u8(0xBF);
|
|
395
|
+
uint8x16_t prev = zero;
|
|
396
|
+
|
|
397
|
+
#define IN_RANGE(v, lo, hi) \
|
|
398
|
+
vandq_u8(vcgeq_u8((v), vdupq_n_u8(lo)), vcleq_u8((v), vdupq_n_u8(hi)))
|
|
399
|
+
size_t i = 0;
|
|
400
|
+
while (i + 16 <= len) {
|
|
401
|
+
uint8x16_t cur = vld1q_u8(p + i);
|
|
402
|
+
/* lane j = byte(j-1): vext(prev, cur, k) keeps the last k
|
|
403
|
+
* lanes of prev at the head -> k = 15/14/13. */
|
|
404
|
+
uint8x16_t b1 = vextq_u8(prev, cur, 15);
|
|
405
|
+
uint8x16_t b2 = vextq_u8(prev, cur, 14);
|
|
406
|
+
uint8x16_t b3 = vextq_u8(prev, cur, 13);
|
|
407
|
+
|
|
408
|
+
uint8x16_t st2c = IN_RANGE(cur, 0xC2, 0xDF);
|
|
409
|
+
uint8x16_t st3c = IN_RANGE(cur, 0xE0, 0xEF);
|
|
410
|
+
uint8x16_t st4c = IN_RANGE(cur, 0xF0, 0xF4);
|
|
411
|
+
uint8x16_t is_ascii = vcltq_u8(cur, b80);
|
|
412
|
+
uint8x16_t legal_start = vorrq_u8(st2c, vorrq_u8(st3c, st4c));
|
|
413
|
+
|
|
414
|
+
uint8x16_t st2b1 = IN_RANGE(b1, 0xC2, 0xDF);
|
|
415
|
+
uint8x16_t st3b1 = IN_RANGE(b1, 0xE0, 0xEF);
|
|
416
|
+
uint8x16_t st4b1 = IN_RANGE(b1, 0xF0, 0xF4);
|
|
417
|
+
uint8x16_t st3b2 = IN_RANGE(b2, 0xE0, 0xEF);
|
|
418
|
+
uint8x16_t st4b2 = IN_RANGE(b2, 0xF0, 0xF4);
|
|
419
|
+
uint8x16_t st4b3 = IN_RANGE(b3, 0xF0, 0xF4);
|
|
420
|
+
|
|
421
|
+
uint8x16_t must_cont =
|
|
422
|
+
vorrq_u8(vorrq_u8(st2b1, vorrq_u8(st3b1, st4b1)),
|
|
423
|
+
vorrq_u8(vorrq_u8(st3b2, st4b2), st4b3));
|
|
424
|
+
|
|
425
|
+
const uint8x16_t bA0 = vdupq_n_u8(0xA0);
|
|
426
|
+
const uint8x16_t b90 = vdupq_n_u8(0x90);
|
|
427
|
+
const uint8x16_t b9F = vdupq_n_u8(0x9F);
|
|
428
|
+
const uint8x16_t b8F = vdupq_n_u8(0x8F);
|
|
429
|
+
uint8x16_t e0 = vceqq_u8(b1, vdupq_n_u8(0xE0));
|
|
430
|
+
uint8x16_t ed = vceqq_u8(b1, vdupq_n_u8(0xED));
|
|
431
|
+
uint8x16_t f0 = vceqq_u8(b1, vdupq_n_u8(0xF0));
|
|
432
|
+
uint8x16_t f4 = vceqq_u8(b1, vdupq_n_u8(0xF4));
|
|
433
|
+
uint8x16_t lo = vbslq_u8(e0, bA0, b80);
|
|
434
|
+
lo = vbslq_u8(f0, b90, lo);
|
|
435
|
+
uint8x16_t hi = vbslq_u8(ed, b9F, bBF);
|
|
436
|
+
hi = vbslq_u8(f4, b8F, hi);
|
|
437
|
+
|
|
438
|
+
uint8x16_t cont_ok = vandq_u8(vcgeq_u8(cur, lo), vcleq_u8(cur, hi));
|
|
439
|
+
|
|
440
|
+
uint8x16_t block_error = vorrq_u8(
|
|
441
|
+
vandq_u8(must_cont, veorq_u8(cont_ok, ff)),
|
|
442
|
+
vandq_u8(veorq_u8(must_cont, ff),
|
|
443
|
+
vandq_u8(veorq_u8(is_ascii, ff),
|
|
444
|
+
veorq_u8(legal_start, ff))));
|
|
445
|
+
/* any lane set? */
|
|
446
|
+
/* any lane set? */
|
|
447
|
+
if (vmaxvq_u8(block_error) != 0) return 0;
|
|
448
|
+
prev = cur;
|
|
449
|
+
i += 16;
|
|
450
|
+
}
|
|
451
|
+
if (i >= len) {
|
|
452
|
+
/* The input ENDS at the block edge: the last sequence must
|
|
453
|
+
* be complete (the scalar DFA's "len - i <= n" rule). Walk
|
|
454
|
+
* back over at most 3 continuations; s2 lands ON the last
|
|
455
|
+
* continuation (or the final non-continuation byte). */
|
|
456
|
+
size_t s2 = i;
|
|
457
|
+
size_t conts = 0;
|
|
458
|
+
while (s2 > 0 && (p[s2 - 1] & 0xC0) == 0x80 && conts < 3) {
|
|
459
|
+
s2--;
|
|
460
|
+
conts++;
|
|
461
|
+
}
|
|
462
|
+
if (conts > 0) {
|
|
463
|
+
if (s2 == 0) return 0; /* continuations, no opener anywhere */
|
|
464
|
+
unsigned op = p[s2 - 1]; /* the opener BEFORE the run */
|
|
465
|
+
unsigned owed = (op >= 0xF0) ? 3
|
|
466
|
+
: (op >= 0xE0) ? 2
|
|
467
|
+
: (op >= 0xC2) ? 1
|
|
468
|
+
: 0;
|
|
469
|
+
if (owed > conts) return 0;
|
|
470
|
+
return 1;
|
|
471
|
+
}
|
|
472
|
+
/* the last byte is not a continuation: it must be ASCII */
|
|
473
|
+
unsigned op = p[i - 1];
|
|
474
|
+
unsigned owed = (op >= 0xF0) ? 3
|
|
475
|
+
: (op >= 0xE0) ? 2
|
|
476
|
+
: (op >= 0xC2) ? 1
|
|
477
|
+
: 0;
|
|
478
|
+
return owed == 0 ? 1 : 0;
|
|
479
|
+
}
|
|
480
|
+
/* Tail: scalar DFA anchored at a character boundary (at most 3
|
|
481
|
+
* bytes back from the SIMD end; a sequence is at most 4) — the
|
|
482
|
+
* anchored run also covers a sequence spanning the block edge.
|
|
483
|
+
* One more step back when the byte before the anchor is a start
|
|
484
|
+
* byte: an opener in the final block lane reaches into the tail
|
|
485
|
+
* (400k-case randomized cross-check vs the scalar oracle). */
|
|
486
|
+
size_t start = i;
|
|
487
|
+
while (start > 0 && (p[start] & 0xC0) == 0x80 && i - start < 3)
|
|
488
|
+
start--;
|
|
489
|
+
if (i - start < 3 && start > 0) {
|
|
490
|
+
unsigned char b = p[start - 1];
|
|
491
|
+
if (b >= 0xC2 && b <= 0xF4) start--;
|
|
492
|
+
}
|
|
493
|
+
return leptris_utf8_valid_scalar((const char*)(p + start), len - start);
|
|
494
|
+
}
|
|
495
|
+
#undef IN_RANGE
|
|
@@ -72,6 +72,37 @@ LEPTRIS_API void leptris_version_components(int* major, int* minor, int* patch)
|
|
|
72
72
|
if (patch) *patch = LEPTRIS_VERSION_PATCH;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/* leptris#1905: the capability descriptor. Unconditional faces are
|
|
76
|
+
* always compiled into this library; the build-conditional bits
|
|
77
|
+
* follow the same macros the build system defines for the sources
|
|
78
|
+
* — the descriptor cannot drift from the binary it ships in. */
|
|
79
|
+
LEPTRIS_API LeptrisCapabilities leptris_capabilities(void) {
|
|
80
|
+
LeptrisCapabilities c;
|
|
81
|
+
c.caps = LEPTRIS_CAP_PARSE | LEPTRIS_CAP_DOM | LEPTRIS_CAP_XPATH |
|
|
82
|
+
LEPTRIS_CAP_XQUERY | LEPTRIS_CAP_DTD | LEPTRIS_CAP_SAX |
|
|
83
|
+
LEPTRIS_CAP_HTML | LEPTRIS_CAP_XINCLUDE | LEPTRIS_CAP_DIFF |
|
|
84
|
+
LEPTRIS_CAP_DIGEST | LEPTRIS_CAP_XSD | LEPTRIS_CAP_XSD_SET |
|
|
85
|
+
LEPTRIS_CAP_RELAXNG | LEPTRIS_CAP_SCHEMATRON |
|
|
86
|
+
LEPTRIS_CAP_XSLT | LEPTRIS_CAP_DESCRIPTOR |
|
|
87
|
+
LEPTRIS_CAP_NS_SNAPSHOT_BULK | LEPTRIS_CAP_CHILDREN_BATCH |
|
|
88
|
+
LEPTRIS_CAP_NAME_COMPARE | LEPTRIS_CAP_VERSION |
|
|
89
|
+
LEPTRIS_CAP_ENTITY_REF_NODES;
|
|
90
|
+
#if defined(LEPTRIS_HAS_DUCET)
|
|
91
|
+
c.caps |= LEPTRIS_CAP_COLLATION_DUCET;
|
|
92
|
+
#endif
|
|
93
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
94
|
+
c.caps |= LEPTRIS_CAP_UNICODE_UTF8PROC;
|
|
95
|
+
#endif
|
|
96
|
+
#if defined(LEPTRIS_HAS_ICONV)
|
|
97
|
+
c.caps |= LEPTRIS_CAP_ENCODING_ICONV;
|
|
98
|
+
#endif
|
|
99
|
+
#if defined(LEPTRIS_HAS_AVX2_BUILD)
|
|
100
|
+
c.caps |= LEPTRIS_CAP_SIMD_AVX2;
|
|
101
|
+
#endif
|
|
102
|
+
c.descriptor_version = LEPTRIS_CAPS_DESCRIPTOR_VERSION;
|
|
103
|
+
return c;
|
|
104
|
+
}
|
|
105
|
+
|
|
75
106
|
LEPTRIS_API void leptris_thread_cleanup(void) {
|
|
76
107
|
leptris_xpath_drain_thread_caches();
|
|
77
108
|
leptris_root_doc_drain_thread_caches();
|
|
@@ -2314,6 +2314,12 @@ static struct leptris_document* direct_parse_internal(char* buf, size_t len,
|
|
|
2314
2314
|
if (*p.pos != '?') {
|
|
2315
2315
|
if (dp_nul(&p, p.pos)) goto fail;
|
|
2316
2316
|
p.pos++;
|
|
2317
|
+
/* consuming the separator may exhaust the buffer
|
|
2318
|
+
* (" <?xm" — the fuzzer shape): p.pos one past
|
|
2319
|
+
* p.end made p.end - p.pos underflow to SIZE_MAX and
|
|
2320
|
+
* the data memchr below walked off the allocation
|
|
2321
|
+
* (nightly-fuzz crash-aac3c9bb, no-LTO build) */
|
|
2322
|
+
if (p.pos >= p.end) goto fail;
|
|
2317
2323
|
data_start = p.pos;
|
|
2318
2324
|
}
|
|
2319
2325
|
/* memchr for '?' — fast-skip PI data bodies. */
|
|
@@ -3531,48 +3537,15 @@ static int dp_declares_non_utf8(const char* s, size_t len) {
|
|
|
3531
3537
|
}
|
|
3532
3538
|
|
|
3533
3539
|
static int dp_utf8_valid(const char* s, size_t len) {
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
*
|
|
3538
|
-
*
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
i += 8;
|
|
3544
|
-
}
|
|
3545
|
-
while (i < len && p[i] < 0x80) i++;
|
|
3546
|
-
while (i < len) {
|
|
3547
|
-
unsigned c = p[i];
|
|
3548
|
-
if (c < 0x80) {
|
|
3549
|
-
i++;
|
|
3550
|
-
continue;
|
|
3551
|
-
}
|
|
3552
|
-
size_t n;
|
|
3553
|
-
unsigned lo = 0x80;
|
|
3554
|
-
unsigned hi = 0xBF;
|
|
3555
|
-
if (c >= 0xC2 && c <= 0xDF) {
|
|
3556
|
-
n = 1;
|
|
3557
|
-
} else if (c >= 0xE0 && c <= 0xEF) {
|
|
3558
|
-
n = 2;
|
|
3559
|
-
if (c == 0xE0) lo = 0xA0;
|
|
3560
|
-
else if (c == 0xED) hi = 0x9F;
|
|
3561
|
-
} else if (c >= 0xF0 && c <= 0xF4) {
|
|
3562
|
-
n = 3;
|
|
3563
|
-
if (c == 0xF0) lo = 0x90;
|
|
3564
|
-
else if (c == 0xF4) hi = 0x8F;
|
|
3565
|
-
} else {
|
|
3566
|
-
return 0;
|
|
3567
|
-
}
|
|
3568
|
-
if (len - i <= n) return 0;
|
|
3569
|
-
if (p[i + 1] < lo || p[i + 1] > hi) return 0;
|
|
3570
|
-
for (size_t k = 2; k <= n; k++) {
|
|
3571
|
-
if (p[i + k] < 0x80 || p[i + k] > 0xBF) return 0;
|
|
3572
|
-
}
|
|
3573
|
-
i += n + 1;
|
|
3574
|
-
}
|
|
3575
|
-
return 1;
|
|
3540
|
+
/* #1842 A5: the per-byte DFA moved to the SIMD layer —
|
|
3541
|
+
* leptris_utf8_valid dispatches to boundary-vector cores (AVX2/
|
|
3542
|
+
* NEON) with the DFA as the fallback+oracle. Pure accept/reject
|
|
3543
|
+
* on the caller's bytes: no buffer-identity interaction (the A3
|
|
3544
|
+
* revert settled that question for xml_buffer; this face only
|
|
3545
|
+
* READS). */
|
|
3546
|
+
(void)s;
|
|
3547
|
+
(void)len;
|
|
3548
|
+
return leptris_utf8_valid(s, len);
|
|
3576
3549
|
}
|
|
3577
3550
|
|
|
3578
3551
|
static int dp_input_encoding_ok(const char* s, size_t len) {
|
|
@@ -3658,7 +3631,10 @@ struct leptris_document* direct_parse_inplace(char* buf, size_t len) {
|
|
|
3658
3631
|
if (!dp_declares_non_utf8(buf, len) && saw_high &&
|
|
3659
3632
|
!dp_utf8_valid(buf, len))
|
|
3660
3633
|
return NULL;
|
|
3661
|
-
|
|
3634
|
+
/* No sentinel write past len: the caller owns exactly `len`
|
|
3635
|
+
* writable bytes (the header contract promises NULL terminators
|
|
3636
|
+
* INSIDE the buffer; an exactly-sized caller trips ASAN, and
|
|
3637
|
+
* bindings hand read-only views). The scanner takes `len`. */
|
|
3662
3638
|
return direct_parse_internal(buf, len, 0, 0, 0, 0, 0, 0,
|
|
3663
3639
|
lt_count, dq_count, sq_count);
|
|
3664
3640
|
}
|
|
@@ -643,44 +643,26 @@ static XPathASTNode* parse_string_concat_expr(XPathParser* parser) {
|
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
/* True when the token AFTER a bare value-comparison keyword may
|
|
646
|
-
* START an operand.
|
|
647
|
-
*
|
|
648
|
-
*
|
|
646
|
+
* START an operand. Scalars, parens, variables, and (leptris#1910)
|
|
647
|
+
* every path start: a bare NCName/QName is an element name test in
|
|
648
|
+
* operand position, and adjacent name tokens are never a valid
|
|
649
|
+
* path continuation, so the keyword reading is unambiguous
|
|
650
|
+
* (`w le h` in xs:assert/xs:alternative tests). */
|
|
649
651
|
static int kw_operand_boundary(XPathParser* parser, XPathToken* nx) {
|
|
652
|
+
(void)parser;
|
|
650
653
|
if (!nx) return 1;
|
|
651
654
|
switch (nx->type) {
|
|
652
655
|
case TOK_EOF: case TOK_RPAREN: case TOK_COMMA:
|
|
653
656
|
case TOK_RBRACE: case TOK_LBRACE: case TOK_NUMBER:
|
|
654
657
|
case TOK_STRING: case TOK_DOLLAR: case TOK_LPAREN:
|
|
655
658
|
case TOK_MINUS: case TOK_PLUS:
|
|
659
|
+
case TOK_SLASH: case TOK_DOUBLE_SLASH: case TOK_AT:
|
|
660
|
+
case TOK_DOT: case TOK_DOUBLE_DOT: case TOK_STAR:
|
|
656
661
|
return 1;
|
|
657
662
|
case TOK_QNAME:
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
/* unprefixed call: name '(' ... prefixed call:
|
|
662
|
-
* name ':' name '(' (the QT3 corpus writes fn:year-...). */
|
|
663
|
-
XPathToken* nnx =
|
|
664
|
-
parser->token_pos + 2 < parser->token_count
|
|
665
|
-
? &parser->tokens[parser->token_pos + 2]
|
|
666
|
-
: NULL;
|
|
667
|
-
if (nnx && nnx->type == TOK_LPAREN) return 1;
|
|
668
|
-
XPathToken* t3 =
|
|
669
|
-
parser->token_pos + 3 < parser->token_count
|
|
670
|
-
? &parser->tokens[parser->token_pos + 3]
|
|
671
|
-
: NULL;
|
|
672
|
-
XPathToken* t4 =
|
|
673
|
-
parser->token_pos + 4 < parser->token_count
|
|
674
|
-
? &parser->tokens[parser->token_pos + 4]
|
|
675
|
-
: NULL;
|
|
676
|
-
XPathToken* t5 =
|
|
677
|
-
parser->token_pos + 5 < parser->token_count
|
|
678
|
-
? &parser->tokens[parser->token_pos + 5]
|
|
679
|
-
: NULL;
|
|
680
|
-
return nnx && nnx->type == TOK_COLON && t3 &&
|
|
681
|
-
t3->type == TOK_NCNAME && t4 &&
|
|
682
|
-
t4->type == TOK_LPAREN && t5;
|
|
683
|
-
}
|
|
663
|
+
case TOK_NCNAME:
|
|
664
|
+
/* name test, function call, or prefixed call */
|
|
665
|
+
return 1;
|
|
684
666
|
default:
|
|
685
667
|
return 0;
|
|
686
668
|
}
|
|
@@ -51,7 +51,11 @@ struct leptris_xsd_schema {
|
|
|
51
51
|
char* target_ns;
|
|
52
52
|
int version_11; /* xs:schema @version = "1.1" */
|
|
53
53
|
char* base_dir; /* schemaLocation resolution (file compile) */
|
|
54
|
-
int include_depth;
|
|
54
|
+
int include_depth;
|
|
55
|
+
/* xs:override body capture replaces same-named components: the
|
|
56
|
+
* attributeGroup pre-pass must re-register defs the base
|
|
57
|
+
* already captured (leptris#1913). */
|
|
58
|
+
int capture_force; /* include/import recursion guard */
|
|
55
59
|
/* leptris#1644: already-captured schema units (canonical
|
|
56
60
|
* location per entry) — the include/import DAG is dense and
|
|
57
61
|
* cyclic; without this set every DAG path re-parses and
|
|
@@ -179,7 +183,9 @@ static void xsd_cm_free(XsdCm* cm) {
|
|
|
179
183
|
free(cm->name);
|
|
180
184
|
free(cm->type);
|
|
181
185
|
free(cm->ns);
|
|
182
|
-
free(cm->
|
|
186
|
+
free(cm->res_base);
|
|
187
|
+
free(cm->res_base_ns);
|
|
188
|
+
free(cm->ext_base);
|
|
183
189
|
free(cm->ext_base_ns);
|
|
184
190
|
free(cm->any_ns);
|
|
185
191
|
free(cm->not_qname);
|
|
@@ -679,7 +685,7 @@ static int xsd_is_declaration(const char* local, const char* uri) {
|
|
|
679
685
|
"element", "attribute", "simpleType", "complexType",
|
|
680
686
|
"group", "attributeGroup", "notation", "include",
|
|
681
687
|
"defaultOpenContent",
|
|
682
|
-
"import", "redefine", NULL
|
|
688
|
+
"import", "redefine", "override", NULL
|
|
683
689
|
};
|
|
684
690
|
for (int i = 0; names[i]; i++)
|
|
685
691
|
if (strcmp(local, names[i]) == 0) return 1;
|
|
@@ -1810,7 +1816,8 @@ static void xsd_collect_decls_doc(LeptrisElement root, const char* eff_tns,
|
|
|
1810
1816
|
if (!l || !u || strcmp(u, XSD_NS) != 0)
|
|
1811
1817
|
continue;
|
|
1812
1818
|
int is_inc = (strcmp(l, "include") == 0 ||
|
|
1813
|
-
strcmp(l, "redefine") == 0
|
|
1819
|
+
strcmp(l, "redefine") == 0 ||
|
|
1820
|
+
strcmp(l, "override") == 0);
|
|
1814
1821
|
int is_imp = (strcmp(l, "import") == 0);
|
|
1815
1822
|
if (!is_inc && !is_imp) continue;
|
|
1816
1823
|
const char* loc = leptris_element_attribute(c, "schemaLocation");
|
|
@@ -1853,6 +1860,13 @@ static void xsd_collect_decls_doc(LeptrisElement root, const char* eff_tns,
|
|
|
1853
1860
|
xsd_collect_decls_doc(subroot, eff, sub_base, decls, n, max,
|
|
1854
1861
|
claimed, nclaimed, cmax, depth + 1);
|
|
1855
1862
|
}
|
|
1863
|
+
/* leptris#1913/#1926: override and redefine bodies'
|
|
1864
|
+
* declarations belong to the symbol space too — walk the
|
|
1865
|
+
* derivation element as a pseudo-root (it has no
|
|
1866
|
+
* include/import children of its own). */
|
|
1867
|
+
if (strcmp(l, "override") == 0 || strcmp(l, "redefine") == 0)
|
|
1868
|
+
xsd_collect_decls_doc(c, eff_tns, base_dir, decls, n, max,
|
|
1869
|
+
claimed, nclaimed, cmax, depth + 1);
|
|
1856
1870
|
leptris_document_free(sub);
|
|
1857
1871
|
}
|
|
1858
1872
|
}
|
|
@@ -1960,8 +1974,24 @@ static LeptrisXsdSchema xsd_compile_impl(const char* xsd_text,
|
|
|
1960
1974
|
s->target_ns = xsd_strdup(
|
|
1961
1975
|
leptris_element_attribute(root, "targetNamespace"));
|
|
1962
1976
|
{
|
|
1977
|
+
/* XSD 1.1 §4.3 schema versioning: the W3C suite and the
|
|
1978
|
+
* binding ecosystem mark 1.1 with vc:minVersion (the
|
|
1979
|
+
* versioning namespace); the plain version attribute is
|
|
1980
|
+
* the other legal spelling. vc:maxVersion below 1.1 pins
|
|
1981
|
+
* a 1.0 processor target regardless. leptris#1910/#1911:
|
|
1982
|
+
* reading only @version silently compiled asserts and
|
|
1983
|
+
* alternatives away for every vc-marked schema. */
|
|
1963
1984
|
const char* ver = leptris_element_attribute(root, "version");
|
|
1964
|
-
|
|
1985
|
+
const char* vmin = leptris_element_attribute_ns(
|
|
1986
|
+
root, "http://www.w3.org/2001/XMLSchema-versioning",
|
|
1987
|
+
"minVersion");
|
|
1988
|
+
const char* vmax = leptris_element_attribute_ns(
|
|
1989
|
+
root, "http://www.w3.org/2001/XMLSchema-versioning",
|
|
1990
|
+
"maxVersion");
|
|
1991
|
+
int is11 = (ver && strcmp(ver, "1.1") == 0) ||
|
|
1992
|
+
(vmin && strcmp(vmin, "1.1") == 0);
|
|
1993
|
+
if (vmax && strcmp(vmax, "1.0") == 0) is11 = 0;
|
|
1994
|
+
s->version_11 = is11;
|
|
1965
1995
|
}
|
|
1966
1996
|
|
|
1967
1997
|
const char* local = NULL;
|
|
@@ -2247,7 +2277,8 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
|
2247
2277
|
|
|
2248
2278
|
if (strcmp(local, "include") == 0 ||
|
|
2249
2279
|
strcmp(local, "import") == 0 ||
|
|
2250
|
-
strcmp(local, "redefine") == 0
|
|
2280
|
+
strcmp(local, "redefine") == 0 ||
|
|
2281
|
+
strcmp(local, "override") == 0) {
|
|
2251
2282
|
const char* loc = leptris_element_attribute(
|
|
2252
2283
|
(LeptrisElement)n, "schemaLocation");
|
|
2253
2284
|
if (loc && s->base_dir && s->include_depth < 16) {
|
|
@@ -2314,6 +2345,26 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
|
2314
2345
|
if (sub_ns) free(sub_ns);
|
|
2315
2346
|
s->target_ns = saved_ns;
|
|
2316
2347
|
}
|
|
2348
|
+
/* leptris#1913 (XSD 1.1 §I.4) + #1926 (XSD
|
|
2349
|
+
* 1.0 §4.2.2): the override/redefine
|
|
2350
|
+
* element's OWN declaration children replace
|
|
2351
|
+
* same-named components of the base just
|
|
2352
|
+
* captured — capture them AFTER the base so
|
|
2353
|
+
* the lookup ladder (head-first) sees the
|
|
2354
|
+
* replacements. A redefine body that EXTENDS
|
|
2355
|
+
* the redefined name folds its extension
|
|
2356
|
+
* against the base at this moment (base
|
|
2357
|
+
* captured, ladder sees the new head), which
|
|
2358
|
+
* is exactly the redefine hook. */
|
|
2359
|
+
if (subroot && same_ns &&
|
|
2360
|
+
(strcmp(local, "override") == 0 ||
|
|
2361
|
+
strcmp(local, "redefine") == 0)) {
|
|
2362
|
+
s->include_depth++;
|
|
2363
|
+
s->capture_force = 1;
|
|
2364
|
+
xsd_capture_root(s, (LeptrisElement)n);
|
|
2365
|
+
s->capture_force = 0;
|
|
2366
|
+
s->include_depth--;
|
|
2367
|
+
}
|
|
2317
2368
|
leptris_document_free(sub);
|
|
2318
2369
|
}
|
|
2319
2370
|
free(text);
|
|
@@ -2448,7 +2499,8 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
|
2448
2499
|
XsdAttrGroupDef* seen = s->attr_groups;
|
|
2449
2500
|
for (; seen; seen = seen->next)
|
|
2450
2501
|
if (strcmp(seen->name, nm) == 0) break;
|
|
2451
|
-
if (!seen
|
|
2502
|
+
if (!seen || s->capture_force)
|
|
2503
|
+
xsd_capture_attr_group_def(s, (LeptrisElement)n, nm);
|
|
2452
2504
|
}
|
|
2453
2505
|
} else if (strcmp(local, "group") == 0 && uri && !strcmp(uri, XSD_NS)) {
|
|
2454
2506
|
const char* nm =
|
|
@@ -2902,9 +2954,21 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
2902
2954
|
attrs = own_attrs;
|
|
2903
2955
|
}
|
|
2904
2956
|
} else {
|
|
2905
|
-
/* restriction: the derived model REPLACES
|
|
2957
|
+
/* restriction: the derived model REPLACES.
|
|
2958
|
+
* leptris#1923: the base still rides the type —
|
|
2959
|
+
* libxml2 admits the restriction chain's declared
|
|
2960
|
+
* attributes at validation, so record the base
|
|
2961
|
+
* (local + resolved ns) for the strict-attribute
|
|
2962
|
+
* admission walk. */
|
|
2906
2963
|
root->first_child = xsd_cm_parse(s, dq, NULL);
|
|
2907
2964
|
attrs = xsd_capture_attr_rows(s, dq, 0);
|
|
2965
|
+
if (base && strncmp(base, "xs:", 3) != 0) {
|
|
2966
|
+
free(root->res_base);
|
|
2967
|
+
root->res_base = xsd_strdup(base);
|
|
2968
|
+
free(root->res_base_ns);
|
|
2969
|
+
root->res_base_ns =
|
|
2970
|
+
buri[0] ? xsd_strdup(buri) : NULL;
|
|
2971
|
+
}
|
|
2908
2972
|
}
|
|
2909
2973
|
/* XSD 1.1: asserts nested under the derivation */
|
|
2910
2974
|
if (s->version_11)
|
|
@@ -2971,6 +3035,16 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
2971
3035
|
} else {
|
|
2972
3036
|
root->text_type = xsd_strdup(base);
|
|
2973
3037
|
}
|
|
3038
|
+
/* leptris#1923: a simpleContent restriction
|
|
3039
|
+
* rides its base for strict-attribute
|
|
3040
|
+
* admission (libxml2 admits the chain's
|
|
3041
|
+
* declared attributes — ctE019). */
|
|
3042
|
+
if (strcmp(dl, "restriction") == 0) {
|
|
3043
|
+
free(root->res_base);
|
|
3044
|
+
root->res_base = xsd_strdup(base);
|
|
3045
|
+
free(root->res_base_ns);
|
|
3046
|
+
root->res_base_ns = NULL;
|
|
3047
|
+
}
|
|
2974
3048
|
}
|
|
2975
3049
|
}
|
|
2976
3050
|
attrs = xsd_capture_attr_rows(s, dq, 0);
|