teptris 0.2.37-aarch64-linux → 0.2.39-aarch64-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/ext/teptris_ext/engine/LICENSE.md +21 -0
- data/ext/teptris_ext/engine/src/include/teptris/plan.h +123 -0
- data/ext/teptris_ext/engine/src/include/teptris/teptris.h +208 -0
- data/ext/teptris_ext/engine/src/include/teptris/version.h +9 -0
- data/ext/teptris_ext/engine/src/teptris/api.c +362 -0
- data/ext/teptris_ext/engine/src/teptris/build.c +355 -0
- data/ext/teptris_ext/engine/src/teptris/common/chartype.c +70 -0
- data/ext/teptris_ext/engine/src/teptris/common/chartype.h +41 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/common.h +114 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s.c +496 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s_full_table.h +367 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/d2s_intrinsics.h +357 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/digit_table.h +35 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/ryu_d2s.h +39 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/ryu_parse.h +45 -0
- data/ext/teptris_ext/engine/src/teptris/common/ryu/s2d.c +259 -0
- data/ext/teptris_ext/engine/src/teptris/common/simd.h +119 -0
- data/ext/teptris_ext/engine/src/teptris/dom/dom.c +192 -0
- data/ext/teptris_ext/engine/src/teptris/dom/dom.h +124 -0
- data/ext/teptris_ext/engine/src/teptris/emit/emitter.c +718 -0
- data/ext/teptris_ext/engine/src/teptris/emit/emitter.h +11 -0
- data/ext/teptris_ext/engine/src/teptris/memory/arena.c +104 -0
- data/ext/teptris_ext/engine/src/teptris/memory/arena.h +53 -0
- data/ext/teptris_ext/engine/src/teptris/parse/datetime.c +218 -0
- data/ext/teptris_ext/engine/src/teptris/parse/keys.c +121 -0
- data/ext/teptris_ext/engine/src/teptris/parse/parse.h +76 -0
- data/ext/teptris_ext/engine/src/teptris/parse/parser.c +931 -0
- data/ext/teptris_ext/engine/src/teptris/parse/scalars.c +940 -0
- data/ext/teptris_ext/engine/src/teptris/plan/plan.c +472 -0
- data/ext/teptris_ext/engine/src/teptris/version.c +6 -0
- data/ext/teptris_ext/extconf.rb +61 -0
- data/ext/teptris_ext/materialize.c +608 -0
- data/lib/{teptris_ext.so → teptris/3.3/teptris_ext.so} +0 -0
- data/lib/teptris/3.4/teptris_ext.so +0 -0
- data/lib/teptris/4.0/teptris_ext.so +0 -0
- data/lib/teptris/version.rb +1 -1
- data/lib/teptris.rb +19 -11
- metadata +37 -3
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
// Copyright 2019 Ulf Adams
|
|
2
|
+
//
|
|
3
|
+
// The contents of this file may be used under the terms of the Apache License,
|
|
4
|
+
// Version 2.0.
|
|
5
|
+
//
|
|
6
|
+
// (See accompanying file LICENSE-Apache or copy at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0)
|
|
8
|
+
//
|
|
9
|
+
// Alternatively, the contents of this file may be used under the terms of
|
|
10
|
+
// the Boost Software License, Version 1.0.
|
|
11
|
+
// (See accompanying file LICENSE-Boost or copy at
|
|
12
|
+
// https://www.boost.org/LICENSE_1_0.txt)
|
|
13
|
+
//
|
|
14
|
+
// Unless required by applicable law or agreed to in writing, this software
|
|
15
|
+
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
// KIND, either express or implied.
|
|
17
|
+
|
|
18
|
+
#include "ryu_parse.h"
|
|
19
|
+
|
|
20
|
+
#include <assert.h>
|
|
21
|
+
#include <stdbool.h>
|
|
22
|
+
#include <stdint.h>
|
|
23
|
+
#include <stdlib.h>
|
|
24
|
+
#include <string.h>
|
|
25
|
+
|
|
26
|
+
#ifdef RYU_DEBUG
|
|
27
|
+
#include <inttypes.h>
|
|
28
|
+
#include <stdio.h>
|
|
29
|
+
#endif
|
|
30
|
+
|
|
31
|
+
#include "common.h"
|
|
32
|
+
#include "d2s_intrinsics.h"
|
|
33
|
+
|
|
34
|
+
#if defined(RYU_OPTIMIZE_SIZE)
|
|
35
|
+
#include "d2s_small_table.h"
|
|
36
|
+
#else
|
|
37
|
+
#include "d2s_full_table.h"
|
|
38
|
+
#endif
|
|
39
|
+
|
|
40
|
+
#define DOUBLE_MANTISSA_BITS 52
|
|
41
|
+
#define DOUBLE_EXPONENT_BITS 11
|
|
42
|
+
#define DOUBLE_EXPONENT_BIAS 1023
|
|
43
|
+
|
|
44
|
+
#if defined(_MSC_VER)
|
|
45
|
+
#include <intrin.h>
|
|
46
|
+
|
|
47
|
+
static inline uint32_t floor_log2(const uint64_t value) {
|
|
48
|
+
long index;
|
|
49
|
+
return _BitScanReverse64(&index, value) ? index : 64;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#else
|
|
53
|
+
|
|
54
|
+
static inline uint32_t floor_log2(const uint64_t value) {
|
|
55
|
+
return 63 - __builtin_clzll(value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#endif
|
|
59
|
+
|
|
60
|
+
// The max function is already defined on Windows.
|
|
61
|
+
static inline int32_t max32(int32_t a, int32_t b) {
|
|
62
|
+
return a < b ? b : a;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static inline double int64Bits2Double(uint64_t bits) {
|
|
66
|
+
double f;
|
|
67
|
+
memcpy(&f, &bits, sizeof(double));
|
|
68
|
+
return f;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
enum teptris_ryu_status teptris_ryu_s2d_n(const char * buffer, const int len, double * result) {
|
|
72
|
+
if (len == 0) {
|
|
73
|
+
return TEPTRIS_RYU_INPUT_TOO_SHORT;
|
|
74
|
+
}
|
|
75
|
+
int m10digits = 0;
|
|
76
|
+
int e10digits = 0;
|
|
77
|
+
int dotIndex = len;
|
|
78
|
+
int eIndex = len;
|
|
79
|
+
uint64_t m10 = 0;
|
|
80
|
+
int32_t e10 = 0;
|
|
81
|
+
bool signedM = false;
|
|
82
|
+
bool signedE = false;
|
|
83
|
+
int i = 0;
|
|
84
|
+
if (buffer[i] == '-') {
|
|
85
|
+
signedM = true;
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
for (; i < len; i++) {
|
|
89
|
+
char c = buffer[i];
|
|
90
|
+
if (c == '.') {
|
|
91
|
+
if (dotIndex != len) {
|
|
92
|
+
return TEPTRIS_RYU_MALFORMED_INPUT;
|
|
93
|
+
}
|
|
94
|
+
dotIndex = i;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if ((c < '0') || (c > '9')) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
if (m10digits >= 17) {
|
|
101
|
+
return TEPTRIS_RYU_INPUT_TOO_LONG;
|
|
102
|
+
}
|
|
103
|
+
m10 = 10 * m10 + (c - '0');
|
|
104
|
+
if (m10 != 0) {
|
|
105
|
+
m10digits++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (i < len && ((buffer[i] == 'e') || (buffer[i] == 'E'))) {
|
|
109
|
+
eIndex = i;
|
|
110
|
+
i++;
|
|
111
|
+
if (i < len && ((buffer[i] == '-') || (buffer[i] == '+'))) {
|
|
112
|
+
signedE = buffer[i] == '-';
|
|
113
|
+
i++;
|
|
114
|
+
}
|
|
115
|
+
for (; i < len; i++) {
|
|
116
|
+
char c = buffer[i];
|
|
117
|
+
if ((c < '0') || (c > '9')) {
|
|
118
|
+
return TEPTRIS_RYU_MALFORMED_INPUT;
|
|
119
|
+
}
|
|
120
|
+
if (e10digits > 3) {
|
|
121
|
+
// TODO: Be more lenient. Return +/-Infinity or +/-0 instead.
|
|
122
|
+
return TEPTRIS_RYU_INPUT_TOO_LONG;
|
|
123
|
+
}
|
|
124
|
+
e10 = 10 * e10 + (c - '0');
|
|
125
|
+
if (e10 != 0) {
|
|
126
|
+
e10digits++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (i < len) {
|
|
131
|
+
return TEPTRIS_RYU_MALFORMED_INPUT;
|
|
132
|
+
}
|
|
133
|
+
if (signedE) {
|
|
134
|
+
e10 = -e10;
|
|
135
|
+
}
|
|
136
|
+
e10 -= dotIndex < eIndex ? eIndex - dotIndex - 1 : 0;
|
|
137
|
+
if (m10 == 0) {
|
|
138
|
+
*result = signedM ? -0.0 : 0.0;
|
|
139
|
+
return TEPTRIS_RYU_SUCCESS;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#ifdef RYU_DEBUG
|
|
143
|
+
printf("Input=%s\n", buffer);
|
|
144
|
+
printf("m10digits = %d\n", m10digits);
|
|
145
|
+
printf("e10digits = %d\n", e10digits);
|
|
146
|
+
printf("m10 * 10^e10 = %" PRIu64 " * 10^%d\n", m10, e10);
|
|
147
|
+
#endif
|
|
148
|
+
|
|
149
|
+
if ((m10digits + e10 <= -324) || (m10 == 0)) {
|
|
150
|
+
// Number is less than 1e-324, which should be rounded down to 0; return +/-0.0.
|
|
151
|
+
uint64_t ieee = ((uint64_t) signedM) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS);
|
|
152
|
+
*result = int64Bits2Double(ieee);
|
|
153
|
+
return TEPTRIS_RYU_SUCCESS;
|
|
154
|
+
}
|
|
155
|
+
if (m10digits + e10 >= 310) {
|
|
156
|
+
// Number is larger than 1e+309, which should be rounded to +/-Infinity.
|
|
157
|
+
uint64_t ieee = (((uint64_t) signedM) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS)) | (0x7ffull << DOUBLE_MANTISSA_BITS);
|
|
158
|
+
*result = int64Bits2Double(ieee);
|
|
159
|
+
return TEPTRIS_RYU_SUCCESS;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Convert to binary float m2 * 2^e2, while retaining information about whether the conversion
|
|
163
|
+
// was exact (trailingZeros).
|
|
164
|
+
int32_t e2;
|
|
165
|
+
uint64_t m2;
|
|
166
|
+
bool trailingZeros;
|
|
167
|
+
if (e10 >= 0) {
|
|
168
|
+
// The length of m * 10^e in bits is:
|
|
169
|
+
// log2(m10 * 10^e10) = log2(m10) + e10 log2(10) = log2(m10) + e10 + e10 * log2(5)
|
|
170
|
+
//
|
|
171
|
+
// We want to compute the DOUBLE_MANTISSA_BITS + 1 top-most bits (+1 for the implicit leading
|
|
172
|
+
// one in IEEE format). We therefore choose a binary output exponent of
|
|
173
|
+
// log2(m10 * 10^e10) - (DOUBLE_MANTISSA_BITS + 1).
|
|
174
|
+
//
|
|
175
|
+
// We use floor(log2(5^e10)) so that we get at least this many bits; better to
|
|
176
|
+
// have an additional bit than to not have enough bits.
|
|
177
|
+
e2 = floor_log2(m10) + e10 + log2pow5(e10) - (DOUBLE_MANTISSA_BITS + 1);
|
|
178
|
+
|
|
179
|
+
// We now compute [m10 * 10^e10 / 2^e2] = [m10 * 5^e10 / 2^(e2-e10)].
|
|
180
|
+
// To that end, we use the DOUBLE_POW5_SPLIT table.
|
|
181
|
+
int j = e2 - e10 - ceil_log2pow5(e10) + DOUBLE_POW5_BITCOUNT;
|
|
182
|
+
assert(j >= 0);
|
|
183
|
+
#if defined(RYU_OPTIMIZE_SIZE)
|
|
184
|
+
uint64_t pow5[2];
|
|
185
|
+
double_computePow5(e10, pow5);
|
|
186
|
+
m2 = mulShift64(m10, pow5, j);
|
|
187
|
+
#else
|
|
188
|
+
assert(e10 < DOUBLE_POW5_TABLE_SIZE);
|
|
189
|
+
m2 = mulShift64(m10, DOUBLE_POW5_SPLIT[e10], j);
|
|
190
|
+
#endif
|
|
191
|
+
// We also compute if the result is exact, i.e.,
|
|
192
|
+
// [m10 * 10^e10 / 2^e2] == m10 * 10^e10 / 2^e2.
|
|
193
|
+
// This can only be the case if 2^e2 divides m10 * 10^e10, which in turn requires that the
|
|
194
|
+
// largest power of 2 that divides m10 + e10 is greater than e2. If e2 is less than e10, then
|
|
195
|
+
// the result must be exact. Otherwise we use the existing multipleOfPowerOf2 function.
|
|
196
|
+
trailingZeros = e2 < e10 || (e2 - e10 < 64 && multipleOfPowerOf2(m10, e2 - e10));
|
|
197
|
+
} else {
|
|
198
|
+
e2 = floor_log2(m10) + e10 - ceil_log2pow5(-e10) - (DOUBLE_MANTISSA_BITS + 1);
|
|
199
|
+
int j = e2 - e10 + ceil_log2pow5(-e10) - 1 + DOUBLE_POW5_INV_BITCOUNT;
|
|
200
|
+
#if defined(RYU_OPTIMIZE_SIZE)
|
|
201
|
+
uint64_t pow5[2];
|
|
202
|
+
double_computeInvPow5(-e10, pow5);
|
|
203
|
+
m2 = mulShift64(m10, pow5, j);
|
|
204
|
+
#else
|
|
205
|
+
assert(-e10 < DOUBLE_POW5_INV_TABLE_SIZE);
|
|
206
|
+
m2 = mulShift64(m10, DOUBLE_POW5_INV_SPLIT[-e10], j);
|
|
207
|
+
#endif
|
|
208
|
+
trailingZeros = multipleOfPowerOf5(m10, -e10);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#ifdef RYU_DEBUG
|
|
212
|
+
printf("m2 * 2^e2 = %" PRIu64 " * 2^%d\n", m2, e2);
|
|
213
|
+
#endif
|
|
214
|
+
|
|
215
|
+
// Compute the final IEEE exponent.
|
|
216
|
+
uint32_t ieee_e2 = (uint32_t) max32(0, e2 + DOUBLE_EXPONENT_BIAS + floor_log2(m2));
|
|
217
|
+
|
|
218
|
+
if (ieee_e2 > 0x7fe) {
|
|
219
|
+
// Final IEEE exponent is larger than the maximum representable; return +/-Infinity.
|
|
220
|
+
uint64_t ieee = (((uint64_t) signedM) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS)) | (0x7ffull << DOUBLE_MANTISSA_BITS);
|
|
221
|
+
*result = int64Bits2Double(ieee);
|
|
222
|
+
return TEPTRIS_RYU_SUCCESS;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// We need to figure out how much we need to shift m2. The tricky part is that we need to take
|
|
226
|
+
// the final IEEE exponent into account, so we need to reverse the bias and also special-case
|
|
227
|
+
// the value 0.
|
|
228
|
+
int32_t shift = (ieee_e2 == 0 ? 1 : ieee_e2) - e2 - DOUBLE_EXPONENT_BIAS - DOUBLE_MANTISSA_BITS;
|
|
229
|
+
assert(shift >= 0);
|
|
230
|
+
#ifdef RYU_DEBUG
|
|
231
|
+
printf("ieee_e2 = %d\n", ieee_e2);
|
|
232
|
+
printf("shift = %d\n", shift);
|
|
233
|
+
#endif
|
|
234
|
+
|
|
235
|
+
// We need to round up if the exact value is more than 0.5 above the value we computed. That's
|
|
236
|
+
// equivalent to checking if the last removed bit was 1 and either the value was not just
|
|
237
|
+
// trailing zeros or the result would otherwise be odd.
|
|
238
|
+
//
|
|
239
|
+
// We need to update trailingZeros given that we have the exact output exponent ieee_e2 now.
|
|
240
|
+
trailingZeros &= (m2 & ((1ull << (shift - 1)) - 1)) == 0;
|
|
241
|
+
uint64_t lastRemovedBit = (m2 >> (shift - 1)) & 1;
|
|
242
|
+
bool roundUp = (lastRemovedBit != 0) && (!trailingZeros || (((m2 >> shift) & 1) != 0));
|
|
243
|
+
|
|
244
|
+
#ifdef RYU_DEBUG
|
|
245
|
+
printf("roundUp = %d\n", roundUp);
|
|
246
|
+
printf("ieee_m2 = %" PRIu64 "\n", (m2 >> shift) + roundUp);
|
|
247
|
+
#endif
|
|
248
|
+
uint64_t ieee_m2 = (m2 >> shift) + roundUp;
|
|
249
|
+
assert(ieee_m2 <= (1ull << (DOUBLE_MANTISSA_BITS + 1)));
|
|
250
|
+
ieee_m2 &= (1ull << DOUBLE_MANTISSA_BITS) - 1;
|
|
251
|
+
if (ieee_m2 == 0 && roundUp) {
|
|
252
|
+
// Due to how the IEEE represents +/-Infinity, we don't need to check for overflow here.
|
|
253
|
+
ieee_e2++;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
uint64_t ieee = (((((uint64_t) signedM) << DOUBLE_EXPONENT_BITS) | (uint64_t)ieee_e2) << DOUBLE_MANTISSA_BITS) | ieee_m2;
|
|
257
|
+
*result = int64Bits2Double(ieee);
|
|
258
|
+
return TEPTRIS_RYU_SUCCESS;
|
|
259
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* common/simd.h — length-guarded SIMD scan kernels (leptris lineage).
|
|
2
|
+
*
|
|
3
|
+
* Techniques ported from libleptris's simd_text framework:
|
|
4
|
+
* - vceqq/_mm_cmpeq + horizontal-nonzero early exit (find)
|
|
5
|
+
* - widen-then-add population counts (vpaddlq_u16/vaddvq_u16; the raw
|
|
6
|
+
* byte add would truncate mod 256 at density >= 2)
|
|
7
|
+
* - NEON movemask emulation: narrow mask >> 7, widen, shift each lane
|
|
8
|
+
* by its own index (vector shift), horizontal add — weights sum to
|
|
9
|
+
* the exact bitmask because every bit appears exactly once
|
|
10
|
+
* - chunk only while a full vector remains; scalar tail; never read
|
|
11
|
+
* past len
|
|
12
|
+
*
|
|
13
|
+
* ISA selection is baseline-only (NEON is architectural on aarch64,
|
|
14
|
+
* SSE2 on x86-64), so no runtime dispatch and no extra compile flags.
|
|
15
|
+
* Kernels return the first matching OFFSET, or len when nothing
|
|
16
|
+
* matched — callers test against len, never -1. */
|
|
17
|
+
#ifndef TEPTRIS_COMMON_SIMD_H
|
|
18
|
+
#define TEPTRIS_COMMON_SIMD_H
|
|
19
|
+
|
|
20
|
+
#include <stdbool.h>
|
|
21
|
+
#include <stddef.h>
|
|
22
|
+
#include <stdint.h>
|
|
23
|
+
#include <string.h>
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#if defined(_MSC_VER)
|
|
27
|
+
#include <intrin.h>
|
|
28
|
+
static inline unsigned tep_ctz32(unsigned x)
|
|
29
|
+
{
|
|
30
|
+
unsigned long i;
|
|
31
|
+
_BitScanForward(&i, x);
|
|
32
|
+
return (unsigned)i;
|
|
33
|
+
}
|
|
34
|
+
#else
|
|
35
|
+
static inline unsigned tep_ctz32(unsigned x) { return (unsigned)__builtin_ctz(x); }
|
|
36
|
+
#endif
|
|
37
|
+
|
|
38
|
+
#if defined(__aarch64__)
|
|
39
|
+
#define TEPTRIS_SIMD_NEON 1
|
|
40
|
+
#include <arm_neon.h>
|
|
41
|
+
#elif defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64)
|
|
42
|
+
#define TEPTRIS_SIMD_SSE2 1
|
|
43
|
+
#include <emmintrin.h>
|
|
44
|
+
#endif
|
|
45
|
+
|
|
46
|
+
/* NEON: 16-bit mask from a byte-compare vector (leptris trick). */
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/* String-content stop scan: first offset where p[i] is q or e (either
|
|
50
|
+
* may be 0 to disable) or a control byte (c < 0x20, tab allowed) or
|
|
51
|
+
* 0x7F. len when absent. This is the combined terminator/validation
|
|
52
|
+
* scan for basic and literal strings and for comment bodies — one
|
|
53
|
+
* vector pass replaces per-byte quote/backslash/control checks. */
|
|
54
|
+
static inline size_t tep_simd_str_stop(const char *p, size_t len, char q,
|
|
55
|
+
char e)
|
|
56
|
+
{
|
|
57
|
+
size_t i = 0;
|
|
58
|
+
#if defined(TEPTRIS_SIMD_NEON)
|
|
59
|
+
const uint8x16_t kq = vdupq_n_u8((uint8_t)q);
|
|
60
|
+
const uint8x16_t ke = vdupq_n_u8((uint8_t)e);
|
|
61
|
+
const uint8x16_t tab = vdupq_n_u8('\t');
|
|
62
|
+
const uint8x16_t lo = vdupq_n_u8(0x20);
|
|
63
|
+
const uint8x16_t del = vdupq_n_u8(0x7F);
|
|
64
|
+
for (; len - i >= 16; i += 16) {
|
|
65
|
+
uint8x16_t v = vld1q_u8((const uint8_t *)(p + i));
|
|
66
|
+
uint8x16_t ctrl = vandq_u8(vcltq_u8(v, lo), vmvnq_u8(vceqq_u8(v, tab)));
|
|
67
|
+
uint8x16_t m = vorrq_u8(ctrl, vceqq_u8(v, del));
|
|
68
|
+
if (q != 0) {
|
|
69
|
+
m = vorrq_u8(m, vceqq_u8(v, kq));
|
|
70
|
+
}
|
|
71
|
+
if (e != 0) {
|
|
72
|
+
m = vorrq_u8(m, vceqq_u8(v, ke));
|
|
73
|
+
}
|
|
74
|
+
if (vmaxvq_u8(m) != 0) {
|
|
75
|
+
uint8_t bytes[16];
|
|
76
|
+
vst1q_u8(bytes, m);
|
|
77
|
+
for (int k = 0; k < 16; k++) {
|
|
78
|
+
if (bytes[k]) {
|
|
79
|
+
return i + (size_t)k;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
#elif defined(TEPTRIS_SIMD_SSE2)
|
|
85
|
+
const __m128i zero = _mm_setzero_si128();
|
|
86
|
+
const __m128i kq = _mm_set1_epi8(q);
|
|
87
|
+
const __m128i ke = _mm_set1_epi8(e);
|
|
88
|
+
const __m128i sp = _mm_set1_epi8(' ');
|
|
89
|
+
const __m128i tab = _mm_set1_epi8('\t');
|
|
90
|
+
const __m128i k20 = _mm_set1_epi8(0x20);
|
|
91
|
+
const __m128i del = _mm_set1_epi8((char)0x7F);
|
|
92
|
+
for (; len - i >= 16; i += 16) {
|
|
93
|
+
__m128i v = _mm_loadu_si128((const __m128i *)(p + i));
|
|
94
|
+
__m128i below = _mm_cmpeq_epi8(_mm_subs_epu8(v, k20), zero); /* c<=0x20 */
|
|
95
|
+
__m128i ctrl = _mm_andnot_si128(
|
|
96
|
+
_mm_or_si128(_mm_cmpeq_epi8(v, sp), _mm_cmpeq_epi8(v, tab)), below);
|
|
97
|
+
__m128i m = _mm_or_si128(ctrl, _mm_cmpeq_epi8(v, del));
|
|
98
|
+
if (q != 0) {
|
|
99
|
+
m = _mm_or_si128(m, _mm_cmpeq_epi8(v, kq));
|
|
100
|
+
}
|
|
101
|
+
if (e != 0) {
|
|
102
|
+
m = _mm_or_si128(m, _mm_cmpeq_epi8(v, ke));
|
|
103
|
+
}
|
|
104
|
+
unsigned mask = (unsigned)_mm_movemask_epi8(m);
|
|
105
|
+
if (mask != 0) {
|
|
106
|
+
return i + tep_ctz32(mask);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
#endif
|
|
110
|
+
for (; i < len; i++) {
|
|
111
|
+
unsigned char c = (unsigned char)p[i];
|
|
112
|
+
if (c == q || c == e || (c < 0x20 && c != '\t') || c == 0x7F) {
|
|
113
|
+
return i;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return len;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#endif /* TEPTRIS_COMMON_SIMD_H */
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#include "teptris/dom/dom.h"
|
|
2
|
+
|
|
3
|
+
#include <string.h>
|
|
4
|
+
|
|
5
|
+
#include "teptris/memory/arena.h"
|
|
6
|
+
|
|
7
|
+
uint64_t teptris_dom_key_hash(const char *key, size_t key_len)
|
|
8
|
+
{
|
|
9
|
+
uint64_t h = 1469598103934665603ULL;
|
|
10
|
+
for (size_t i = 0; i < key_len; i++) {
|
|
11
|
+
h ^= (unsigned char)key[i];
|
|
12
|
+
h *= 1099511628211ULL;
|
|
13
|
+
}
|
|
14
|
+
return h;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
teptris_node *teptris_dom_new_node(teptris_document *doc, teptris_kind kind)
|
|
18
|
+
{
|
|
19
|
+
teptris_node *n = teptris_arena_fast_alloc(&doc->arena,
|
|
20
|
+
sizeof(teptris_node));
|
|
21
|
+
if (n == NULL) {
|
|
22
|
+
return NULL;
|
|
23
|
+
}
|
|
24
|
+
memset(n, 0, sizeof(*n));
|
|
25
|
+
n->kind = (uint8_t)kind;
|
|
26
|
+
return n;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
teptris_node *teptris_dom_new_table(teptris_document *doc, uint8_t flags)
|
|
30
|
+
{
|
|
31
|
+
teptris_node *n = teptris_dom_new_node(doc, TEPTRIS_TABLE);
|
|
32
|
+
if (n != NULL) {
|
|
33
|
+
n->flags = flags;
|
|
34
|
+
}
|
|
35
|
+
return n;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
teptris_node *teptris_dom_new_array(teptris_document *doc, uint8_t flags)
|
|
39
|
+
{
|
|
40
|
+
teptris_node *n = teptris_dom_new_node(doc, TEPTRIS_ARRAY);
|
|
41
|
+
if (n != NULL) {
|
|
42
|
+
n->flags = flags;
|
|
43
|
+
}
|
|
44
|
+
return n;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static void index_insert(teptris_node *t, uint32_t entry_idx, uint64_t h)
|
|
48
|
+
{
|
|
49
|
+
size_t mask = t->as.table.idx_cap - 1;
|
|
50
|
+
size_t slot = (size_t)h & mask;
|
|
51
|
+
while (t->as.table.index[slot] != 0) {
|
|
52
|
+
slot = (slot + 1) & mask;
|
|
53
|
+
}
|
|
54
|
+
t->as.table.index[slot] = entry_idx + 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static teptris_status index_rebuild(teptris_document *doc, teptris_node *t,
|
|
58
|
+
size_t new_cap)
|
|
59
|
+
{
|
|
60
|
+
uint32_t *idx = teptris_arena_alloc(&doc->arena, new_cap * sizeof(uint32_t));
|
|
61
|
+
if (idx == NULL) {
|
|
62
|
+
return TEPTRIS_ERR_ALLOC;
|
|
63
|
+
}
|
|
64
|
+
memset(idx, 0, new_cap * sizeof(uint32_t));
|
|
65
|
+
t->as.table.index = idx;
|
|
66
|
+
t->as.table.idx_cap = new_cap;
|
|
67
|
+
for (size_t i = 0; i < t->as.table.len; i++) {
|
|
68
|
+
index_insert(t, (uint32_t)i, t->as.table.entries[i].hash);
|
|
69
|
+
}
|
|
70
|
+
return TEPTRIS_OK;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
teptris_entry *teptris_dom_table_find_h(const teptris_node *t, const char *key,
|
|
74
|
+
size_t key_len, uint64_t *hash_out)
|
|
75
|
+
{
|
|
76
|
+
if (t == NULL || t->kind != TEPTRIS_TABLE || t->as.table.idx_cap == 0) {
|
|
77
|
+
*hash_out = teptris_dom_key_hash(key, key_len);
|
|
78
|
+
return NULL;
|
|
79
|
+
}
|
|
80
|
+
uint64_t h = teptris_dom_key_hash(key, key_len);
|
|
81
|
+
*hash_out = h;
|
|
82
|
+
size_t mask = t->as.table.idx_cap - 1;
|
|
83
|
+
size_t slot = (size_t)h & mask;
|
|
84
|
+
for (;;) {
|
|
85
|
+
uint32_t e = t->as.table.index[slot];
|
|
86
|
+
if (e == 0) {
|
|
87
|
+
return NULL;
|
|
88
|
+
}
|
|
89
|
+
teptris_entry *entry = &t->as.table.entries[e - 1];
|
|
90
|
+
if (entry->key.len == key_len &&
|
|
91
|
+
memcmp(entry->key.ptr, key, key_len) == 0) {
|
|
92
|
+
return entry;
|
|
93
|
+
}
|
|
94
|
+
slot = (slot + 1) & mask;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
teptris_entry *teptris_dom_table_find(const teptris_node *t, const char *key,
|
|
99
|
+
size_t key_len)
|
|
100
|
+
{
|
|
101
|
+
uint64_t h;
|
|
102
|
+
return teptris_dom_table_find_h(t, key, key_len, &h);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
teptris_status teptris_dom_table_insert(teptris_document *doc, teptris_node *t,
|
|
106
|
+
teptris_view key, teptris_node *value)
|
|
107
|
+
{
|
|
108
|
+
return teptris_dom_table_insert_h(doc, t, key,
|
|
109
|
+
teptris_dom_key_hash(key.ptr, key.len),
|
|
110
|
+
value);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
teptris_status teptris_dom_table_insert_h(teptris_document *doc, teptris_node *t,
|
|
114
|
+
teptris_view key, uint64_t hash,
|
|
115
|
+
teptris_node *value)
|
|
116
|
+
{
|
|
117
|
+
return teptris_dom_table_insert_slot(doc, t, key, hash, value, SIZE_MAX);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
teptris_status teptris_dom_table_insert_slot(teptris_document *doc,
|
|
121
|
+
teptris_node *t,
|
|
122
|
+
teptris_view key, uint64_t hash,
|
|
123
|
+
teptris_node *value, size_t slot)
|
|
124
|
+
{
|
|
125
|
+
teptris_arena *a = &doc->arena;
|
|
126
|
+
|
|
127
|
+
/* load factor FIRST: a rebuild re-probes every entry and any
|
|
128
|
+
* pre-resolved slot becomes stale */
|
|
129
|
+
if ((t->as.table.len + 1) * 10 >= t->as.table.idx_cap * 7) {
|
|
130
|
+
size_t ncap = t->as.table.idx_cap ? t->as.table.idx_cap * 2 : 16;
|
|
131
|
+
teptris_status st = index_rebuild(doc, t, ncap);
|
|
132
|
+
if (st != TEPTRIS_OK) {
|
|
133
|
+
return st;
|
|
134
|
+
}
|
|
135
|
+
slot = SIZE_MAX;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (t->as.table.len == t->as.table.cap) {
|
|
139
|
+
size_t ncap = t->as.table.cap ? t->as.table.cap * 2 : 8;
|
|
140
|
+
teptris_entry *ne =
|
|
141
|
+
teptris_arena_try_grow(a, t->as.table.entries,
|
|
142
|
+
t->as.table.cap * sizeof(teptris_entry),
|
|
143
|
+
ncap * sizeof(teptris_entry));
|
|
144
|
+
if (ne == NULL) {
|
|
145
|
+
ne = teptris_arena_alloc(a, ncap * sizeof(teptris_entry));
|
|
146
|
+
if (ne == NULL) {
|
|
147
|
+
return TEPTRIS_ERR_ALLOC;
|
|
148
|
+
}
|
|
149
|
+
memcpy(ne, t->as.table.entries,
|
|
150
|
+
t->as.table.len * sizeof(teptris_entry));
|
|
151
|
+
t->as.table.entries = ne;
|
|
152
|
+
}
|
|
153
|
+
t->as.table.cap = ncap;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
t->as.table.entries[t->as.table.len].key = key;
|
|
157
|
+
t->as.table.entries[t->as.table.len].value = value;
|
|
158
|
+
t->as.table.entries[t->as.table.len].hash = hash;
|
|
159
|
+
if (slot != SIZE_MAX) {
|
|
160
|
+
t->as.table.index[slot] = (uint32_t)t->as.table.len + 1;
|
|
161
|
+
} else {
|
|
162
|
+
index_insert(t, (uint32_t)t->as.table.len, hash);
|
|
163
|
+
}
|
|
164
|
+
t->as.table.len++;
|
|
165
|
+
return TEPTRIS_OK;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
teptris_status teptris_dom_array_push(teptris_document *doc, teptris_node *a,
|
|
169
|
+
teptris_node *value)
|
|
170
|
+
{
|
|
171
|
+
teptris_arena *ar = &doc->arena;
|
|
172
|
+
|
|
173
|
+
if (a->as.array.len == a->as.array.cap) {
|
|
174
|
+
size_t ncap = a->as.array.cap ? a->as.array.cap * 2 : 8;
|
|
175
|
+
teptris_node **ni =
|
|
176
|
+
teptris_arena_try_grow(ar, a->as.array.items,
|
|
177
|
+
a->as.array.cap * sizeof(teptris_node *),
|
|
178
|
+
ncap * sizeof(teptris_node *));
|
|
179
|
+
if (ni == NULL) {
|
|
180
|
+
ni = teptris_arena_alloc(ar, ncap * sizeof(teptris_node *));
|
|
181
|
+
if (ni == NULL) {
|
|
182
|
+
return TEPTRIS_ERR_ALLOC;
|
|
183
|
+
}
|
|
184
|
+
memcpy(ni, a->as.array.items,
|
|
185
|
+
a->as.array.len * sizeof(teptris_node *));
|
|
186
|
+
a->as.array.items = ni;
|
|
187
|
+
}
|
|
188
|
+
a->as.array.cap = ncap;
|
|
189
|
+
}
|
|
190
|
+
a->as.array.items[a->as.array.len++] = value;
|
|
191
|
+
return TEPTRIS_OK;
|
|
192
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#ifndef TEPTRIS_DOM_H
|
|
2
|
+
#define TEPTRIS_DOM_H
|
|
3
|
+
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
#include "teptris/memory/arena.h"
|
|
8
|
+
#include "teptris/teptris.h"
|
|
9
|
+
|
|
10
|
+
/* Table define-semantics flags (parser-only state, invisible to readers). */
|
|
11
|
+
#define TBL_EXPLICIT 0x01u /* named by a [table] header (or root) */
|
|
12
|
+
#define TBL_IMPLICIT 0x02u /* intermediate of a header path */
|
|
13
|
+
#define TBL_DOTTED 0x04u /* intermediate of a dotted key */
|
|
14
|
+
#define TBL_INLINE 0x08u /* inline table value: immutable */
|
|
15
|
+
#define ARR_AOT 0x10u /* array created by [[...]] headers */
|
|
16
|
+
|
|
17
|
+
typedef struct teptris_entry {
|
|
18
|
+
teptris_view key; /* view into the parse input */
|
|
19
|
+
teptris_node *value;
|
|
20
|
+
uint64_t hash; /* cached: index rebuilds never re-hash keys */
|
|
21
|
+
} teptris_entry;
|
|
22
|
+
|
|
23
|
+
struct teptris_node {
|
|
24
|
+
uint8_t kind;
|
|
25
|
+
uint8_t flags;
|
|
26
|
+
union {
|
|
27
|
+
teptris_view str; /* NUL-terminated arena copy */
|
|
28
|
+
int64_t i;
|
|
29
|
+
double f;
|
|
30
|
+
bool b;
|
|
31
|
+
teptris_datetime dt;
|
|
32
|
+
struct {
|
|
33
|
+
teptris_entry *entries; /* insertion order */
|
|
34
|
+
size_t len, cap;
|
|
35
|
+
uint32_t *index; /* entry_idx+1; 0 = empty; linear probe */
|
|
36
|
+
size_t idx_cap;
|
|
37
|
+
} table;
|
|
38
|
+
struct {
|
|
39
|
+
struct teptris_node **items;
|
|
40
|
+
size_t len, cap;
|
|
41
|
+
} array;
|
|
42
|
+
} as;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/* C99/MSVC-C-mode/nd C++ portable static assert (negative array
|
|
46
|
+
* size on violation); _Static_assert would pin us to C11+ */
|
|
47
|
+
#if defined(__cplusplus)
|
|
48
|
+
#define TEP_SA(cond, name) static_assert(cond, #name)
|
|
49
|
+
#else
|
|
50
|
+
#define TEP_SA(cond, name) typedef char tep_sa_##name[(cond) ? 1 : -1]
|
|
51
|
+
#endif
|
|
52
|
+
TEP_SA(sizeof(teptris_node) <= 64, node_fits_64);
|
|
53
|
+
|
|
54
|
+
struct teptris_document {
|
|
55
|
+
teptris_arena arena;
|
|
56
|
+
teptris_node *root;
|
|
57
|
+
teptris_error err;
|
|
58
|
+
char err_msg[192];
|
|
59
|
+
uint32_t max_depth;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/* Constructors (arena-owned, zeroed). Return NULL on allocation failure. */
|
|
63
|
+
teptris_node *teptris_dom_new_node(teptris_document *doc, teptris_kind kind);
|
|
64
|
+
teptris_node *teptris_dom_new_table(teptris_document *doc, uint8_t flags);
|
|
65
|
+
teptris_node *teptris_dom_new_array(teptris_document *doc, uint8_t flags);
|
|
66
|
+
|
|
67
|
+
/* Find by key (hash index); NULL when absent. */
|
|
68
|
+
teptris_entry *teptris_dom_table_find(const teptris_node *table, const char *key,
|
|
69
|
+
size_t key_len);
|
|
70
|
+
|
|
71
|
+
/* Single-hash variants: find also reports the key hash so a following
|
|
72
|
+
* insert does not rehash (the common insert-dotted/resolve flow). */
|
|
73
|
+
uint64_t teptris_dom_key_hash(const char *key, size_t key_len);
|
|
74
|
+
teptris_entry *teptris_dom_table_find_h(const teptris_node *table,
|
|
75
|
+
const char *key, size_t key_len,
|
|
76
|
+
uint64_t *hash_out);
|
|
77
|
+
|
|
78
|
+
/* Probe with a PRECOMPUTED hash that also reports the empty index
|
|
79
|
+
* slot the walk stopped at, for reuse by insert_slot — one walk for
|
|
80
|
+
* the whole miss->insert sequence (header and keyval paths).
|
|
81
|
+
* *slot_out is SIZE_MAX when the slot cannot be reused (empty table,
|
|
82
|
+
* non-table). Must be header-inline: cross-TU, the call cost ate the
|
|
83
|
+
* measured gain last time. */
|
|
84
|
+
static inline teptris_entry *
|
|
85
|
+
teptris_dom_table_probe_slot(const teptris_node *t, uint64_t hash,
|
|
86
|
+
const char *key, size_t key_len, size_t *slot_out)
|
|
87
|
+
{
|
|
88
|
+
*slot_out = SIZE_MAX;
|
|
89
|
+
if (t == NULL || t->kind != TEPTRIS_TABLE || t->as.table.idx_cap == 0) {
|
|
90
|
+
return NULL;
|
|
91
|
+
}
|
|
92
|
+
size_t mask = t->as.table.idx_cap - 1;
|
|
93
|
+
size_t slot = (size_t)hash & mask;
|
|
94
|
+
while (t->as.table.index[slot] != 0) {
|
|
95
|
+
uint32_t ei = t->as.table.index[slot] - 1;
|
|
96
|
+
const teptris_entry *e = &t->as.table.entries[ei];
|
|
97
|
+
if (e->hash == hash && e->key.len == key_len &&
|
|
98
|
+
memcmp(e->key.ptr, key, key_len) == 0) {
|
|
99
|
+
return (teptris_entry *)e;
|
|
100
|
+
}
|
|
101
|
+
slot = (slot + 1) & mask;
|
|
102
|
+
}
|
|
103
|
+
*slot_out = slot;
|
|
104
|
+
return NULL;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Insert; the key must not already exist (caller checks). */
|
|
108
|
+
teptris_status teptris_dom_table_insert(teptris_document *doc, teptris_node *table,
|
|
109
|
+
teptris_view key, teptris_node *value);
|
|
110
|
+
teptris_status teptris_dom_table_insert_h(teptris_document *doc,
|
|
111
|
+
teptris_node *table, teptris_view key,
|
|
112
|
+
uint64_t hash, teptris_node *value);
|
|
113
|
+
/* insert_h with the empty index slot pre-resolved by probe_slot
|
|
114
|
+
* (SIZE_MAX = unresolved). A load-factor rebuild invalidates the
|
|
115
|
+
* slot internally and falls back to a fresh probe. */
|
|
116
|
+
teptris_status teptris_dom_table_insert_slot(teptris_document *doc,
|
|
117
|
+
teptris_node *table,
|
|
118
|
+
teptris_view key, uint64_t hash,
|
|
119
|
+
teptris_node *value, size_t slot);
|
|
120
|
+
|
|
121
|
+
teptris_status teptris_dom_array_push(teptris_document *doc, teptris_node *array,
|
|
122
|
+
teptris_node *value);
|
|
123
|
+
|
|
124
|
+
#endif /* TEPTRIS_DOM_H */
|