mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Grow the ASCII run before walking for forward index reads (#2502)
This commit is contained in:
@@ -1756,6 +1756,36 @@ static __attribute__((noinline)) size_t scr_u16_to_byte_walk(const ScrStr *s, Sc
|
||||
e->cb = cb;
|
||||
return cb;
|
||||
}
|
||||
/* A forward access past the cached ASCII run (a scanner taking slices at
|
||||
* increasing positions of a mostly ASCII text) grows the run over the
|
||||
* ASCII bytes up to it. When a non-ASCII character ends the run first, a
|
||||
* nearby access walks from the run's end or the hot cursor, whichever is
|
||||
* nearer, and a new run starts where the walk lands, so the accesses after
|
||||
* it map by arithmetic again. */
|
||||
if (u16 >= e->ascii_cu) {
|
||||
if (scr_sidx_ascii_grow(s, e, e->ascii_cb + (u16 - e->ascii_cu))) {
|
||||
e->cb = e->ascii_cb + (u16 - e->ascii_cu);
|
||||
e->cu = u16;
|
||||
*mid = false;
|
||||
return e->cb;
|
||||
}
|
||||
size_t run_cu = e->ascii_cu + (e->ascii_end - e->ascii_cb);
|
||||
if (e->ascii_end < s->len && u16 - run_cu <= SCR_SIDX_NEAR_UNITS) {
|
||||
size_t cu = run_cu, cb = e->ascii_end;
|
||||
if (e->cb <= s->len && e->cu >= cu && e->cu <= u16) {
|
||||
cu = e->cu;
|
||||
cb = e->cb;
|
||||
}
|
||||
size_t at = scr_sidx_walk_from(s, e, cu, cb, u16, mid, false);
|
||||
if (!*mid && at < s->len && (unsigned char)s->data[at] < 0x80) {
|
||||
e->ascii_cu = e->cu;
|
||||
e->ascii_cb = at;
|
||||
e->ascii_end = at;
|
||||
(void)scr_sidx_ascii_grow(s, e, at);
|
||||
}
|
||||
return at;
|
||||
}
|
||||
}
|
||||
/* Within a short distance of the hot cursor (a scanner
|
||||
* taking slices near its position), walk from that cursor: no index
|
||||
* search, extension or ASCII-run growth is cheaper. */
|
||||
@@ -1785,12 +1815,6 @@ static __attribute__((noinline)) size_t scr_u16_to_byte_walk(const ScrStr *s, Sc
|
||||
return cb;
|
||||
}
|
||||
}
|
||||
if (u16 >= e->ascii_cu && scr_sidx_ascii_grow(s, e, e->ascii_cb + (u16 - e->ascii_cu))) {
|
||||
e->cb = e->ascii_cb + (u16 - e->ascii_cu);
|
||||
e->cu = u16;
|
||||
*mid = false;
|
||||
return e->cb;
|
||||
}
|
||||
scr_sidx_extend_to_u16(s, e, u16);
|
||||
/* Extending a far-end lookup can just have proved identity. Do not
|
||||
* materialize an index that the identity fast path will never consult. */
|
||||
|
||||
@@ -927,6 +927,54 @@ static void local_navigation_asserts(void) {
|
||||
scr_sidx_test_reset_cache();
|
||||
}
|
||||
|
||||
/* A scanner reading a mostly ASCII text front to back (positioned
|
||||
* startsWith and substrings at increasing positions, with a non-ASCII
|
||||
* character every 300 bytes) maps its positions through the ASCII run, so
|
||||
* it walks only across the non-ASCII characters. */
|
||||
static void forward_scan_asserts(void) {
|
||||
enum { ASCII = 300, UNITS = ASCII + 1, REPS = 100 };
|
||||
char piece[ASCII + 2];
|
||||
for (size_t i = 0; i < ASCII; i++) piece[i] = (char)('a' + i % 26);
|
||||
memcpy(piece + ASCII, "\xC3\xA9", 2); /* é: one unit */
|
||||
ScrStr *unit = scr_str_new(piece, sizeof piece);
|
||||
ScrStr *s = scr_str_repeat(unit, REPS);
|
||||
scr_str_release(unit);
|
||||
scr_sidx_test_reset_cache();
|
||||
if (scr_str_utf16_len(s) != UNITS * REPS) sidx_fail("forward scan length");
|
||||
ScrStr *needle = scr_str_new("ab", 2);
|
||||
scr_sidx_test_reset_steps();
|
||||
for (size_t at = 0; at + 2 <= UNITS * REPS; at += 5) {
|
||||
size_t offset = at % UNITS;
|
||||
bool expected = offset + 1 < ASCII && offset % 26 == 0;
|
||||
if (scr_str_starts_with_from(s, needle, (double)at) != expected) {
|
||||
sidx_fail("forward startsWith");
|
||||
break;
|
||||
}
|
||||
char want[4];
|
||||
size_t n = 0;
|
||||
for (size_t k = at; k < at + 2; k++) {
|
||||
size_t o = k % UNITS;
|
||||
if (o < ASCII) want[n++] = (char)('a' + o % 26);
|
||||
else {
|
||||
want[n++] = '\xC3';
|
||||
want[n++] = '\xA9';
|
||||
}
|
||||
}
|
||||
ScrStr *part = scr_str_substring(s, (double)at, (double)(at + 2));
|
||||
bool same = part->len == n && memcmp(part->data, want, n) == 0;
|
||||
scr_str_release(part);
|
||||
if (!same) {
|
||||
sidx_fail("forward substring");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (scr_sidx_test_walk_steps() > REPS * 16)
|
||||
sidx_fail("forward reads walked between non-ASCII characters");
|
||||
scr_str_release(needle);
|
||||
scr_str_release(s);
|
||||
scr_sidx_test_reset_cache();
|
||||
}
|
||||
|
||||
/* An entry borrowed for a receiver must remain its entry when a second
|
||||
* string needs indexing. Fill the cursor tier so the needle would evict
|
||||
* precisely the receiver under a receiver-first lookup order. */
|
||||
@@ -1244,6 +1292,7 @@ int main(int argc, char **argv) {
|
||||
index_activity_asserts();
|
||||
sparse_slice_churn_asserts();
|
||||
local_navigation_asserts();
|
||||
forward_scan_asserts();
|
||||
positioned_suffix_eviction_asserts();
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user