mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Adds strstr and memmem. (#275)
This commit is contained in:
@@ -23,7 +23,7 @@ EOF
|
||||
-mbulk-memory -mreference-types \
|
||||
-mnontrapping-fptoint -msign-ext \
|
||||
-fno-stack-protector -fno-stack-clash-protection \
|
||||
-Wl,-z,stack-size=1024 \
|
||||
-Wl,-z,stack-size=4096 \
|
||||
-Wl,--stack-first \
|
||||
-Wl,--import-undefined \
|
||||
-Wl,--initial-memory=16777216 \
|
||||
@@ -31,6 +31,7 @@ EOF
|
||||
-Wl,--export=memchr \
|
||||
-Wl,--export=memcmp \
|
||||
-Wl,--export=memcpy \
|
||||
-Wl,--export=memmem \
|
||||
-Wl,--export=memmove \
|
||||
-Wl,--export=memrchr \
|
||||
-Wl,--export=memset \
|
||||
@@ -47,6 +48,7 @@ EOF
|
||||
-Wl,--export=strncpy \
|
||||
-Wl,--export=strrchr \
|
||||
-Wl,--export=strspn \
|
||||
-Wl,--export=strstr \
|
||||
-Wl,--export=qsort
|
||||
|
||||
"$BINARYEN/wasm-ctor-eval" -g -c _initialize libc.wasm -o libc.tmp
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@ var (
|
||||
strlen api.Function
|
||||
strchr api.Function
|
||||
strcmp api.Function
|
||||
strstr api.Function
|
||||
strspn api.Function
|
||||
strrchr api.Function
|
||||
strncmp api.Function
|
||||
@@ -64,6 +65,7 @@ func TestMain(m *testing.M) {
|
||||
strlen = mod.ExportedFunction("strlen")
|
||||
strchr = mod.ExportedFunction("strchr")
|
||||
strcmp = mod.ExportedFunction("strcmp")
|
||||
strstr = mod.ExportedFunction("strstr")
|
||||
strspn = mod.ExportedFunction("strspn")
|
||||
strrchr = mod.ExportedFunction("strrchr")
|
||||
strncmp = mod.ExportedFunction("strncmp")
|
||||
@@ -210,6 +212,21 @@ func Benchmark_strcspn(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed string.h
|
||||
var source string
|
||||
|
||||
func Benchmark_strstr(b *testing.B) {
|
||||
clear(memory)
|
||||
copy(memory[ptr1:], source)
|
||||
copy(memory[ptr2:], "memcpy(dest, src, slen)")
|
||||
|
||||
b.SetBytes(int64(len(source)))
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
call(strstr, ptr1, ptr2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_memcmp(t *testing.T) {
|
||||
const s1 string = "" +
|
||||
"\x94\x63\x8f\x01\x74\x63\x8f\x01\x54\x63\x8f\x01\x34\x63\x8f\x01" +
|
||||
@@ -230,17 +247,16 @@ func Test_memcmp(t *testing.T) {
|
||||
"\x94\xf3\x93\x01\x74\x7f\x93\x01\x54\xf3\x93\x01\x34\xf3\x93\x01" +
|
||||
"\x80\xf3\x93\x01\x00\x02"
|
||||
|
||||
p1 := ptr1
|
||||
p2 := len(memory) - len(s2)
|
||||
ptr2 := len(memory) - len(s2)
|
||||
|
||||
clear(memory)
|
||||
copy(memory[p1:], s1)
|
||||
copy(memory[p2:], s2)
|
||||
copy(memory[ptr1:], s1)
|
||||
copy(memory[ptr2:], s2)
|
||||
|
||||
for i := range len(s1) + 1 {
|
||||
for j := range len(s1) - i {
|
||||
want := strings.Compare(s1[i:i+j], s2[i:i+j])
|
||||
got := call(memcmp, uint64(p1+i), uint64(p2+i), uint64(j))
|
||||
got := call(memcmp, uint64(ptr1+i), uint64(ptr2+i), uint64(j))
|
||||
if sign(int32(got)) != want {
|
||||
t.Errorf("strcmp(%d, %d, %d) = %d, want %d",
|
||||
ptr1+i, ptr2+i, j, int32(got), want)
|
||||
@@ -269,19 +285,18 @@ func Test_strcmp(t *testing.T) {
|
||||
"\x94\xf3\x93\x01\x74\x7f\x93\x01\x54\xf3\x93\x01\x34\xf3\x93\x01" +
|
||||
"\x80\xf3\x93\x01\x00\x02"
|
||||
|
||||
p1 := ptr1
|
||||
p2 := len(memory) - len(s2) - 1
|
||||
ptr2 := len(memory) - len(s2) - 1
|
||||
|
||||
clear(memory)
|
||||
copy(memory[p1:], s1)
|
||||
copy(memory[p2:], s2)
|
||||
copy(memory[ptr1:], s1)
|
||||
copy(memory[ptr2:], s2)
|
||||
|
||||
for i := range len(s1) + 1 {
|
||||
want := strings.Compare(term(s1[i:]), term(s2[i:]))
|
||||
got := call(strcmp, uint64(p1+i), uint64(p2+i))
|
||||
got := call(strcmp, uint64(ptr1+i), uint64(ptr2+i))
|
||||
if sign(int32(got)) != want {
|
||||
t.Errorf("strcmp(%d, %d) = %d, want %d",
|
||||
p1+i, ptr2+i, int32(got), want)
|
||||
ptr1+i, ptr2+i, int32(got), want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,17 +321,16 @@ func Test_strncmp(t *testing.T) {
|
||||
"\x94\xf3\x93\x01\x74\x7f\x93\x01\x54\xf3\x93\x01\x34\xf3\x93\x01" +
|
||||
"\x80\xf3\x93\x01\x00\x02"
|
||||
|
||||
p1 := ptr1
|
||||
p2 := len(memory) - len(s2) - 1
|
||||
ptr2 := len(memory) - len(s2) - 1
|
||||
|
||||
clear(memory)
|
||||
copy(memory[p1:], s1)
|
||||
copy(memory[p2:], s2)
|
||||
copy(memory[ptr1:], s1)
|
||||
copy(memory[ptr2:], s2)
|
||||
|
||||
for i := range len(s1) + 1 {
|
||||
for j := range len(s1) - i + 1 {
|
||||
want := strings.Compare(term(s1[i:i+j]), term(s2[i:i+j]))
|
||||
got := call(strncmp, uint64(p1+i), uint64(p2+i), uint64(j))
|
||||
got := call(strncmp, uint64(ptr1+i), uint64(ptr2+i), uint64(j))
|
||||
if sign(int32(got)) != want {
|
||||
t.Errorf("strncmp(%d, %d, %d) = %d, want %d",
|
||||
ptr1+i, ptr2+i, j, int32(got), want)
|
||||
@@ -595,6 +609,156 @@ func Test_strcspn(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_strstr(t *testing.T) {
|
||||
var tt = []struct {
|
||||
h string
|
||||
n string
|
||||
out int
|
||||
}{
|
||||
{"", "", 0},
|
||||
{"", "a", -1},
|
||||
{"", "foo", -1},
|
||||
{"fo", "foo", -1},
|
||||
{"foo", "foo", 0},
|
||||
{"oofofoofooo", "f", 2},
|
||||
{"oofofoofooo", "foo", 4},
|
||||
{"barfoobarfoo", "foo", 3},
|
||||
{"foo", "", 0},
|
||||
{"foo", "o", 1},
|
||||
{"abcABCabc", "A", 3},
|
||||
{"jrzm6jjhorimglljrea4w3rlgosts0w2gia17hno2td4qd1jz", "jz", 47},
|
||||
{"ekkuk5oft4eq0ocpacknhwouic1uua46unx12l37nioq9wbpnocqks6", "ks6", 52},
|
||||
{"999f2xmimunbuyew5vrkla9cpwhmxan8o98ec", "98ec", 33},
|
||||
{"9lpt9r98i04k8bz6c6dsrthb96bhi", "96bhi", 24},
|
||||
{"55u558eqfaod2r2gu42xxsu631xf0zobs5840vl", "5840vl", 33},
|
||||
{"", "a", -1},
|
||||
{"x", "a", -1},
|
||||
{"x", "x", 0},
|
||||
{"abc", "a", 0},
|
||||
{"abc", "b", 1},
|
||||
{"abc", "c", 2},
|
||||
{"abc", "x", -1},
|
||||
{"", "ab", -1},
|
||||
{"bc", "ab", -1},
|
||||
{"ab", "ab", 0},
|
||||
{"xab", "ab", 1},
|
||||
{"xab"[:2], "ab", -1},
|
||||
{"", "abc", -1},
|
||||
{"xbc", "abc", -1},
|
||||
{"abc", "abc", 0},
|
||||
{"xabc", "abc", 1},
|
||||
{"xabc"[:3], "abc", -1},
|
||||
{"xabxc", "abc", -1},
|
||||
{"", "abcd", -1},
|
||||
{"xbcd", "abcd", -1},
|
||||
{"abcd", "abcd", 0},
|
||||
{"xabcd", "abcd", 1},
|
||||
{"xyabcd"[:5], "abcd", -1},
|
||||
{"xbcqq", "abcqq", -1},
|
||||
{"abcqq", "abcqq", 0},
|
||||
{"xabcqq", "abcqq", 1},
|
||||
{"xyabcqq"[:6], "abcqq", -1},
|
||||
{"xabxcqq", "abcqq", -1},
|
||||
{"xabcqxq", "abcqq", -1},
|
||||
{"", "01234567", -1},
|
||||
{"32145678", "01234567", -1},
|
||||
{"01234567", "01234567", 0},
|
||||
{"x01234567", "01234567", 1},
|
||||
{"x0123456x01234567", "01234567", 9},
|
||||
{"xx01234567"[:9], "01234567", -1},
|
||||
{"", "0123456789", -1},
|
||||
{"3214567844", "0123456789", -1},
|
||||
{"0123456789", "0123456789", 0},
|
||||
{"x0123456789", "0123456789", 1},
|
||||
{"x012345678x0123456789", "0123456789", 11},
|
||||
{"xyz0123456789"[:12], "0123456789", -1},
|
||||
{"x01234567x89", "0123456789", -1},
|
||||
{"", "0123456789012345", -1},
|
||||
{"3214567889012345", "0123456789012345", -1},
|
||||
{"0123456789012345", "0123456789012345", 0},
|
||||
{"x0123456789012345", "0123456789012345", 1},
|
||||
{"x012345678901234x0123456789012345", "0123456789012345", 17},
|
||||
{"", "01234567890123456789", -1},
|
||||
{"32145678890123456789", "01234567890123456789", -1},
|
||||
{"01234567890123456789", "01234567890123456789", 0},
|
||||
{"x01234567890123456789", "01234567890123456789", 1},
|
||||
{"x0123456789012345678x01234567890123456789", "01234567890123456789", 21},
|
||||
{"xyz01234567890123456789"[:22], "01234567890123456789", -1},
|
||||
{"", "0123456789012345678901234567890", -1},
|
||||
{"321456788901234567890123456789012345678911", "0123456789012345678901234567890", -1},
|
||||
{"0123456789012345678901234567890", "0123456789012345678901234567890", 0},
|
||||
{"x0123456789012345678901234567890", "0123456789012345678901234567890", 1},
|
||||
{"x012345678901234567890123456789x0123456789012345678901234567890", "0123456789012345678901234567890", 32},
|
||||
{"xyz0123456789012345678901234567890"[:33], "0123456789012345678901234567890", -1},
|
||||
{"", "01234567890123456789012345678901", -1},
|
||||
{"32145678890123456789012345678901234567890211", "01234567890123456789012345678901", -1},
|
||||
{"01234567890123456789012345678901", "01234567890123456789012345678901", 0},
|
||||
{"x01234567890123456789012345678901", "01234567890123456789012345678901", 1},
|
||||
{"x0123456789012345678901234567890x01234567890123456789012345678901", "01234567890123456789012345678901", 33},
|
||||
{"xyz01234567890123456789012345678901"[:34], "01234567890123456789012345678901", -1},
|
||||
{"xxxxxx012345678901234567890123456789012345678901234567890123456789012", "012345678901234567890123456789012345678901234567890123456789012", 6},
|
||||
{"", "0123456789012345678901234567890123456789", -1},
|
||||
{"xx012345678901234567890123456789012345678901234567890123456789012", "0123456789012345678901234567890123456789", 2},
|
||||
{"xx012345678901234567890123456789012345678901234567890123456789012"[:41], "0123456789012345678901234567890123456789", -1},
|
||||
{"xx012345678901234567890123456789012345678901234567890123456789012", "0123456789012345678901234567890123456xxx", -1},
|
||||
{"xx0123456789012345678901234567890123456789012345678901234567890120123456789012345678901234567890123456xxx", "0123456789012345678901234567890123456xxx", 65},
|
||||
{"barfoobarfooyyyzzzyyyzzzyyyzzzyyyxxxzzzyyy", "x", 33},
|
||||
{"fofofofooofoboo", "oo", 7},
|
||||
{"fofofofofofoboo", "ob", 11},
|
||||
{"fofofofofofoboo", "boo", 12},
|
||||
{"fofofofofofoboo", "oboo", 11},
|
||||
{"fofofofofoooboo", "fooo", 8},
|
||||
{"fofofofofofoboo", "foboo", 10},
|
||||
{"fofofofofofoboo", "fofob", 8},
|
||||
{"fofofofofofofoffofoobarfoo", "foffof", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffof", 13},
|
||||
{"fofofofofofofoffofoobarfoo", "foffofo", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffofo", 13},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffofoo", 13},
|
||||
{"fofofofofofofoffofoobarfoo", "foffofoo", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffofoob", 13},
|
||||
{"fofofofofofofoffofoobarfoo", "foffofoob", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffofooba", 13},
|
||||
{"fofofofofofofoffofoobarfoo", "foffofooba", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffofoobar", 13},
|
||||
{"fofofofofofofoffofoobarfoo", "foffofoobar", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffofoobarf", 13},
|
||||
{"fofofofofofofoffofoobarfoo", "foffofoobarf", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffofoobarfo", 13},
|
||||
{"fofofofofofofoffofoobarfoo", "foffofoobarfo", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "foffofoobarfoo", 13},
|
||||
{"fofofofofofofoffofoobarfoo", "foffofoobarfoo", 12},
|
||||
{"fofofofofoofofoffofoobarfoo", "ofoffofoobarfoo", 12},
|
||||
{"fofofofofofofoffofoobarfoo", "ofoffofoobarfoo", 11},
|
||||
{"fofofofofoofofoffofoobarfoo", "fofoffofoobarfoo", 11},
|
||||
{"fofofofofofofoffofoobarfoo", "fofoffofoobarfoo", 10},
|
||||
{"fofofofofoofofoffofoobarfoo", "foobars", -1},
|
||||
{"foofyfoobarfoobar", "y", 4},
|
||||
{"oooooooooooooooooooooo", "r", -1},
|
||||
{"oxoxoxoxoxoxoxoxoxoxoxoy", "oy", 22},
|
||||
{"oxoxoxoxoxoxoxoxoxoxoxox", "oy", -1},
|
||||
}
|
||||
|
||||
for i := range tt {
|
||||
ptr1 := uint64(len(memory) - len(tt[i].h) - 1)
|
||||
|
||||
clear(memory)
|
||||
copy(memory[ptr1:], tt[i].h)
|
||||
copy(memory[ptr2:], tt[i].n)
|
||||
|
||||
var want uint64
|
||||
if tt[i].out >= 0 {
|
||||
want = ptr1 + uint64(tt[i].out)
|
||||
}
|
||||
|
||||
got := call(strstr, uint64(ptr1), uint64(ptr2))
|
||||
if got != want {
|
||||
t.Errorf("strstr(%q, %q) = %d, want %d",
|
||||
tt[i].h, tt[i].n, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fill(s []byte, v byte) {
|
||||
for i := range s {
|
||||
s[i] = v
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef _WASM_SIMD128_STRING_H
|
||||
#define _WASM_SIMD128_STRING_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <strings.h>
|
||||
#include <wasm_simd128.h>
|
||||
#include <__macro_PAGESIZE.h>
|
||||
|
||||
@@ -458,6 +458,110 @@ size_t strcspn(const char *s, const char *c) {
|
||||
#undef _WASM_SIMD128_CHKBITS
|
||||
#undef _WASM_SIMD128_BITMAP256_T
|
||||
|
||||
static const char *__memmem_rabin(const char *haystk, size_t sh,
|
||||
const char *needle, size_t sn,
|
||||
uint8_t bmbc[256]) {
|
||||
// http://0x80.pl/notesen/2016-11-28-simd-strfind.html
|
||||
__builtin_assume(2 <= sn && sn <= sh);
|
||||
|
||||
const v128_t fst = wasm_i8x16_splat(needle[0]);
|
||||
const v128_t lst = wasm_i8x16_splat(needle[sn - 1]);
|
||||
const char *N =
|
||||
(char *)(__builtin_wasm_memory_size(0) * PAGESIZE - sn - sizeof(v128_t));
|
||||
|
||||
while (haystk <= N) {
|
||||
const v128_t blk_fst = wasm_v128_load((v128_t *)(haystk));
|
||||
const v128_t blk_lst = wasm_v128_load((v128_t *)(haystk + sn - 1));
|
||||
const v128_t eq_fst = wasm_i8x16_eq(fst, blk_fst);
|
||||
const v128_t eq_lst = wasm_i8x16_eq(lst, blk_lst);
|
||||
|
||||
const v128_t cmp = eq_fst & eq_lst;
|
||||
if (wasm_v128_any_true(cmp)) {
|
||||
for (uint32_t mask = wasm_i8x16_bitmask(cmp); mask; mask &= mask - 1) {
|
||||
size_t ctz = __builtin_ctz(mask);
|
||||
if (!bcmp(haystk + ctz + 1, needle + 1, sn - 2)) {
|
||||
return haystk + ctz;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t skip = sizeof(v128_t);
|
||||
if (bmbc) skip += bmbc[wasm_i8x16_extract_lane(blk_lst, 15)];
|
||||
if (__builtin_sub_overflow(sh, skip, &sh)) return NULL;
|
||||
if (sn > sh) return NULL;
|
||||
haystk += skip;
|
||||
}
|
||||
|
||||
// Baseline algorithm.
|
||||
for (size_t j = 0; j <= sh - sn; j++) {
|
||||
for (size_t i = 0;; i++) {
|
||||
if (i >= sn) return haystk;
|
||||
if (needle[i] != haystk[i]) break;
|
||||
}
|
||||
haystk++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *__memmem_raita(const char *haystk, size_t sh,
|
||||
const char *needle, size_t sn) {
|
||||
// https://www-igm.univ-mlv.fr/~lecroq/string/node22.html
|
||||
__builtin_assume(2 <= sn && sn <= sh);
|
||||
|
||||
#ifndef _REENTRANT
|
||||
static
|
||||
#endif
|
||||
uint8_t bmbc[256];
|
||||
memset(bmbc, sn - 1 < 255 ? sn - 1 : 255, sizeof(bmbc));
|
||||
for (size_t i = 0; i < sn - 1; i++) {
|
||||
size_t t = sn - 1 - i - 1;
|
||||
if (t > 255) t = 255;
|
||||
bmbc[(unsigned char)needle[i]] = t;
|
||||
}
|
||||
|
||||
return __memmem_rabin(haystk, sh, needle, sn, bmbc);
|
||||
}
|
||||
|
||||
static const char *__memmem(const char *haystk, size_t sh, //
|
||||
const char *needle, size_t sn) {
|
||||
// Return when needle is longer than haystack.
|
||||
if (sn > sh) return NULL;
|
||||
|
||||
return sn < sizeof(v128_t) ? __memmem_rabin(haystk, sh, needle, sn, NULL)
|
||||
: __memmem_raita(haystk, sh, needle, sn);
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void *memmem(const void *vh, size_t sh, const void *vn, size_t sn) {
|
||||
// Return immediately on empty needle.
|
||||
if (sn == 0) return (void *)vh;
|
||||
|
||||
// Return immediately when needle is longer than haystack.
|
||||
if (sn > sh) return NULL;
|
||||
|
||||
// Skip to the first matching character using memchr,
|
||||
// handling single character needles.
|
||||
const char *needle = (char *)vn;
|
||||
const char *haystk = (char *)memchr(vh, *needle, sh);
|
||||
if (!haystk || sn == 1) return (void *)haystk;
|
||||
|
||||
sh -= haystk - (char *)vh;
|
||||
return (void *)__memmem(haystk, sh, needle, sn);
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
char *strstr(const char *haystk, const char *needle) {
|
||||
// Return immediately on empty needle.
|
||||
if (!needle[0]) return (char *)haystk;
|
||||
|
||||
// Skip to the first matching character using strchr,
|
||||
// handling single character needles.
|
||||
haystk = strchr(haystk, *needle);
|
||||
if (!haystk || !needle[1]) return (char *)haystk;
|
||||
|
||||
return (char *)__memmem(haystk, strlen(haystk), needle, strlen(needle));
|
||||
}
|
||||
|
||||
// Given the above SIMD implementations,
|
||||
// these are best implemented as
|
||||
// small wrappers over those functions.
|
||||
@@ -500,7 +604,8 @@ static char *__stpcpy(char *__restrict dest, const char *__restrict src) {
|
||||
return dest + slen;
|
||||
}
|
||||
|
||||
static char *__stpncpy(char *__restrict dest, const char *__restrict src, size_t n) {
|
||||
static char *__stpncpy(char *__restrict dest, const char *__restrict src,
|
||||
size_t n) {
|
||||
size_t strnlen(const char *s, size_t n);
|
||||
size_t slen = strnlen(src, n);
|
||||
memcpy(dest, src, slen);
|
||||
@@ -513,6 +618,7 @@ char *stpcpy(char *__restrict dest, const char *__restrict src) {
|
||||
return __stpcpy(dest, src);
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
char *strcpy(char *__restrict dest, const char *__restrict src) {
|
||||
__stpcpy(dest, src);
|
||||
return dest;
|
||||
|
||||
@@ -11,6 +11,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __wasm_simd128__
|
||||
#ifndef __OPTIMIZE_SIZE__
|
||||
|
||||
__attribute__((weak))
|
||||
int bcmp(const void *v1, const void *v2, size_t n) {
|
||||
@@ -48,6 +49,7 @@ int bcmp(const void *v1, const void *v2, size_t n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // __OPTIMIZE_SIZE__
|
||||
#endif // __wasm_simd128__
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user