Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5842ec9343 | ||
|
|
137e2694c0 | ||
|
|
d29aacb0b2 | ||
|
|
30649fa50a | ||
|
|
ed9a77fa96 | ||
|
|
4c0daafbf7 | ||
|
|
1ad454cfae | ||
|
|
ed7dab3edd | ||
|
|
a68b5f4364 | ||
|
|
299c57d1b8 | ||
|
|
501f863d20 | ||
|
|
33bea5277f | ||
|
|
c263381f1f | ||
|
|
c4f254bc37 | ||
|
|
c97c5a1f86 | ||
|
|
3e1b1313c1 | ||
|
|
83744cb9c8 | ||
|
|
a36d72c2dc | ||
|
|
e7f5604199 | ||
|
|
e50083912c | ||
|
|
d4764fb2fa | ||
|
|
42df71f3ff | ||
|
|
91e969c06b | ||
|
|
8ab0ddf53e | ||
|
|
74d22ded0a | ||
|
|
d962611796 | ||
|
|
7df3814c34 | ||
|
|
c5f49b835a | ||
|
|
0e55451a0b | ||
|
|
ea9a58ab19 | ||
|
|
0b46e74ea6 | ||
|
|
8dca850bee | ||
|
|
5b78823416 | ||
|
|
1764a571da | ||
|
|
9837310af7 | ||
|
|
ec8961a621 | ||
|
|
8c37aa2d97 | ||
|
|
ca93c498e7 | ||
|
|
15e9087fa8 | ||
|
|
7028e3a5b9 | ||
|
|
03bb20de6e | ||
|
|
20a51a344e | ||
|
|
2dbcc480f7 | ||
|
|
0f0716c438 |
@@ -1,23 +0,0 @@
|
||||
name: Benchmark libc
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, macos-15-intel]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with: { go-version: stable }
|
||||
|
||||
- name: Benchmark
|
||||
shell: bash
|
||||
run: sqlite3/libc/benchmark.sh
|
||||
@@ -5,11 +5,6 @@ set -euo pipefail
|
||||
sqlite3/download.sh
|
||||
sqlite3/tools.sh
|
||||
embed/build.sh
|
||||
embed/bcw2/build.sh
|
||||
|
||||
# Download and build sqlite-createtable-parser
|
||||
util/sql3util/wasm/download.sh
|
||||
util/sql3util/wasm/build.sh
|
||||
|
||||
# Check diffs
|
||||
git diff --exit-code
|
||||
@@ -23,10 +23,8 @@ jobs:
|
||||
shell: bash
|
||||
run: .github/workflows/repro.sh
|
||||
|
||||
- uses: actions/attest-build-provenance@v3
|
||||
- uses: actions/attest-build-provenance@v4
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
with:
|
||||
subject-path: |
|
||||
embed/sqlite3.wasm
|
||||
embed/bcw2/bcw2.wasm
|
||||
util/sql3util/wasm/sql3parse_table.wasm
|
||||
embed/sqlite3.wasm
|
||||
+19
-16
@@ -68,8 +68,8 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
go work init .
|
||||
go work use -r embed gormlite
|
||||
go test ./embed/bcw2/...
|
||||
go work use -r gormlite
|
||||
go test ./gormlite
|
||||
|
||||
- name: Test GORM
|
||||
shell: bash
|
||||
@@ -93,25 +93,28 @@ jobs:
|
||||
github.event_name == 'push' &&
|
||||
matrix.os == 'ubuntu-latest'
|
||||
|
||||
test-bsd:
|
||||
test-cross:
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- name: freebsd
|
||||
version: '14.3'
|
||||
version: '15.0'
|
||||
- name: netbsd
|
||||
version: '10.1'
|
||||
- name: freebsd
|
||||
arch: arm64
|
||||
version: '14.3'
|
||||
tflags: '-test.short'
|
||||
- name: netbsd
|
||||
arch: arm64
|
||||
version: '10.1'
|
||||
tflags: '-test.short'
|
||||
- name: illumos
|
||||
action: omnios
|
||||
version: 'r151056'
|
||||
- name: openbsd
|
||||
version: '7.8'
|
||||
tflags: '-test.short'
|
||||
- name: freebsd
|
||||
arch: arm64
|
||||
version: '15.0'
|
||||
tflags: '-test.short'
|
||||
- name: netbsd
|
||||
arch: arm64
|
||||
version: '10.1'
|
||||
tflags: '-test.short'
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
|
||||
@@ -126,9 +129,9 @@ jobs:
|
||||
run: .github/workflows/build-test.sh
|
||||
|
||||
- name: Test
|
||||
uses: cross-platform-actions/action@v0.30.0
|
||||
uses: cross-platform-actions/action@v0.32.0
|
||||
with:
|
||||
operating_system: ${{ matrix.os.name }}
|
||||
operating_system: ${{ matrix.os.action || matrix.os.name }}
|
||||
architecture: ${{ matrix.os.arch }}
|
||||
version: ${{ matrix.os.version }}
|
||||
shell: bash
|
||||
@@ -142,7 +145,7 @@ jobs:
|
||||
- name: dragonfly
|
||||
action: 'vmactions/dragonflybsd-vm@v1'
|
||||
- name: illumos
|
||||
action: 'vmactions/omnios-vm@v1'
|
||||
action: 'vmactions/openindiana-vm@v0'
|
||||
- name: solaris
|
||||
action: 'vmactions/solaris-vm@v1'
|
||||
bflags: '-tags sqlite3_dotlk'
|
||||
@@ -189,7 +192,7 @@ jobs:
|
||||
needs: test
|
||||
|
||||
steps:
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
- uses: docker/setup-qemu-action@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with: { go-version: stable }
|
||||
|
||||
@@ -157,16 +157,20 @@ func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, erro
|
||||
stk_t(op), stk_t(ptr)))
|
||||
ret = util.Read32[vfs.LockLevel](c.mod, ptr)
|
||||
|
||||
case FCNTL_VFS_POINTER:
|
||||
case FCNTL_VFSNAME, FCNTL_VFS_POINTER:
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), stk_t(ptr)))
|
||||
stk_t(FCNTL_VFS_POINTER), stk_t(ptr)))
|
||||
if rc == _OK {
|
||||
const zNameOffset = 16
|
||||
ptr = util.Read32[ptr_t](c.mod, ptr)
|
||||
ptr = util.Read32[ptr_t](c.mod, ptr+zNameOffset)
|
||||
name := util.ReadString(c.mod, ptr, _MAX_NAME)
|
||||
ret = vfs.Find(name)
|
||||
if op == FCNTL_VFS_POINTER {
|
||||
ret = vfs.Find(name)
|
||||
} else {
|
||||
ret = name
|
||||
}
|
||||
}
|
||||
|
||||
case FCNTL_FILE_POINTER, FCNTL_JOURNAL_POINTER:
|
||||
|
||||
@@ -173,7 +173,7 @@ const (
|
||||
|
||||
// PrepareFlag is a flag that can be passed to [Conn.PrepareFlags].
|
||||
//
|
||||
// https://sqlite.org/c3ref/c_prepare_normalize.html
|
||||
// https://sqlite.org/c3ref/c_prepare_dont_log.html
|
||||
type PrepareFlag uint32
|
||||
|
||||
const (
|
||||
@@ -181,6 +181,7 @@ const (
|
||||
PREPARE_NORMALIZE PrepareFlag = 0x02
|
||||
PREPARE_NO_VTAB PrepareFlag = 0x04
|
||||
PREPARE_DONT_LOG PrepareFlag = 0x10
|
||||
// PREPARE_FROM_DDL PrepareFlag = 0x20
|
||||
)
|
||||
|
||||
// FunctionFlag is a flag that can be passed to
|
||||
@@ -267,6 +268,7 @@ const (
|
||||
DBCONFIG_ENABLE_ATTACH_CREATE DBConfig = 1020
|
||||
DBCONFIG_ENABLE_ATTACH_WRITE DBConfig = 1021
|
||||
DBCONFIG_ENABLE_COMMENTS DBConfig = 1022
|
||||
// SQLITE_DBCONFIG_FP_DIGITS DBConfig = 1023
|
||||
// DBCONFIG_MAX DBConfig = 1022
|
||||
)
|
||||
|
||||
@@ -280,6 +282,7 @@ const (
|
||||
FCNTL_CHUNK_SIZE FcntlOpcode = 6
|
||||
FCNTL_FILE_POINTER FcntlOpcode = 7
|
||||
FCNTL_PERSIST_WAL FcntlOpcode = 10
|
||||
FCNTL_VFSNAME FcntlOpcode = 12
|
||||
FCNTL_POWERSAFE_OVERWRITE FcntlOpcode = 13
|
||||
FCNTL_VFS_POINTER FcntlOpcode = 27
|
||||
FCNTL_JOURNAL_POINTER FcntlOpcode = 28
|
||||
@@ -307,6 +310,7 @@ const (
|
||||
LIMIT_VARIABLE_NUMBER LimitCategory = 9
|
||||
LIMIT_TRIGGER_DEPTH LimitCategory = 10
|
||||
LIMIT_WORKER_THREADS LimitCategory = 11
|
||||
// LIMIT_PARSER_DEPTH LimitCategory = 12
|
||||
)
|
||||
|
||||
// AuthorizerActionCode are the integer action codes
|
||||
|
||||
+3
-9
@@ -91,13 +91,7 @@ func (ctx Context) ResultText(value string) {
|
||||
//
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultRawText(value []byte) {
|
||||
if len(value) == 0 {
|
||||
ctx.ResultText("")
|
||||
return
|
||||
}
|
||||
ptr := ctx.c.newBytes(value)
|
||||
ctx.c.call("sqlite3_result_text_go",
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
|
||||
ctx.ResultText(string(value)) // does not escape
|
||||
}
|
||||
|
||||
// ResultBlob sets the result of the function to a []byte.
|
||||
@@ -151,11 +145,11 @@ func (ctx Context) ResultTime(value time.Time, format TimeFormat) {
|
||||
}
|
||||
|
||||
func (ctx Context) resultRFC3339Nano(value time.Time) {
|
||||
const maxlen = int64(len(time.RFC3339Nano)) + 5
|
||||
|
||||
const maxlen = 48
|
||||
ptr := ctx.c.new(maxlen)
|
||||
buf := util.View(ctx.c.mod, ptr, maxlen)
|
||||
buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
|
||||
_ = append(buf, 0)
|
||||
|
||||
ctx.c.call("sqlite3_result_text_go",
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(buf)))
|
||||
|
||||
+15
-56
@@ -440,22 +440,6 @@ func (c *conn) CheckNamedValue(arg *driver.NamedValue) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: for Litestream use only; may be removed at any time.
|
||||
func (c *conn) FileControlPersistWAL(schema string, mode int) (int, error) {
|
||||
// notest
|
||||
arg := make([]any, 1)
|
||||
if mode >= 0 {
|
||||
arg[0] = mode > 0
|
||||
} else {
|
||||
arg = arg[:0]
|
||||
}
|
||||
res, err := c.Conn.FileControl(schema, sqlite3.FCNTL_PERSIST_WAL, arg...)
|
||||
if res == true {
|
||||
return 1, err
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
type stmt struct {
|
||||
*sqlite3.Stmt
|
||||
tmWrite sqlite3.TimeFormat
|
||||
@@ -669,14 +653,12 @@ type rows struct {
|
||||
names []string
|
||||
types []string
|
||||
scans []scantype
|
||||
dest []driver.Value
|
||||
}
|
||||
|
||||
var (
|
||||
// Ensure these interfaces are implemented:
|
||||
_ driver.RowsColumnTypeDatabaseTypeName = &rows{}
|
||||
_ driver.RowsColumnTypeNullable = &rows{}
|
||||
// _ driver.RowsColumnScanner = &rows{}
|
||||
)
|
||||
|
||||
func (r *rows) Close() error {
|
||||
@@ -716,17 +698,23 @@ func (r *rows) loadColumnMetadata() {
|
||||
types := make([]string, count)
|
||||
scans := make([]scantype, count)
|
||||
for i := range types {
|
||||
var notnull bool
|
||||
if col := r.Stmt.ColumnOriginName(i); col != "" {
|
||||
types[i], _, notnull, _, _, _ = c.TableColumnMetadata(
|
||||
var declType string
|
||||
var notNull, autoInc bool
|
||||
if column := r.Stmt.ColumnOriginName(i); column != "" {
|
||||
declType, _, notNull, _, autoInc, _ = c.TableColumnMetadata(
|
||||
r.Stmt.ColumnDatabaseName(i),
|
||||
r.Stmt.ColumnTableName(i),
|
||||
col)
|
||||
types[i] = strings.ToUpper(types[i])
|
||||
scans[i] = scanFromDecl(types[i])
|
||||
if notnull {
|
||||
scans[i] |= _NOT_NULL
|
||||
}
|
||||
column)
|
||||
} else {
|
||||
declType = r.Stmt.ColumnDeclType(i)
|
||||
}
|
||||
if declType != "" {
|
||||
declType = strings.ToUpper(declType)
|
||||
scans[i] = scanFromDecl(declType)
|
||||
types[i] = declType
|
||||
}
|
||||
if notNull || autoInc {
|
||||
scans[i] |= _NOT_NULL
|
||||
}
|
||||
}
|
||||
r.types = types
|
||||
@@ -796,7 +784,6 @@ func (r *rows) ColumnTypeScanType(index int) (typ reflect.Type) {
|
||||
}
|
||||
|
||||
func (r *rows) Next(dest []driver.Value) error {
|
||||
r.dest = nil
|
||||
c := r.Stmt.Conn()
|
||||
if old := c.SetInterrupt(r.ctx); old != r.ctx {
|
||||
defer c.SetInterrupt(old)
|
||||
@@ -846,33 +833,5 @@ func (r *rows) Next(dest []driver.Value) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
r.dest = dest
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rows) ScanColumn(dest any, index int) (err error) {
|
||||
// notest // Go 1.26
|
||||
var tm *time.Time
|
||||
var ok *bool
|
||||
switch d := dest.(type) {
|
||||
case *time.Time:
|
||||
tm = d
|
||||
case *sql.NullTime:
|
||||
tm = &d.Time
|
||||
ok = &d.Valid
|
||||
case *sql.Null[time.Time]:
|
||||
tm = &d.V
|
||||
ok = &d.Valid
|
||||
default:
|
||||
return driver.ErrSkip
|
||||
}
|
||||
value := r.dest[index]
|
||||
*tm, err = r.tmRead.Decode(value)
|
||||
if ok != nil {
|
||||
*ok = err == nil
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"math"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -521,39 +520,6 @@ func Test_ColumnType_ScanType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_rows_ScanColumn(t *testing.T) {
|
||||
t.Parallel()
|
||||
dsn := memdb.TestDB(t)
|
||||
|
||||
db, err := Open(dsn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var tm time.Time
|
||||
err = db.QueryRow(`SELECT NULL`).Scan(&tm)
|
||||
if err == nil {
|
||||
t.Error("want error")
|
||||
}
|
||||
// Go 1.26
|
||||
err = db.QueryRow(`SELECT datetime()`).Scan(&tm)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "sql: Scan error") {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var nt sql.NullTime
|
||||
err = db.QueryRow(`SELECT NULL`).Scan(&nt)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// Go 1.26
|
||||
err = db.QueryRow(`SELECT datetime()`).Scan(&nt)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "sql: Scan error") {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_loop(b *testing.B) {
|
||||
db, err := Open(":memory:")
|
||||
if err != nil {
|
||||
|
||||
+2
-5
@@ -1,6 +1,6 @@
|
||||
# Embeddable Wasm build of SQLite
|
||||
|
||||
This folder includes an embeddable Wasm build of SQLite 3.50.4 for use with
|
||||
This folder includes an embeddable Wasm build of SQLite 3.51.2 for use with
|
||||
[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
|
||||
|
||||
The following optional features are compiled in:
|
||||
@@ -33,9 +33,6 @@ The build is easily reproducible, and verifiable, using
|
||||
|
||||
You can use your own custom build of SQLite.
|
||||
|
||||
Examples of custom builds of SQLite are:
|
||||
- [`github.com/ncruces/go-sqlite3/embed/bcw2`](https://github.com/ncruces/go-sqlite3/tree/main/embed/bcw2)
|
||||
built from a branch supporting [`BEGIN CONCURRENT`](https://sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md)
|
||||
and [Wal2](https://sqlite.org/cgi/src/doc/wal2/doc/wal2.md).
|
||||
Example of a custom build of SQLite:
|
||||
- [`github.com/asg017/sqlite-vec-go-bindings/ncruces`](https://github.com/asg017/sqlite-vec-go-bindings)
|
||||
which includes the [`sqlite-vec`](https://github.com/asg017/sqlite-vec) vector search extension.
|
||||
@@ -1,2 +0,0 @@
|
||||
build/
|
||||
sqlite/
|
||||
@@ -1,22 +0,0 @@
|
||||
# Embeddable Wasm build of SQLite
|
||||
|
||||
This folder includes an alternative embeddable Wasm build of SQLite,
|
||||
which includes the experimental
|
||||
[`BEGIN CONCURRENT`](https://sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md) and
|
||||
[Wal2](https://sqlite.org/cgi/src/doc/wal2/doc/wal2.md) patches.
|
||||
|
||||
It also enables the optional
|
||||
[`UPDATE … ORDER BY … LIMIT`](https://sqlite.org/lang_update.html#optional_limit_and_order_by_clauses) and
|
||||
[`DELETE … ORDER BY … LIMIT`](https://sqlite.org/lang_delete.html#optional_limit_and_order_by_clauses) clauses,
|
||||
and the [`WITHIN GROUP ORDER BY`](https://sqlite.org/compile.html#enable_ordered_set_aggregates) aggregate syntax.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This package is experimental.
|
||||
> It is built from the `bedrock` branch of SQLite,
|
||||
> since that is _currently_ the most stable, maintained branch to include these features.
|
||||
|
||||
> [!CAUTION]
|
||||
> The Wal2 journaling mode creates databases that other versions of SQLite cannot access.
|
||||
|
||||
The build is easily reproducible, and verifiable, using
|
||||
[Artifact Attestations](https://github.com/ncruces/go-sqlite3/attestations).
|
||||
Binary file not shown.
@@ -1,59 +0,0 @@
|
||||
package bcw2
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/driver"
|
||||
"github.com/ncruces/go-sqlite3/ext/stats"
|
||||
"github.com/ncruces/go-sqlite3/vfs"
|
||||
)
|
||||
|
||||
func Test_bcw2(t *testing.T) {
|
||||
if !vfs.SupportsSharedMemory {
|
||||
t.Skip("skipping without shared memory")
|
||||
}
|
||||
|
||||
tmp := filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))
|
||||
|
||||
db, err := driver.Open("file:"+tmp+"?_pragma=journal_mode(wal2)&_txlock=concurrent", stats.Register)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
_, err = tx.Exec(`CREATE TABLE test (col)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(`DELETE FROM test LIMIT 1`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(`SELECT median() WITHIN GROUP (ORDER BY col) FROM test`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var version string
|
||||
err = db.QueryRow(`SELECT sqlite_version()`).Scan(&version)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if version != "3.52.0" {
|
||||
t.Error(version)
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
ROOT=../../
|
||||
BINARYEN="$ROOT/tools/binaryen/bin"
|
||||
WASI_SDK="$ROOT/tools/wasi-sdk/bin"
|
||||
|
||||
trap 'rm -rf sqlite/ build/ bcw2.tmp' EXIT
|
||||
|
||||
mkdir -p sqlite/
|
||||
mkdir -p build/ext/
|
||||
cp "$ROOT"/sqlite3/*.[ch] build/
|
||||
cp "$ROOT"/sqlite3/*.patch build/
|
||||
cd sqlite/
|
||||
|
||||
# https://sqlite.org/src/info/352b363a5d727047
|
||||
curl -#L https://github.com/sqlite/sqlite/archive/dbd613c.tar.gz | tar xz --strip-components=1
|
||||
# curl -#L https://sqlite.org/src/tarball/sqlite.tar.gz?r=352b363a5d | tar xz --strip-components=1
|
||||
|
||||
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
|
||||
MSYS_NO_PATHCONV=1 nmake /f makefile.msc sqlite3.c "OPTS=-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES"
|
||||
else
|
||||
sh configure --enable-update-limit
|
||||
OPTS=-DSQLITE_ENABLE_ORDERED_SET_AGGREGATES make sqlite3.c
|
||||
fi
|
||||
cd ~-
|
||||
|
||||
mv sqlite/sqlite3.c build/
|
||||
mv sqlite/sqlite3.h build/
|
||||
mv sqlite/sqlite3ext.h build/
|
||||
mv sqlite/ext/misc/anycollseq.c build/ext/
|
||||
mv sqlite/ext/misc/base64.c build/ext/
|
||||
mv sqlite/ext/misc/decimal.c build/ext/
|
||||
mv sqlite/ext/misc/ieee754.c build/ext/
|
||||
mv sqlite/ext/misc/regexp.c build/ext/
|
||||
mv sqlite/ext/misc/series.c build/ext/
|
||||
mv sqlite/ext/misc/spellfix.c build/ext/
|
||||
mv sqlite/ext/misc/uint.c build/ext/
|
||||
|
||||
cd build
|
||||
cat *.patch | patch -p0 --no-backup-if-mismatch
|
||||
cd ~-
|
||||
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -g0 -O2 \
|
||||
-Wall -Wextra -Wno-unused-parameter -Wno-unused-function \
|
||||
-o bcw2.wasm build/main.c \
|
||||
-I"$ROOT/sqlite3/libc" -I"build" \
|
||||
-mexec-model=reactor \
|
||||
-mmutable-globals -mnontrapping-fptoint \
|
||||
-msimd128 -mbulk-memory -msign-ext \
|
||||
-mreference-types -mmultivalue \
|
||||
-mno-extended-const \
|
||||
-fno-stack-protector \
|
||||
-Wl,--stack-first \
|
||||
-Wl,--import-undefined \
|
||||
-Wl,--initial-memory=327680 \
|
||||
-D_HAVE_SQLITE_CONFIG_H \
|
||||
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT \
|
||||
-DSQLITE_ENABLE_ORDERED_SET_AGGREGATES \
|
||||
-DSQLITE_EXPERIMENTAL_PRAGMA_20251114 \
|
||||
-DSQLITE_CUSTOM_INCLUDE=sqlite_opt.h \
|
||||
$(awk '{print "-Wl,--export="$0}' ../exports.txt)
|
||||
|
||||
"$BINARYEN/wasm-ctor-eval" -g -c _initialize bcw2.wasm -o bcw2.tmp
|
||||
"$BINARYEN/wasm-opt" -g bcw2.tmp -o bcw2.wasm \
|
||||
--low-memory-unused --gufa --generate-global-effects --converge -O3 \
|
||||
--enable-mutable-globals --enable-nontrapping-float-to-int \
|
||||
--enable-simd --enable-bulk-memory --enable-sign-ext \
|
||||
--enable-reference-types --enable-multivalue \
|
||||
--strip --strip-producers
|
||||
@@ -1,12 +0,0 @@
|
||||
module github.com/ncruces/go-sqlite3/embed/bcw2
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require github.com/ncruces/go-sqlite3 v0.30.1
|
||||
|
||||
require (
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/ncruces/sort v0.1.6 // indirect
|
||||
github.com/tetratelabs/wazero v1.10.1 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
)
|
||||
@@ -1,12 +0,0 @@
|
||||
github.com/ncruces/go-sqlite3 v0.30.1 h1:pHC3YsyRdJv4pCMB4MO1Q2BXw/CAa+Hoj7GSaKtVk+g=
|
||||
github.com/ncruces/go-sqlite3 v0.30.1/go.mod h1:UVsWrQaq1qkcal5/vT5lOJnZCVlR5rsThKdwidjFsKc=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/ncruces/sort v0.1.6 h1:TrsJfGRH1AoWoaeB4/+gCohot9+cA6u/INaH5agIhNk=
|
||||
github.com/ncruces/sort v0.1.6/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
|
||||
github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8=
|
||||
github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
@@ -1,24 +0,0 @@
|
||||
// Package bcw2 embeds SQLite into your application.
|
||||
//
|
||||
// Importing package bcw2 initializes the [sqlite3.Binary] variable
|
||||
// with a build of SQLite that includes the [BEGIN CONCURRENT] and [Wal2] patches:
|
||||
//
|
||||
// import _ "github.com/ncruces/go-sqlite3/embed/bcw2"
|
||||
//
|
||||
// [BEGIN CONCURRENT]: https://sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md
|
||||
// [Wal2]: https://sqlite.org/cgi/src/doc/wal2/doc/wal2.md
|
||||
package bcw2
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
)
|
||||
|
||||
//go:embed bcw2.wasm
|
||||
var binary string
|
||||
|
||||
func init() {
|
||||
sqlite3.Binary = unsafe.Slice(unsafe.StringData(binary), len(binary))
|
||||
}
|
||||
+5
-3
@@ -9,7 +9,7 @@ WASI_SDK="$ROOT/tools/wasi-sdk/bin"
|
||||
|
||||
trap 'rm -f sqlite3.tmp' EXIT
|
||||
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -g0 -O2 \
|
||||
"$WASI_SDK/clang" --target=wasm32 -nostdlib -std=c23 -g0 -O2 \
|
||||
-Wall -Wextra -Wno-unused-parameter -Wno-unused-function \
|
||||
-o sqlite3.wasm "$ROOT/sqlite3/main.c" \
|
||||
-I"$ROOT/sqlite3/libc" -I"$ROOT/sqlite3" \
|
||||
@@ -23,13 +23,15 @@ trap 'rm -f sqlite3.tmp' EXIT
|
||||
-Wl,--import-undefined \
|
||||
-Wl,--initial-memory=327680 \
|
||||
-D_HAVE_SQLITE_CONFIG_H \
|
||||
-DSQLITE_EXPERIMENTAL_PRAGMA_20251114 \
|
||||
-DSQLITE_CUSTOM_INCLUDE=sqlite_opt.h \
|
||||
$(awk '{print "-Wl,--export="$0}' exports.txt)
|
||||
|
||||
"$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp
|
||||
"$BINARYEN/wasm-opt" -g sqlite3.tmp -o sqlite3.wasm \
|
||||
--low-memory-unused --gufa --generate-global-effects --converge -O3 \
|
||||
--gufa --generate-global-effects --low-memory-unused --converge -O3 \
|
||||
--enable-mutable-globals --enable-nontrapping-float-to-int \
|
||||
--enable-simd --enable-bulk-memory --enable-sign-ext \
|
||||
--enable-reference-types --enable-multivalue \
|
||||
--strip --strip-producers
|
||||
--disable-extended-const \
|
||||
--strip --strip-producers
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ func Test_init(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if version != "3.51.0" {
|
||||
if version != "3.51.3" {
|
||||
t.Error(version)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
+14
-13
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
"github.com/ncruces/go-sqlite3/util/sql3util"
|
||||
)
|
||||
|
||||
// Register registers the bloom_filter virtual table:
|
||||
@@ -34,6 +35,8 @@ type bloom struct {
|
||||
hashes int
|
||||
}
|
||||
|
||||
const vtab = `CREATE TABLE x(present, word TEXT HIDDEN NOT NULL PRIMARY KEY) WITHOUT ROWID`
|
||||
|
||||
func create(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom, err error) {
|
||||
b := bloom{
|
||||
db: db,
|
||||
@@ -55,11 +58,9 @@ func create(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom,
|
||||
}
|
||||
|
||||
if len(arg) > 1 {
|
||||
b.prob, err = strconv.ParseFloat(arg[1], 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if b.prob <= 0 || b.prob >= 1 {
|
||||
var ok bool
|
||||
b.prob, ok = sql3util.ParseFloat(arg[1])
|
||||
if !ok || b.prob <= 0 || b.prob >= 1 {
|
||||
return nil, util.ErrorString("bloom: probability must be in the range (0,1)")
|
||||
}
|
||||
} else {
|
||||
@@ -80,8 +81,7 @@ func create(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom,
|
||||
|
||||
b.bytes = numBytes(nelem, b.prob)
|
||||
|
||||
err = db.DeclareVTab(
|
||||
`CREATE TABLE x(present, word HIDDEN NOT NULL PRIMARY KEY) WITHOUT ROWID`)
|
||||
err = db.DeclareVTab(vtab)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -115,15 +115,15 @@ func connect(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom
|
||||
storage: table + "_storage",
|
||||
}
|
||||
|
||||
err = db.DeclareVTab(
|
||||
`CREATE TABLE x(present, word HIDDEN NOT NULL PRIMARY KEY) WITHOUT ROWID`)
|
||||
err = db.DeclareVTab(vtab)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
load, _, err := db.Prepare(fmt.Sprintf(
|
||||
load, _, err := db.PrepareFlags(fmt.Sprintf(
|
||||
`SELECT m/8, p, k FROM %s.%s WHERE rowid = 1`,
|
||||
sqlite3.QuoteIdentifier(b.schema), sqlite3.QuoteIdentifier(b.storage)))
|
||||
sqlite3.QuoteIdentifier(b.schema), sqlite3.QuoteIdentifier(b.storage)),
|
||||
sqlite3.PREPARE_DONT_LOG)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -166,9 +166,10 @@ func (t *bloom) ShadowTables() {
|
||||
}
|
||||
|
||||
func (t *bloom) Integrity(schema, table string, flags int) error {
|
||||
load, _, err := t.db.Prepare(fmt.Sprintf(
|
||||
load, _, err := t.db.PrepareFlags(fmt.Sprintf(
|
||||
`SELECT typeof(data), length(data), p, n, m, k FROM %s.%s WHERE rowid = 1`,
|
||||
sqlite3.QuoteIdentifier(t.schema), sqlite3.QuoteIdentifier(t.storage)))
|
||||
sqlite3.QuoteIdentifier(t.schema), sqlite3.QuoteIdentifier(t.storage)),
|
||||
sqlite3.PREPARE_DONT_LOG)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bloom: %v", err) // can't wrap!
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func Register(db *sqlite3.Conn) error {
|
||||
done.Add(key)
|
||||
}
|
||||
|
||||
err := db.DeclareVTab(`CREATE TABLE x(id,depth,root HIDDEN,tablename HIDDEN,idcolumn HIDDEN,parentcolumn HIDDEN)`)
|
||||
err := db.DeclareVTab(`CREATE TABLE x(id INT,depth INT,root HIDDEN,tablename TEXT HIDDEN,idcolumn TEXT HIDDEN,parentcolumn TEXT HIDDEN)`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -202,7 +202,7 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
|
||||
sqlite3.QuoteIdentifier(column),
|
||||
sqlite3.QuoteIdentifier(parent),
|
||||
)
|
||||
stmt, _, err := c.db.Prepare(sql)
|
||||
stmt, _, err := c.db.PrepareFlags(sql, sqlite3.PREPARE_DONT_LOG)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+16
-22
@@ -85,7 +85,8 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error {
|
||||
header: header,
|
||||
}
|
||||
|
||||
if schema == "" {
|
||||
hadSchema := schema != ""
|
||||
if !hadSchema {
|
||||
var row []string
|
||||
if header || columns < 0 {
|
||||
csv, c, err := t.newReader()
|
||||
@@ -99,17 +100,14 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error {
|
||||
}
|
||||
}
|
||||
schema = getSchema(header, columns, row)
|
||||
} else {
|
||||
t.typs, err = getColumnAffinities(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = db.DeclareVTab(schema)
|
||||
if err == nil {
|
||||
err = db.VTabConfig(sqlite3.VTAB_DIRECTONLY)
|
||||
}
|
||||
if err == nil && hadSchema {
|
||||
t.typs, err = getColumnAffinities(schema)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -123,7 +121,7 @@ type table struct {
|
||||
fsys fs.FS
|
||||
name string
|
||||
data string
|
||||
typs []affinity
|
||||
typs []sql3util.Affinity
|
||||
comma rune
|
||||
comment rune
|
||||
header bool
|
||||
@@ -242,31 +240,27 @@ func (c *cursor) RowID() (int64, error) {
|
||||
|
||||
func (c *cursor) Column(ctx sqlite3.Context, col int) error {
|
||||
if col < len(c.row) {
|
||||
typ := text
|
||||
typ := sql3util.TEXT
|
||||
if col < len(c.table.typs) {
|
||||
typ = c.table.typs[col]
|
||||
}
|
||||
|
||||
txt := c.row[col]
|
||||
if txt == "" && typ != text {
|
||||
if txt == "" && typ != sql3util.TEXT {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case numeric, integer:
|
||||
if strings.TrimLeft(txt, "+-0123456789") == "" {
|
||||
if i, err := strconv.ParseInt(txt, 10, 64); err == nil {
|
||||
ctx.ResultInt64(i)
|
||||
return nil
|
||||
}
|
||||
case sql3util.NUMERIC, sql3util.INTEGER:
|
||||
if i, err := strconv.ParseInt(txt, 10, 64); err == nil {
|
||||
ctx.ResultInt64(i)
|
||||
return nil
|
||||
}
|
||||
fallthrough
|
||||
case real:
|
||||
if strings.TrimLeft(txt, "+-.0123456789Ee") == "" {
|
||||
if f, err := strconv.ParseFloat(txt, 64); err == nil {
|
||||
ctx.ResultFloat(f)
|
||||
return nil
|
||||
}
|
||||
case sql3util.REAL:
|
||||
if f, ok := sql3util.ParseFloat(txt); ok {
|
||||
ctx.ResultFloat(f)
|
||||
return nil
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
|
||||
+13
-13
@@ -9,31 +9,31 @@ import (
|
||||
|
||||
func getSchema(header bool, columns int, row []string) string {
|
||||
var sep string
|
||||
var str strings.Builder
|
||||
str.WriteString("CREATE TABLE x(")
|
||||
var buf strings.Builder
|
||||
buf.WriteString("CREATE TABLE x(")
|
||||
|
||||
if 0 <= columns && columns < len(row) {
|
||||
row = row[:columns]
|
||||
}
|
||||
for i, f := range row {
|
||||
str.WriteString(sep)
|
||||
buf.WriteString(sep)
|
||||
if header && f != "" {
|
||||
str.WriteString(sqlite3.QuoteIdentifier(f))
|
||||
buf.WriteString(sqlite3.QuoteIdentifier(f))
|
||||
} else {
|
||||
str.WriteString("c")
|
||||
str.WriteString(strconv.Itoa(i + 1))
|
||||
buf.WriteString("c")
|
||||
buf.WriteString(strconv.Itoa(i + 1))
|
||||
}
|
||||
str.WriteString(" TEXT")
|
||||
buf.WriteString(" TEXT")
|
||||
sep = ","
|
||||
}
|
||||
for i := len(row); i < columns; i++ {
|
||||
str.WriteString(sep)
|
||||
str.WriteString("c")
|
||||
str.WriteString(strconv.Itoa(i + 1))
|
||||
str.WriteString(" TEXT")
|
||||
buf.WriteString(sep)
|
||||
buf.WriteString("c")
|
||||
buf.WriteString(strconv.Itoa(i + 1))
|
||||
buf.WriteString(" TEXT")
|
||||
sep = ","
|
||||
}
|
||||
str.WriteByte(')')
|
||||
buf.WriteByte(')')
|
||||
|
||||
return str.String()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
+4
-39
@@ -1,52 +1,17 @@
|
||||
package csv
|
||||
|
||||
import (
|
||||
"strings"
|
||||
import "github.com/ncruces/go-sqlite3/util/sql3util"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/util/sql3util"
|
||||
)
|
||||
|
||||
type affinity byte
|
||||
|
||||
const (
|
||||
blob affinity = 0
|
||||
text affinity = 1
|
||||
numeric affinity = 2
|
||||
integer affinity = 3
|
||||
real affinity = 4
|
||||
)
|
||||
|
||||
func getColumnAffinities(schema string) ([]affinity, error) {
|
||||
func getColumnAffinities(schema string) ([]sql3util.Affinity, error) {
|
||||
tab, err := sql3util.ParseTable(schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := tab.Columns
|
||||
types := make([]affinity, len(columns))
|
||||
types := make([]sql3util.Affinity, len(columns))
|
||||
for i, col := range columns {
|
||||
types[i] = getAffinity(col.Type)
|
||||
types[i] = sql3util.GetAffinity(col.Type)
|
||||
}
|
||||
return types, nil
|
||||
}
|
||||
|
||||
func getAffinity(declType string) affinity {
|
||||
// https://sqlite.org/datatype3.html#determination_of_column_affinity
|
||||
if declType == "" {
|
||||
return blob
|
||||
}
|
||||
name := strings.ToUpper(declType)
|
||||
if strings.Contains(name, "INT") {
|
||||
return integer
|
||||
}
|
||||
if strings.Contains(name, "CHAR") || strings.Contains(name, "CLOB") || strings.Contains(name, "TEXT") {
|
||||
return text
|
||||
}
|
||||
if strings.Contains(name, "BLOB") {
|
||||
return blob
|
||||
}
|
||||
if strings.Contains(name, "REAL") || strings.Contains(name, "FLOA") || strings.Contains(name, "DOUB") {
|
||||
return real
|
||||
}
|
||||
return numeric
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package csv
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_getAffinity(t *testing.T) {
|
||||
tests := []struct {
|
||||
decl string
|
||||
want affinity
|
||||
}{
|
||||
{"", blob},
|
||||
{"INTEGER", integer},
|
||||
{"TINYINT", integer},
|
||||
{"TEXT", text},
|
||||
{"CHAR", text},
|
||||
{"CLOB", text},
|
||||
{"BLOB", blob},
|
||||
{"REAL", real},
|
||||
{"FLOAT", real},
|
||||
{"DOUBLE", real},
|
||||
{"NUMERIC", numeric},
|
||||
{"DECIMAL", numeric},
|
||||
{"BOOLEAN", numeric},
|
||||
{"DATETIME", numeric},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.decl, func(t *testing.T) {
|
||||
if got := getAffinity(tt.decl); got != tt.want {
|
||||
t.Errorf("getAffinity() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error {
|
||||
db.CreateFunction("readfile", 1, sqlite3.DIRECTONLY, readfile(fsys)),
|
||||
db.CreateFunction("lsmode", 1, sqlite3.DETERMINISTIC, lsmode),
|
||||
sqlite3.CreateModule(db, "fsdir", nil, func(db *sqlite3.Conn, _, _, _ string, _ ...string) (fsdir, error) {
|
||||
err := db.DeclareVTab(`CREATE TABLE x(name,mode,mtime TIMESTAMP,data,path HIDDEN,dir HIDDEN)`)
|
||||
err := db.DeclareVTab(`CREATE TABLE x(name TEXT,mode INT,mtime TIMESTAMP,data BLOB,path HIDDEN,dir HIDDEN)`)
|
||||
if err == nil {
|
||||
err = db.VTabConfig(sqlite3.VTAB_DIRECTONLY)
|
||||
}
|
||||
|
||||
+22
-6
@@ -43,11 +43,14 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (ret *table, err e
|
||||
|
||||
// Row key query.
|
||||
t.scan = "SELECT * FROM\n" + arg[0]
|
||||
stmt, _, err := db.Prepare(t.scan)
|
||||
stmt, tail, err := db.PrepareFlags(t.scan, 0 /*PREPARE_FROM_DDL*/)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
if tail != "" {
|
||||
return nil, util.TailErr
|
||||
}
|
||||
|
||||
t.keys = make([]string, stmt.ColumnCount())
|
||||
for i := range t.keys {
|
||||
@@ -55,15 +58,20 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (ret *table, err e
|
||||
t.keys[i] = name
|
||||
create.WriteString(sep)
|
||||
create.WriteString(name)
|
||||
create.WriteString(" ")
|
||||
create.WriteString(stmt.ColumnDeclType(i))
|
||||
sep = ","
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
// Column definition query.
|
||||
stmt, _, err = db.Prepare("SELECT * FROM\n" + arg[1])
|
||||
stmt, tail, err = db.PrepareFlags("SELECT * FROM\n"+arg[1], 0 /*PREPARE_FROM_DDL*/)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tail != "" {
|
||||
return nil, util.TailErr
|
||||
}
|
||||
|
||||
if stmt.ColumnCount() != 2 {
|
||||
return nil, util.ErrorString("pivot: column definition query expects 2 result columns")
|
||||
@@ -71,17 +79,23 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (ret *table, err e
|
||||
for stmt.Step() {
|
||||
name := sqlite3.QuoteIdentifier(stmt.ColumnText(1))
|
||||
t.cols = append(t.cols, stmt.ColumnValue(0).Dup())
|
||||
create.WriteString(",")
|
||||
create.WriteString(sep)
|
||||
create.WriteString(name)
|
||||
create.WriteString(" ")
|
||||
create.WriteString(stmt.ColumnDeclType(1))
|
||||
sep = ","
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
// Pivot cell query.
|
||||
t.cell = "SELECT * FROM\n" + arg[2]
|
||||
stmt, _, err = db.Prepare(t.cell)
|
||||
stmt, tail, err = db.PrepareFlags(t.cell, 0 /*PREPARE_FROM_DDL*/)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tail != "" {
|
||||
return nil, util.TailErr
|
||||
}
|
||||
|
||||
if stmt.ColumnCount() != 1 {
|
||||
return nil, util.ErrorString("pivot: cell query expects 1 result columns")
|
||||
@@ -182,7 +196,9 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
|
||||
return err
|
||||
}
|
||||
|
||||
c.scan, _, err = c.table.db.Prepare(idxStr)
|
||||
const prepflags = sqlite3.PREPARE_DONT_LOG /*PREPARE_FROM_DDL*/
|
||||
|
||||
c.scan, _, err = c.table.db.PrepareFlags(idxStr, prepflags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -194,7 +210,7 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
|
||||
}
|
||||
|
||||
if c.cell == nil {
|
||||
c.cell, _, err = c.table.db.Prepare(c.table.cell)
|
||||
c.cell, _, err = c.table.db.PrepareFlags(c.table.cell, prepflags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+22
-16
@@ -35,40 +35,45 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (*table, error) {
|
||||
|
||||
sql := "SELECT * FROM\n" + arg[0]
|
||||
|
||||
stmt, _, err := db.PrepareFlags(sql, sqlite3.PREPARE_PERSISTENT)
|
||||
stmt, tail, err := db.PrepareFlags(sql,
|
||||
sqlite3.PREPARE_PERSISTENT /*PREPARE_FROM_DDL*/)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tail != "" {
|
||||
stmt.Close()
|
||||
return nil, util.TailErr
|
||||
}
|
||||
|
||||
var sep string
|
||||
var str strings.Builder
|
||||
str.WriteString("CREATE TABLE x(")
|
||||
var buf strings.Builder
|
||||
buf.WriteString("CREATE TABLE x(")
|
||||
outputs := stmt.ColumnCount()
|
||||
for i := range outputs {
|
||||
name := sqlite3.QuoteIdentifier(stmt.ColumnName(i))
|
||||
str.WriteString(sep)
|
||||
str.WriteString(name)
|
||||
str.WriteString(" ")
|
||||
str.WriteString(stmt.ColumnDeclType(i))
|
||||
buf.WriteString(sep)
|
||||
buf.WriteString(name)
|
||||
buf.WriteString(" ")
|
||||
buf.WriteString(stmt.ColumnDeclType(i))
|
||||
sep = ","
|
||||
}
|
||||
inputs := stmt.BindCount()
|
||||
for i := 1; i <= inputs; i++ {
|
||||
str.WriteString(sep)
|
||||
buf.WriteString(sep)
|
||||
name := stmt.BindName(i)
|
||||
if name == "" {
|
||||
str.WriteString("[")
|
||||
str.WriteString(strconv.Itoa(i))
|
||||
str.WriteString("] HIDDEN")
|
||||
buf.WriteString("[")
|
||||
buf.WriteString(strconv.Itoa(i))
|
||||
buf.WriteString("] HIDDEN")
|
||||
} else {
|
||||
str.WriteString(sqlite3.QuoteIdentifier(name[1:]))
|
||||
str.WriteString(" HIDDEN")
|
||||
buf.WriteString(sqlite3.QuoteIdentifier(name[1:]))
|
||||
buf.WriteString(" HIDDEN")
|
||||
}
|
||||
sep = ","
|
||||
}
|
||||
str.WriteByte(')')
|
||||
buf.WriteByte(')')
|
||||
|
||||
err = db.DeclareVTab(str.String())
|
||||
err = db.DeclareVTab(buf.String())
|
||||
if err != nil {
|
||||
stmt.Close()
|
||||
return nil, err
|
||||
@@ -129,7 +134,8 @@ func (t *table) Open() (_ sqlite3.VTabCursor, err error) {
|
||||
if !t.inuse {
|
||||
t.inuse = true
|
||||
} else {
|
||||
stmt, _, err = t.stmt.Conn().Prepare(t.sql)
|
||||
stmt, _, err = t.stmt.Conn().PrepareFlags(t.sql,
|
||||
sqlite3.PREPARE_DONT_LOG /*PREPARE_FROM_DDL*/)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+6
-6
@@ -5,15 +5,15 @@ package stats
|
||||
type kahan struct{ hi, lo float64 }
|
||||
|
||||
func (k *kahan) add(x float64) {
|
||||
y := k.lo + x
|
||||
t := k.hi + y
|
||||
k.lo = y - (t - k.hi)
|
||||
y := float64(k.lo + x)
|
||||
t := float64(k.hi + y)
|
||||
k.lo = y - float64(t-k.hi)
|
||||
k.hi = t
|
||||
}
|
||||
|
||||
func (k *kahan) sub(x float64) {
|
||||
y := k.lo - x
|
||||
t := k.hi + y
|
||||
k.lo = y - (t - k.hi)
|
||||
y := float64(k.lo - x)
|
||||
t := float64(k.hi + y)
|
||||
k.lo = y - float64(t-k.hi)
|
||||
k.hi = t
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
)
|
||||
|
||||
func cot(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
if f := arg[0].Float(); f != 0.0 {
|
||||
ctx.ResultFloat(1 / math.Tan(f))
|
||||
}
|
||||
}
|
||||
|
||||
func cbrt(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.Type() != sqlite3.NULL {
|
||||
ctx.ResultFloat(math.Cbrt(f))
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ type percentile struct {
|
||||
func (q *percentile) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.NumericType() != sqlite3.NULL {
|
||||
if f != 0.0 || a.Type() != sqlite3.NULL {
|
||||
q.nums = append(q.nums, f)
|
||||
}
|
||||
if q.kind != median && q.arg1 == nil {
|
||||
@@ -45,7 +45,7 @@ func (q *percentile) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
func (q *percentile) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.NumericType() != sqlite3.NULL {
|
||||
if f != 0.0 || a.Type() != sqlite3.NULL {
|
||||
i := slices.Index(q.nums, f)
|
||||
l := len(q.nums) - 1
|
||||
q.nums[i] = q.nums[l]
|
||||
@@ -66,12 +66,12 @@ func (q *percentile) Value(ctx sqlite3.Context) {
|
||||
if q.kind == median {
|
||||
float, err = q.at(0.5)
|
||||
ctx.ResultFloat(float)
|
||||
} else if err = json.Unmarshal(q.arg1, &float); err == nil {
|
||||
float, err = q.at(float)
|
||||
ctx.ResultFloat(float)
|
||||
} else if err = json.Unmarshal(q.arg1, &floats); err == nil {
|
||||
err = q.atMore(floats)
|
||||
ctx.ResultJSON(floats)
|
||||
} else if err = json.Unmarshal(q.arg1, &float); err == nil {
|
||||
float, err = q.at(float)
|
||||
ctx.ResultFloat(float)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.ResultError(fmt.Errorf("percentile: %w", err)) // notest
|
||||
|
||||
+11
-9
@@ -91,7 +91,9 @@ func Register(db *sqlite3.Conn) error {
|
||||
db.CreateWindowFunction("percentile_disc", 2, order, newPercentile(percentile_disc)),
|
||||
db.CreateWindowFunction("every", 1, flags, newBoolean(every)),
|
||||
db.CreateWindowFunction("some", 1, flags, newBoolean(some)),
|
||||
db.CreateWindowFunction("mode", 1, order, newMode))
|
||||
db.CreateWindowFunction("mode", 1, order, newMode),
|
||||
db.CreateFunction("cbrt", 1, flags, cbrt),
|
||||
db.CreateFunction("cot", 1, flags, cot))
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -168,7 +170,7 @@ func (fn *variance) Value(ctx sqlite3.Context) {
|
||||
func (fn *variance) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.NumericType() != sqlite3.NULL {
|
||||
if f != 0.0 || a.Type() != sqlite3.NULL {
|
||||
fn.enqueue(f)
|
||||
}
|
||||
}
|
||||
@@ -176,7 +178,7 @@ func (fn *variance) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
func (fn *variance) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.NumericType() != sqlite3.NULL {
|
||||
if f != 0.0 || a.Type() != sqlite3.NULL {
|
||||
fn.dequeue(f)
|
||||
}
|
||||
}
|
||||
@@ -241,8 +243,8 @@ func (fn *covariance) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
fa := a.Float()
|
||||
fb := b.Float()
|
||||
if true &&
|
||||
(fa != 0.0 || a.NumericType() != sqlite3.NULL) &&
|
||||
(fb != 0.0 || b.NumericType() != sqlite3.NULL) {
|
||||
(fa != 0.0 || a.Type() != sqlite3.NULL) &&
|
||||
(fb != 0.0 || b.Type() != sqlite3.NULL) {
|
||||
fn.enqueue(fa, fb)
|
||||
}
|
||||
}
|
||||
@@ -252,8 +254,8 @@ func (fn *covariance) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
fa := a.Float()
|
||||
fb := b.Float()
|
||||
if true &&
|
||||
(fa != 0.0 || a.NumericType() != sqlite3.NULL) &&
|
||||
(fb != 0.0 || b.NumericType() != sqlite3.NULL) {
|
||||
(fa != 0.0 || a.Type() != sqlite3.NULL) &&
|
||||
(fb != 0.0 || b.Type() != sqlite3.NULL) {
|
||||
fn.dequeue(fa, fb)
|
||||
}
|
||||
}
|
||||
@@ -293,7 +295,7 @@ func (fn *momentfn) Value(ctx sqlite3.Context) {
|
||||
func (fn *momentfn) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.NumericType() != sqlite3.NULL {
|
||||
if f != 0.0 || a.Type() != sqlite3.NULL {
|
||||
fn.enqueue(f)
|
||||
}
|
||||
}
|
||||
@@ -301,7 +303,7 @@ func (fn *momentfn) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
func (fn *momentfn) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.NumericType() != sqlite3.NULL {
|
||||
if f != 0.0 || a.Type() != sqlite3.NULL {
|
||||
fn.dequeue(f)
|
||||
}
|
||||
}
|
||||
|
||||
+33
-6
@@ -233,9 +233,6 @@ func TestRegister_covariance(t *testing.T) {
|
||||
}
|
||||
|
||||
func Benchmark_average(b *testing.B) {
|
||||
sqlite3.Initialize()
|
||||
b.ResetTimer()
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@@ -253,6 +250,7 @@ func Benchmark_average(b *testing.B) {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
if !stmt.Step() {
|
||||
b.Fatal(stmt.Err())
|
||||
} else {
|
||||
@@ -269,9 +267,6 @@ func Benchmark_average(b *testing.B) {
|
||||
}
|
||||
|
||||
func Benchmark_variance(b *testing.B) {
|
||||
sqlite3.Initialize()
|
||||
b.ResetTimer()
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@@ -289,6 +284,7 @@ func Benchmark_variance(b *testing.B) {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
if !stmt.Step() {
|
||||
b.Fatal(stmt.Err())
|
||||
} else if b.N > 100 {
|
||||
@@ -303,3 +299,34 @@ func Benchmark_variance(b *testing.B) {
|
||||
b.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_math(b *testing.B) {
|
||||
benchmarks := []string{"sqrt", "tan", "cot", "cbrt"}
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
for _, bm := range benchmarks {
|
||||
b.Run(bm, func(b *testing.B) {
|
||||
stmt, _, err := db.Prepare(`SELECT ` + bm + `(value) FROM generate_series(0, ?)`)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
err = stmt.BindInt(1, b.N)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
err = stmt.Exec()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -31,8 +31,11 @@ func ExampleConn_CreateAggregateFunction() {
|
||||
count := 0
|
||||
total := 0.0
|
||||
for arg := range seq {
|
||||
total += arg[0].Float()
|
||||
count++
|
||||
switch arg[0].NumericType() {
|
||||
case sqlite3.FLOAT, sqlite3.INTEGER:
|
||||
total += arg[0].Float()
|
||||
count++
|
||||
}
|
||||
}
|
||||
ctx.ResultFloat(total / float64(count))
|
||||
})
|
||||
|
||||
@@ -5,22 +5,24 @@ go 1.24.0
|
||||
require (
|
||||
github.com/ncruces/julianday v1.0.0
|
||||
github.com/ncruces/sort v0.1.6
|
||||
github.com/ncruces/wbt v0.2.0
|
||||
github.com/tetratelabs/wazero v1.10.1
|
||||
golang.org/x/sys v0.38.0
|
||||
github.com/ncruces/wbt v1.0.0
|
||||
github.com/tetratelabs/wazero v1.11.0
|
||||
golang.org/x/sys v0.41.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dchest/siphash v1.2.3 // ext/bloom
|
||||
github.com/google/uuid v1.6.0 // ext/uuid
|
||||
github.com/psanford/httpreadat v0.1.0 // example
|
||||
golang.org/x/crypto v0.45.0 // vfs/adiantum vfs/xts
|
||||
golang.org/x/sync v0.18.0 // test
|
||||
golang.org/x/text v0.31.0 // ext/unicode
|
||||
golang.org/x/crypto v0.48.0 // vfs/adiantum vfs/xts
|
||||
golang.org/x/sync v0.19.0 // test
|
||||
golang.org/x/text v0.34.0 // ext/unicode
|
||||
lukechampine.com/adiantum v1.1.1 // vfs/adiantum
|
||||
)
|
||||
|
||||
retract (
|
||||
v0.31.1 // SQLite 3.52.0 withdrawn
|
||||
v0.31.0 // SQLite 3.52.0 withdrawn
|
||||
v0.23.2 // tagged from the wrong branch
|
||||
v0.4.0 // tagged from the wrong branch
|
||||
)
|
||||
|
||||
@@ -6,19 +6,19 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt
|
||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/ncruces/sort v0.1.6 h1:TrsJfGRH1AoWoaeB4/+gCohot9+cA6u/INaH5agIhNk=
|
||||
github.com/ncruces/sort v0.1.6/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
|
||||
github.com/ncruces/wbt v0.2.0 h1:Q9zlKOBSZc7Yy/R2cGa35g6RKUUE3BjNIW3tfGC4F04=
|
||||
github.com/ncruces/wbt v0.2.0/go.mod h1:DtF92amvMxH69EmBFUSFWRDAlo6hOEfoNQnClxj9C/c=
|
||||
github.com/ncruces/wbt v1.0.0 h1:8iBE7UPjTLUpzu3/FCRjAmuQjWzgxo10RGBgt3ooLSc=
|
||||
github.com/ncruces/wbt v1.0.0/go.mod h1:DtF92amvMxH69EmBFUSFWRDAlo6hOEfoNQnClxj9C/c=
|
||||
github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
|
||||
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
|
||||
github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8=
|
||||
github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
|
||||
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
lukechampine.com/adiantum v1.1.1 h1:4fp6gTxWCqpEbLy40ExiYDDED3oUNWx5cTqBCtPdZqA=
|
||||
lukechampine.com/adiantum v1.1.1/go.mod h1:LrAYVnTYLnUtE/yMp5bQr0HstAf060YUF8nM0B6+rUw=
|
||||
|
||||
+4
-4
@@ -3,7 +3,7 @@ module github.com/ncruces/go-sqlite3/gormlite
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/ncruces/go-sqlite3 v0.30.1
|
||||
github.com/ncruces/go-sqlite3 v0.30.4
|
||||
gorm.io/gorm v1.31.1
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ require (
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.10.1 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.11.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
)
|
||||
|
||||
+8
-8
@@ -2,15 +2,15 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/ncruces/go-sqlite3 v0.30.1 h1:pHC3YsyRdJv4pCMB4MO1Q2BXw/CAa+Hoj7GSaKtVk+g=
|
||||
github.com/ncruces/go-sqlite3 v0.30.1/go.mod h1:UVsWrQaq1qkcal5/vT5lOJnZCVlR5rsThKdwidjFsKc=
|
||||
github.com/ncruces/go-sqlite3 v0.30.4 h1:j9hEoOL7f9ZoXl8uqXVniaq1VNwlWAXihZbTvhqPPjA=
|
||||
github.com/ncruces/go-sqlite3 v0.30.4/go.mod h1:7WR20VSC5IZusKhUdiR9y1NsUqnZgqIYCmKKoMEYg68=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8=
|
||||
github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
|
||||
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||
|
||||
+71
-47
@@ -2,21 +2,27 @@ package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
const (
|
||||
i32s = "\x7f\x7f\x7f\x7f\x7f\x7f"
|
||||
i64s = "\x7e"
|
||||
f64s = "\x7c\x7c"
|
||||
)
|
||||
|
||||
type funcVI[T0 i32] func(context.Context, api.Module, T0)
|
||||
|
||||
func (fn funcVI[T0]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
fn(ctx, mod, T0(stack[0]))
|
||||
}
|
||||
|
||||
func ExportFuncVI[T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0)) {
|
||||
func ExportFuncVI[T0 i32](mod wazero.HostModuleBuilder, name string, fn funcVI[T0]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcVI[T0](fn),
|
||||
[]api.ValueType{api.ValueTypeI32}, nil).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:1]), nil).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -27,10 +33,9 @@ func (fn funcVIII[T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack [
|
||||
fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]))
|
||||
}
|
||||
|
||||
func ExportFuncVIII[T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2)) {
|
||||
func ExportFuncVIII[T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn funcVIII[T0, T1, T2]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcVIII[T0, T1, T2](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:3]), nil).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -41,10 +46,9 @@ func (fn funcVIIII[T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, st
|
||||
fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]))
|
||||
}
|
||||
|
||||
func ExportFuncVIIII[T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3)) {
|
||||
func ExportFuncVIIII[T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn funcVIIII[T0, T1, T2, T3]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcVIIII[T0, T1, T2, T3](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:4]), nil).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -55,10 +59,9 @@ func (fn funcVIIIII[T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Modul
|
||||
fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]))
|
||||
}
|
||||
|
||||
func ExportFuncVIIIII[T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4)) {
|
||||
func ExportFuncVIIIII[T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn funcVIIIII[T0, T1, T2, T3, T4]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcVIIIII[T0, T1, T2, T3, T4](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:5]), nil).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -69,23 +72,21 @@ func (fn funcVIIIIJ[T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Modul
|
||||
fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]))
|
||||
}
|
||||
|
||||
func ExportFuncVIIIIJ[T0, T1, T2, T3 i32, T4 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4)) {
|
||||
func ExportFuncVIIIIJ[T0, T1, T2, T3 i32, T4 i64](mod wazero.HostModuleBuilder, name string, fn funcVIIIIJ[T0, T1, T2, T3, T4]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcVIIIIJ[T0, T1, T2, T3, T4](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, nil).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:4]+i64s[:1]), nil).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
type funcII[TR, T0 i32] func(context.Context, api.Module, T0) TR
|
||||
|
||||
func (fn funcII[TR, T0]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0])))
|
||||
stack[0] = uint64(uint32(fn(ctx, mod, T0(stack[0]))))
|
||||
}
|
||||
|
||||
func ExportFuncII[TR, T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0) TR) {
|
||||
func ExportFuncII[TR, T0 i32](mod wazero.HostModuleBuilder, name string, fn funcII[TR, T0]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcII[TR, T0](fn),
|
||||
[]api.ValueType{api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:1]), []api.ValueType(i32s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -93,13 +94,12 @@ type funcIII[TR, T0, T1 i32] func(context.Context, api.Module, T0, T1) TR
|
||||
|
||||
func (fn funcIII[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
_ = stack[1] // prevent bounds check on every slice access
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
|
||||
stack[0] = uint64(uint32(fn(ctx, mod, T0(stack[0]), T1(stack[1]))))
|
||||
}
|
||||
|
||||
func ExportFuncIII[TR, T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
|
||||
func ExportFuncIII[TR, T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn funcIII[TR, T0, T1]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcIII[TR, T0, T1](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:2]), []api.ValueType(i32s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -107,13 +107,12 @@ type funcIIII[TR, T0, T1, T2 i32] func(context.Context, api.Module, T0, T1, T2)
|
||||
|
||||
func (fn funcIIII[TR, T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
_ = stack[2] // prevent bounds check on every slice access
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2])))
|
||||
stack[0] = uint64(uint32(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]))))
|
||||
}
|
||||
|
||||
func ExportFuncIIII[TR, T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2) TR) {
|
||||
func ExportFuncIIII[TR, T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn funcIIII[TR, T0, T1, T2]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcIIII[TR, T0, T1, T2](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:3]), []api.ValueType(i32s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -121,13 +120,12 @@ type funcIIIII[TR, T0, T1, T2, T3 i32] func(context.Context, api.Module, T0, T1,
|
||||
|
||||
func (fn funcIIIII[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
_ = stack[3] // prevent bounds check on every slice access
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
|
||||
stack[0] = uint64(uint32(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]))))
|
||||
}
|
||||
|
||||
func ExportFuncIIIII[TR, T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
|
||||
func ExportFuncIIIII[TR, T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn funcIIIII[TR, T0, T1, T2, T3]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcIIIII[TR, T0, T1, T2, T3](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:4]), []api.ValueType(i32s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -135,13 +133,12 @@ type funcIIIIII[TR, T0, T1, T2, T3, T4 i32] func(context.Context, api.Module, T0
|
||||
|
||||
func (fn funcIIIIII[TR, T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
_ = stack[4] // prevent bounds check on every slice access
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4])))
|
||||
stack[0] = uint64(uint32(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]))))
|
||||
}
|
||||
|
||||
func ExportFuncIIIIII[TR, T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4) TR) {
|
||||
func ExportFuncIIIIII[TR, T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn funcIIIIII[TR, T0, T1, T2, T3, T4]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcIIIIII[TR, T0, T1, T2, T3, T4](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:5]), []api.ValueType(i32s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -149,13 +146,12 @@ type funcIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32] func(context.Context, api.Modul
|
||||
|
||||
func (fn funcIIIIIII[TR, T0, T1, T2, T3, T4, T5]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
_ = stack[5] // prevent bounds check on every slice access
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]), T5(stack[5])))
|
||||
stack[0] = uint64(uint32(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]), T5(stack[5]))))
|
||||
}
|
||||
|
||||
func ExportFuncIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4, T5) TR) {
|
||||
func ExportFuncIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32](mod wazero.HostModuleBuilder, name string, fn funcIIIIIII[TR, T0, T1, T2, T3, T4, T5]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcIIIIIII[TR, T0, T1, T2, T3, T4, T5](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:6]), []api.ValueType(i32s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -163,13 +159,12 @@ type funcIIIIJ[TR, T0, T1, T2 i32, T3 i64] func(context.Context, api.Module, T0,
|
||||
|
||||
func (fn funcIIIIJ[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
_ = stack[3] // prevent bounds check on every slice access
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
|
||||
stack[0] = uint64(uint32(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]))))
|
||||
}
|
||||
|
||||
func ExportFuncIIIIJ[TR, T0, T1, T2 i32, T3 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
|
||||
func ExportFuncIIIIJ[TR, T0, T1, T2 i32, T3 i64](mod wazero.HostModuleBuilder, name string, fn funcIIIIJ[TR, T0, T1, T2, T3]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcIIIIJ[TR, T0, T1, T2, T3](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:3]+i64s[:1]), []api.ValueType(i32s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -177,12 +172,41 @@ type funcIIJ[TR, T0 i32, T1 i64] func(context.Context, api.Module, T0, T1) TR
|
||||
|
||||
func (fn funcIIJ[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
_ = stack[1] // prevent bounds check on every slice access
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
|
||||
stack[0] = uint64(uint32(fn(ctx, mod, T0(stack[0]), T1(stack[1]))))
|
||||
}
|
||||
|
||||
func ExportFuncIIJ[TR, T0 i32, T1 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
|
||||
func ExportFuncIIJ[TR, T0 i32, T1 i64](mod wazero.HostModuleBuilder, name string, fn funcIIJ[TR, T0, T1]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(funcIIJ[TR, T0, T1](fn),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
|
||||
WithGoModuleFunction(fn, []api.ValueType(i32s[:1]+i64s[:1]), []api.ValueType(i32s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
type flt = interface{ ~float32 | ~float64 }
|
||||
|
||||
type funcDD[TR, T0 flt] func(T0) TR
|
||||
|
||||
func (fn funcDD[TR, T0]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = math.Float64bits(float64(fn(
|
||||
T0(math.Float64frombits(stack[0])))))
|
||||
}
|
||||
|
||||
func ExportFuncDD[TR, T0 flt](mod wazero.HostModuleBuilder, name string, fn funcDD[TR, T0]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(fn, []api.ValueType(f64s[:1]), []api.ValueType(f64s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
type funcDDD[TR, T0, T1 flt] func(T0, T1) TR
|
||||
|
||||
func (fn funcDDD[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
_ = stack[1] // prevent bounds check on every slice access
|
||||
stack[0] = math.Float64bits(float64(fn(
|
||||
T0(math.Float64frombits(stack[0])),
|
||||
T1(math.Float64frombits(stack[1])))))
|
||||
}
|
||||
|
||||
func ExportFuncDDD[TR, T0, T1 flt](mod wazero.HostModuleBuilder, name string, fn funcDDD[TR, T0, T1]) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(fn, []api.ValueType(f64s[:2]), []api.ValueType(f64s[:1])).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ const (
|
||||
)
|
||||
|
||||
type (
|
||||
i8 interface{ ~int8 | ~uint8 }
|
||||
i32 interface{ ~int32 | ~uint32 }
|
||||
i64 interface{ ~int64 | ~uint64 }
|
||||
i8 = interface{ ~int8 | ~uint8 }
|
||||
i32 = interface{ ~int32 | ~uint32 }
|
||||
i64 = interface{ ~int64 | ~uint64 }
|
||||
|
||||
Stk_t = uint64
|
||||
Ptr_t uint32
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package util
|
||||
|
||||
func ValidPageSize(s int) bool {
|
||||
return s&(s-1) == 0 && 512 <= s && s <= 65536
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ParseBool(s string) (b, ok bool) {
|
||||
if len(s) == 0 {
|
||||
return false, false
|
||||
}
|
||||
if s[0] == '0' {
|
||||
return false, true
|
||||
}
|
||||
if '1' <= s[0] && s[0] <= '9' {
|
||||
return true, true
|
||||
}
|
||||
switch strings.ToLower(s) {
|
||||
case "true", "yes", "on":
|
||||
return true, true
|
||||
case "false", "no", "off":
|
||||
return false, true
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
func ParseFloat(s string) (f float64, ok bool) {
|
||||
if strings.TrimLeft(s, "+-.0123456789Ee") != "" {
|
||||
return
|
||||
}
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
return f, err == nil
|
||||
}
|
||||
|
||||
func ParseTimeShift(s string) (years, months, days int, duration time.Duration, ok bool) {
|
||||
// Sign part: ±
|
||||
neg := strings.HasPrefix(s, "-")
|
||||
sign := neg || strings.HasPrefix(s, "+")
|
||||
if sign {
|
||||
s = s[1:]
|
||||
}
|
||||
|
||||
if ok = len(s) >= 5; !ok {
|
||||
return // !ok
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if neg {
|
||||
years = -years
|
||||
months = -months
|
||||
days = -days
|
||||
duration = -duration
|
||||
}
|
||||
}()
|
||||
|
||||
// Date part: YYYY-MM-DD
|
||||
if s[4] == '-' {
|
||||
if ok = sign && len(s) >= 10 && s[7] == '-'; !ok {
|
||||
return // !ok
|
||||
}
|
||||
if years, ok = parseInt(s[0:4], 0); !ok {
|
||||
return // !ok
|
||||
}
|
||||
if months, ok = parseInt(s[5:7], 12); !ok {
|
||||
return // !ok
|
||||
}
|
||||
if days, ok = parseInt(s[8:10], 31); !ok {
|
||||
return // !ok
|
||||
}
|
||||
if len(s) == 10 {
|
||||
return
|
||||
}
|
||||
if ok = s[10] == ' '; !ok {
|
||||
return // !ok
|
||||
}
|
||||
s = s[11:]
|
||||
}
|
||||
|
||||
// Time part: HH:MM
|
||||
if ok = len(s) >= 5 && s[2] == ':'; !ok {
|
||||
return // !ok
|
||||
}
|
||||
|
||||
var hours, minutes int
|
||||
if hours, ok = parseInt(s[0:2], 24); !ok {
|
||||
return
|
||||
}
|
||||
if minutes, ok = parseInt(s[3:5], 60); !ok {
|
||||
return
|
||||
}
|
||||
duration = time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute
|
||||
|
||||
if len(s) == 5 {
|
||||
return
|
||||
}
|
||||
if ok = len(s) >= 8 && s[5] == ':'; !ok {
|
||||
return // !ok
|
||||
}
|
||||
|
||||
// Seconds part: HH:MM:SS
|
||||
var seconds int
|
||||
if seconds, ok = parseInt(s[6:8], 60); !ok {
|
||||
return
|
||||
}
|
||||
duration += time.Duration(seconds) * time.Second
|
||||
|
||||
if len(s) == 8 {
|
||||
return
|
||||
}
|
||||
if ok = len(s) >= 10 && s[8] == '.'; !ok {
|
||||
return // !ok
|
||||
}
|
||||
s = s[9:]
|
||||
|
||||
// Nanosecond part: HH:MM:SS.SSS
|
||||
var nanos int
|
||||
if nanos, ok = parseInt(s[0:min(9, len(s))], 0); !ok {
|
||||
return
|
||||
}
|
||||
for i := len(s); i < 9; i++ {
|
||||
nanos *= 10
|
||||
}
|
||||
duration += time.Duration(nanos)
|
||||
|
||||
// Subnanosecond part.
|
||||
if len(s) > 9 {
|
||||
_, ok = parseInt(s[9:], 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseInt(s string, max int) (i int, _ bool) {
|
||||
for _, r := range []byte(s) {
|
||||
r -= '0'
|
||||
if r > 9 {
|
||||
return
|
||||
}
|
||||
i = i*10 + int(r)
|
||||
}
|
||||
return i, max == 0 || i < max
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package util_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
)
|
||||
|
||||
func TestParseBool(t *testing.T) {
|
||||
tests := []struct {
|
||||
str string
|
||||
val bool
|
||||
ok bool
|
||||
}{
|
||||
{"", false, false},
|
||||
{"0", false, true},
|
||||
{"1", true, true},
|
||||
{"9", true, true},
|
||||
{"T", false, false},
|
||||
{"true", true, true},
|
||||
{"FALSE", false, true},
|
||||
{"false?", false, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.str, func(t *testing.T) {
|
||||
gotVal, gotOK := util.ParseBool(tt.str)
|
||||
if gotVal != tt.val || gotOK != tt.ok {
|
||||
t.Errorf("ParseBool(%q) = (%v, %v) want (%v, %v)", tt.str, gotVal, gotOK, tt.val, tt.ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTimeShift(t *testing.T) {
|
||||
epoch := time.Unix(0, 0)
|
||||
tests := []struct {
|
||||
str string
|
||||
val time.Time
|
||||
ok bool
|
||||
}{
|
||||
{"", epoch, false},
|
||||
{"0001-11-30", epoch, false},
|
||||
{"+_001-11-30", epoch, false},
|
||||
{"+0001-_1-30", epoch.AddDate(1, 0, 0), false},
|
||||
{"+0001-11-_0", epoch.AddDate(1, 11, 0), false},
|
||||
{"+0001-11-30", epoch.AddDate(1, 11, 30), true},
|
||||
{"-0001-11-30", epoch.AddDate(-1, -11, -30), true},
|
||||
{"+0001-11-30T", epoch.AddDate(1, 11, 30), false},
|
||||
{"+0001-11-30 12", epoch.AddDate(1, 11, 30), false},
|
||||
{"+0001-11-30 _2:30", epoch.AddDate(1, 11, 30), false},
|
||||
{"+0001-11-30 12:_0", epoch.AddDate(1, 11, 30), false},
|
||||
{"+0001-11-30 12:30", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), true},
|
||||
{"+0001-11-30 12:30:", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), false},
|
||||
{"+0001-11-30 12:30:_0", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), false},
|
||||
{"+0001-11-30 12:30:59", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), true},
|
||||
{"+0001-11-30 12:30:59.", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), false},
|
||||
{"+0001-11-30 12:30:59._", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), false},
|
||||
{"+0001-11-30 12:30:59.1", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 100*time.Millisecond), true},
|
||||
{"+0001-11-30 12:30:59.123456789_", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 123456789), false},
|
||||
{"+0001-11-30 12:30:59.1234567890", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 123456789), true},
|
||||
{"-12:30:59.1234567890", epoch.Add(-12*time.Hour - 30*time.Minute - 59*time.Second - 123456789), true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.str, func(t *testing.T) {
|
||||
years, months, days, duration, gotOK := util.ParseTimeShift(tt.str)
|
||||
gotVal := epoch.AddDate(years, months, days).Add(duration)
|
||||
if !gotVal.Equal(tt.val) || gotOK != tt.ok {
|
||||
t.Errorf("ParseTimeShift(%q) = (%v, %v) want (%v, %v)", tt.str, gotVal, gotOK, tt.val, tt.ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzParseTimeShift(f *testing.F) {
|
||||
f.Add("")
|
||||
f.Add("0001-12-30")
|
||||
f.Add("+_001-12-30")
|
||||
f.Add("+0001-_2-30")
|
||||
f.Add("+0001-12-_0")
|
||||
f.Add("+0001-12-30")
|
||||
f.Add("-0001-12-30")
|
||||
f.Add("+0001-12-30T")
|
||||
f.Add("+0001-12-30 12")
|
||||
f.Add("+0001-12-30 _2:30")
|
||||
f.Add("+0001-12-30 12:_0")
|
||||
f.Add("+0001-12-30 12:30")
|
||||
f.Add("+0001-12-30 12:30:")
|
||||
f.Add("+0001-12-30 12:30:_0")
|
||||
f.Add("+0001-12-30 12:30:60")
|
||||
f.Add("+0001-12-30 12:30:60.")
|
||||
f.Add("+0001-12-30 12:30:60._")
|
||||
f.Add("+0001-12-30 12:30:60.1")
|
||||
f.Add("+0001-12-30 12:30:60.123456789_")
|
||||
f.Add("+0001-12-30 12:30:60.1234567890")
|
||||
f.Add("-12:30:60.1234567890")
|
||||
|
||||
c, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
s, _, err := c.Prepare(`SELECT julianday('00:00', ?)`)
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
// Default SQLite date.
|
||||
epoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
f.Fuzz(func(t *testing.T, str string) {
|
||||
years, months, days, duration, ok := util.ParseTimeShift(str)
|
||||
|
||||
// Account for a full 400 year cycle.
|
||||
if years < -200 || years > +200 {
|
||||
t.Skip()
|
||||
}
|
||||
// SQLite only tracks milliseconds.
|
||||
if duration != duration.Truncate(time.Millisecond) {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
if ok {
|
||||
s.Reset()
|
||||
s.BindText(1, str)
|
||||
if !s.Step() {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
got := epoch.AddDate(years, months, days).Add(duration)
|
||||
|
||||
// Julian day introduces floating point inaccuracy.
|
||||
want := s.ColumnTime(0, sqlite3.TimeFormatJulianDay)
|
||||
want = want.Round(time.Millisecond)
|
||||
if !got.Equal(want) {
|
||||
t.Fatalf("with %q, got %v, want %v", str, got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -20,6 +20,10 @@ func JSON(value any) any {
|
||||
|
||||
// ResultJSON sets the result of the function to the JSON encoding of value.
|
||||
//
|
||||
// If you return JSON from application-defined functions,
|
||||
// consider registering those functions with [RESULT_SUBTYPE]
|
||||
// and calling [Context.ResultSubtype] with 'J'.
|
||||
//
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultJSON(value any) {
|
||||
err := json.NewEncoder(callbackWriter(func(p []byte) (int, error) {
|
||||
|
||||
+4
-3
@@ -40,7 +40,7 @@ func (s *Stmt) BindJSON(param int, value any) error {
|
||||
w := bytesWriter{sqlite: s.c.sqlite}
|
||||
if err := json.MarshalWrite(&w, value); err != nil {
|
||||
s.c.free(w.ptr)
|
||||
return err
|
||||
return err // notest
|
||||
}
|
||||
rc := res_t(s.c.call("sqlite3_bind_text_go",
|
||||
stk_t(s.handle), stk_t(param),
|
||||
@@ -100,8 +100,8 @@ type bytesWriter struct {
|
||||
}
|
||||
|
||||
func (b *bytesWriter) Write(p []byte) (n int, err error) {
|
||||
if len(p) > cap(b.buf)-len(b.buf) {
|
||||
want := int64(len(b.buf)) + int64(len(p))
|
||||
if len(p) > cap(b.buf)-len(b.buf)-1 {
|
||||
want := int64(len(b.buf)) + int64(len(p)) + 1
|
||||
grow := int64(cap(b.buf))
|
||||
grow += grow >> 1
|
||||
want = max(want, grow)
|
||||
@@ -109,5 +109,6 @@ func (b *bytesWriter) Write(p []byte) (n int, err error) {
|
||||
b.buf = util.View(b.mod, b.ptr, want)[:len(b.buf)]
|
||||
}
|
||||
b.buf = append(b.buf, p...)
|
||||
_ = append(b.buf, 0)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# Litestream lightweight read-replicas
|
||||
|
||||
This package implements the **EXPERIMENTAL** `"litestream"` SQLite VFS
|
||||
that offers Litestream [lightweight read-replicas](https://fly.io/blog/litestream-revamped/#lightweight-read-replicas).
|
||||
|
||||
See the [example](vfs_test.go) for how to use.
|
||||
|
||||
To improve performance,
|
||||
increase `PollInterval` (and `MinLevel`) as much as you can,
|
||||
and set [`PRAGMA cache_size=N`](https://www.sqlite.org/pragma.html#pragma_cache_size)
|
||||
(or use `_pragma=cache_size(N)`).
|
||||
@@ -1,78 +0,0 @@
|
||||
// Package litestream implements a Litestream lightweight read-replica VFS.
|
||||
package litestream
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/litestream"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/vfs"
|
||||
)
|
||||
|
||||
const (
|
||||
// The default poll interval.
|
||||
DefaultPollInterval = 1 * time.Second
|
||||
|
||||
// The default cache size: 10 MiB.
|
||||
DefaultCacheSize = 10 * 1024 * 1024
|
||||
)
|
||||
|
||||
func init() {
|
||||
vfs.Register("litestream", liteVFS{})
|
||||
}
|
||||
|
||||
var (
|
||||
liteMtx sync.RWMutex
|
||||
// +checklocks:liteMtx
|
||||
liteDBs = map[string]*liteDB{}
|
||||
)
|
||||
|
||||
// ReplicaOptions represents options for [NewReplica].
|
||||
type ReplicaOptions struct {
|
||||
// Where to log error messages. May be nil.
|
||||
Logger *slog.Logger
|
||||
|
||||
// Replica poll interval.
|
||||
// Should be less than the compaction interval
|
||||
// used by the replica at MinLevel+1.
|
||||
PollInterval time.Duration
|
||||
|
||||
// Minimum compaction level to track.
|
||||
MinLevel int
|
||||
|
||||
// CacheSize is the maximum size of the page cache in bytes.
|
||||
// Zero means DefaultCacheSize, negative disables caching.
|
||||
CacheSize int
|
||||
}
|
||||
|
||||
// NewReplica creates a read-replica from a Litestream client.
|
||||
func NewReplica(name string, client litestream.ReplicaClient, options ReplicaOptions) {
|
||||
if options.Logger != nil {
|
||||
options.Logger = options.Logger.With("name", name)
|
||||
} else {
|
||||
options.Logger = slog.New(slog.DiscardHandler)
|
||||
}
|
||||
if options.PollInterval <= 0 {
|
||||
options.PollInterval = DefaultPollInterval
|
||||
}
|
||||
if options.CacheSize == 0 {
|
||||
options.CacheSize = DefaultCacheSize
|
||||
}
|
||||
|
||||
liteMtx.Lock()
|
||||
defer liteMtx.Unlock()
|
||||
liteDBs[name] = &liteDB{
|
||||
client: client,
|
||||
opts: options,
|
||||
cache: pageCache{size: options.CacheSize},
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveReplica removes a replica by name.
|
||||
func RemoveReplica(name string) {
|
||||
liteMtx.Lock()
|
||||
defer liteMtx.Unlock()
|
||||
delete(liteDBs, name)
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package litestream
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"github.com/superfly/ltx"
|
||||
)
|
||||
|
||||
type pageCache struct {
|
||||
single singleflight.Group
|
||||
pages map[uint32]cachedPage // +checklocks:mtx
|
||||
size int
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
type cachedPage struct {
|
||||
data []byte
|
||||
txid ltx.TXID
|
||||
}
|
||||
|
||||
func (c *pageCache) getOrFetch(pgno uint32, maxTXID ltx.TXID, fetch func() (any, error)) ([]byte, error) {
|
||||
if c.size >= 0 {
|
||||
c.mtx.Lock()
|
||||
if c.pages == nil {
|
||||
c.pages = map[uint32]cachedPage{}
|
||||
}
|
||||
page := c.pages[pgno]
|
||||
c.mtx.Unlock()
|
||||
|
||||
if page.txid == maxTXID {
|
||||
return page.data, nil
|
||||
}
|
||||
}
|
||||
|
||||
var key [12]byte
|
||||
binary.LittleEndian.PutUint32(key[0:], pgno)
|
||||
binary.LittleEndian.PutUint64(key[4:], uint64(maxTXID))
|
||||
v, err, _ := c.single.Do(string(key[:]), fetch)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
page := cachedPage{v.([]byte), maxTXID}
|
||||
if c.size >= 0 {
|
||||
c.mtx.Lock()
|
||||
c.evict(len(page.data))
|
||||
c.pages[pgno] = page
|
||||
c.mtx.Unlock()
|
||||
}
|
||||
return page.data, nil
|
||||
}
|
||||
|
||||
// +checklocks:c.mtx
|
||||
func (c *pageCache) evict(pageSize int) {
|
||||
// Evict random keys until we're under the maximum size.
|
||||
// SQLite has its own page cache, which it will use for each connection.
|
||||
// Since this is a second layer of shared cache,
|
||||
// random eviction is probably good enough.
|
||||
if pageSize*len(c.pages) < c.size {
|
||||
return
|
||||
}
|
||||
for key := range c.pages {
|
||||
delete(c.pages, key)
|
||||
if pageSize*len(c.pages) < c.size {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package litestream_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/litestream/s3"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/driver"
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
"github.com/ncruces/go-sqlite3/litestream"
|
||||
)
|
||||
|
||||
func ExampleNewReplica() {
|
||||
client := s3.NewReplicaClient()
|
||||
client.Bucket = "test-bucket"
|
||||
client.Path = "fruits.db"
|
||||
|
||||
litestream.NewReplica("fruits.db", client, litestream.ReplicaOptions{
|
||||
PollInterval: 5 * time.Second,
|
||||
})
|
||||
|
||||
db, err := driver.Open("file:fruits.db?vfs=litestream")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
rows, err := db.Query("SELECT * FROM fruits")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var name, color string
|
||||
err := rows.Scan(&name, &color)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Println(name, color)
|
||||
}
|
||||
|
||||
log.Println("===")
|
||||
rows.Close()
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
module github.com/ncruces/go-sqlite3/litestream
|
||||
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/benbjohnson/litestream v0.5.2
|
||||
github.com/ncruces/go-sqlite3 v0.30.1
|
||||
github.com/ncruces/wbt v0.2.0
|
||||
github.com/superfly/ltx v0.5.0
|
||||
)
|
||||
|
||||
// github.com/ncruces/go-sqlite3
|
||||
require (
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.10.1 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
)
|
||||
|
||||
// github.com/superfly/ltx
|
||||
require github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
|
||||
// github.com/benbjohnson/litestream
|
||||
require (
|
||||
filippo.io/age v1.2.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.3 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/psanford/sqlite3vfs v0.0.0-20240315230605-24e1d98cf361 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
modernc.org/sqlite v1.40.1 // indirect
|
||||
)
|
||||
|
||||
// github.com/benbjohnson/litestream/s3
|
||||
require (
|
||||
github.com/aws/aws-sdk-go-v2 v1.37.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.30.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.18.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.85.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.26.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.35.1 // indirect
|
||||
github.com/aws/smithy-go v1.22.5 // indirect
|
||||
)
|
||||
|
||||
replace modernc.org/sqlite => github.com/ncruces/go-sqlite3/litestream/modernc v0.0.0-20251109124432-99b097de3b79
|
||||
@@ -1,197 +0,0 @@
|
||||
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0=
|
||||
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w=
|
||||
cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM=
|
||||
cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU=
|
||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
||||
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI=
|
||||
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
|
||||
cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8=
|
||||
cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
|
||||
filippo.io/age v1.2.1 h1:X0TZjehAZylOIj4DubWYU1vWQxv9bJpo+Uu2/LGhi1o=
|
||||
filippo.io/age v1.2.1/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2 h1:Hr5FTipp7SL07o2FvoVOX9HRiRH3CR3Mj8pxqCcdD5A=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 h1:MhRfI58HblXzCtWEZCO0feHs8LweePB3s90r7WaR1KU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0/go.mod h1:okZ+ZURbArNdlJ+ptXoyHNuOETzOl1Oww19rm8I2WLA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 h1:FwladfywkNirM+FZYLBR2kBz5C8Tg0fw5w5Y7meRXWI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2/go.mod h1:vv5Ad0RrIoT1lJFdWBZwt4mB1+j+V8DUroixmKDTCdk=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.37.1 h1:SMUxeNz3Z6nqGsXv0JuJXc8w5YMtrQMuIBmDx//bBDY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.37.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 h1:6GMWV6CNpA/6fbFHnoAjrv4+LGfyTqZz2LtCHnspgDg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0/go.mod h1:/mXlTIVG9jbxkqDnr5UQNQxW1HRYxeGklkM9vAFeabg=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.30.2 h1:YE1BmSc4fFYqFgN1mN8uzrtc7R9x+7oSWeX8ckoltAw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.30.2/go.mod h1:UNrLGZ6jfAVjgVJpkIxjLufRJqTXCVYOpkeVf83kwBo=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.2 h1:mfm0GKY/PHLhs7KO0sUaOtFnIQ15Qqxt+wXbO/5fIfs=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.18.2/go.mod h1:v0SdJX6ayPeZFQxgXUKw5RhLpAoZUuynxWDfh8+Eknc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1 h1:owmNBboeA0kHKDcdF8KiSXmrIuXZustfMGGytv6OMkM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.1/go.mod h1:Bg1miN59SGxrZqlP8vJZSmXW+1N8Y1MjQDq1OfuNod8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.18.2 h1:YFX4DvH1CPQXgQR8935b46Om+L7+6jus4aTdKqyDR84=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.18.2/go.mod h1:DgMPy7GqxcV0RSyaITnI3rw8HC3lIHB87U3KPQKDxHg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.1 h1:ksZXBYv80EFTcgc8OJO48aQ8XDWXIQL7gGasPeCoTzI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.1/go.mod h1:HSksQyyJETVZS7uM54cir0IgxttTD+8aEoJMPGepHBI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.1 h1:+dn/xF/05utS7tUhjIcndbuaPjfll2LhbH1cCDGLYUQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.1/go.mod h1:hyAGz30LHdm5KBZDI58MXx5lDVZ5CUfvfTZvMu4HCZo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.1 h1:4HbnOGE9491a9zYJ9VpPh1ApgEq6ZlD4Kuv1PJenFpc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.1/go.mod h1:Z6QnHC6TmpJWUxAy8FI4JzA7rTwl6EIANkyK9OR5z5w=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1 h1:ps3nrmBWdWwakZBydGX1CxeYFK80HsQ79JLMwm7Y4/c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.1/go.mod h1:bAdfrfxENre68Hh2swNaGEVuFYE74o0SaSCAlaG9E74=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.1 h1:ky79ysLMxhwk5rxJtS+ILd3Mc8kC5fhsLBrP27r6h4I=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.1/go.mod h1:+2MmkvFvPYM1vsozBWduoLJUi5maxFk5B7KJFECujhY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1 h1:MdVYlN5pcQu1t1OYx4Ajo3fKl1IEhzgdPQbYFCRjYS8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.1/go.mod h1:iikmNLrvHm2p4a3/4BPeix2S9P+nW8yM1IZW73x8bFA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.85.1 h1:Hsqo8+dFxSdDvv9B2PgIx1AJAnDpqgS0znVI+R+MoGY=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.85.1/go.mod h1:8Q0TAPXD68Z8YqlcIGHs/UNIDHsxErV9H4dl4vJEpgw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.26.1 h1:uWaz3DoNK9MNhm7i6UGxqufwu3BEuJZm72WlpGwyVtY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.26.1/go.mod h1:ILpVNjL0BO+Z3Mm0SbEeUoYS9e0eJWV1BxNppp0fcb8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1 h1:XdG6/o1/ZDmn3wJU5SRAejHaWgKS4zHv0jBamuKuS2k=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.31.1/go.mod h1:oiotGTKadCOCl3vg/tYh4k45JlDF81Ka8rdumNhEnIQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.35.1 h1:iF4Xxkc0H9c/K2dS0zZw3SCkj0Z7n6AMnUiiyoJND+I=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.35.1/go.mod h1:0bxIatfN0aLq4mjoLDeBpOjOke68OsFlXPDFJ7V0MYw=
|
||||
github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw=
|
||||
github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||
github.com/benbjohnson/litestream v0.5.2 h1:uD9I17n6RgUgyCwPM/Sw2YXNmMGixecUB5kmJ4FL08o=
|
||||
github.com/benbjohnson/litestream v0.5.2/go.mod h1:jSW6AGqbxmJnEXGjMHchlZclGphzbJ6jGrGo5fYIDhU=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
|
||||
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nats-io/nats.go v1.44.0 h1:ECKVrDLdh/kDPV1g0gAQ+2+m2KprqZK5O/eJAyAnH2M=
|
||||
github.com/nats-io/nats.go v1.44.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/ncruces/go-sqlite3 v0.30.1 h1:pHC3YsyRdJv4pCMB4MO1Q2BXw/CAa+Hoj7GSaKtVk+g=
|
||||
github.com/ncruces/go-sqlite3 v0.30.1/go.mod h1:UVsWrQaq1qkcal5/vT5lOJnZCVlR5rsThKdwidjFsKc=
|
||||
github.com/ncruces/go-sqlite3/litestream/modernc v0.0.0-20251109124432-99b097de3b79 h1:evpQceUV2vRbOe84U/QhBBchfqFERRHTx1JOadFFMLE=
|
||||
github.com/ncruces/go-sqlite3/litestream/modernc v0.0.0-20251109124432-99b097de3b79/go.mod h1:GSM2gXEOb9HIFFtsl0IUtnpvpDmVi7Kbp8z5GzwA0Tw=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/ncruces/wbt v0.2.0 h1:Q9zlKOBSZc7Yy/R2cGa35g6RKUUE3BjNIW3tfGC4F04=
|
||||
github.com/ncruces/wbt v0.2.0/go.mod h1:DtF92amvMxH69EmBFUSFWRDAlo6hOEfoNQnClxj9C/c=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
|
||||
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.3 h1:shd26MlnwTw5jksTDhC7rTQIteBxy+ZZDr3t7F2xN2Q=
|
||||
github.com/prometheus/common v0.67.3/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/psanford/sqlite3vfs v0.0.0-20240315230605-24e1d98cf361 h1:vAKifIJuYY306ZJSrwDgKonWcJGELijdaenABqbV03E=
|
||||
github.com/psanford/sqlite3vfs v0.0.0-20240315230605-24e1d98cf361/go.mod h1:iW4cSew5PAb1sMZiTEkVJAIBNrepaB6jTYjeP47WtI0=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/superfly/ltx v0.5.0 h1:dXNrcT3ZtMb6iKZopIV7z5UBscnapg0b0F02loQsk5o=
|
||||
github.com/superfly/ltx v0.5.0/go.mod h1:Nf50QAIXU/ET4ua3AuQ2fh31MbgNQZA7r/DYx6Os77s=
|
||||
github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8=
|
||||
github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.154.0 h1:X7QkVKZBskztmpPKWQXgjJRPA2dJYrL6r+sYPRLj050=
|
||||
google.golang.org/api v0.154.0/go.mod h1:qhSMkM85hgqiokIYsrRyKxrjfBeIhgl4Z2JmeRkYylc=
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
|
||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA=
|
||||
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -1,11 +0,0 @@
|
||||
module modernc.org/sqlite
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require github.com/ncruces/go-sqlite3 v0.30.1
|
||||
|
||||
require (
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.10.1 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
github.com/ncruces/go-sqlite3 v0.30.1 h1:pHC3YsyRdJv4pCMB4MO1Q2BXw/CAa+Hoj7GSaKtVk+g=
|
||||
github.com/ncruces/go-sqlite3 v0.30.1/go.mod h1:UVsWrQaq1qkcal5/vT5lOJnZCVlR5rsThKdwidjFsKc=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8=
|
||||
github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
@@ -1,20 +0,0 @@
|
||||
// Package sqlite provides a shim that allows Litestream to work with the ncruces SQLite driver.
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"slices"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/driver"
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if !slices.Contains(sql.Drivers(), "sqlite") {
|
||||
sql.Register("sqlite", &driver.SQLite{})
|
||||
}
|
||||
}
|
||||
|
||||
type FileControl interface {
|
||||
FileControlPersistWAL(string, int) (int, error)
|
||||
}
|
||||
@@ -1,309 +0,0 @@
|
||||
package litestream
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/litestream"
|
||||
"github.com/superfly/ltx"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
"github.com/ncruces/go-sqlite3/util/vfsutil"
|
||||
"github.com/ncruces/go-sqlite3/vfs"
|
||||
"github.com/ncruces/wbt"
|
||||
)
|
||||
|
||||
type liteVFS struct{}
|
||||
|
||||
func (liteVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) {
|
||||
// Temp journals, as used by the sorter, use SliceFile.
|
||||
if flags&vfs.OPEN_TEMP_JOURNAL != 0 {
|
||||
return &vfsutil.SliceFile{}, flags | vfs.OPEN_MEMORY, nil
|
||||
}
|
||||
// Refuse to open all other file types.
|
||||
if flags&vfs.OPEN_MAIN_DB == 0 {
|
||||
return nil, flags, sqlite3.CANTOPEN
|
||||
}
|
||||
|
||||
liteMtx.RLock()
|
||||
defer liteMtx.RUnlock()
|
||||
if db, ok := liteDBs[name]; ok {
|
||||
// Build the page index so we can lookup individual pages.
|
||||
if err := db.buildIndex(context.Background()); err != nil {
|
||||
db.opts.Logger.Error("build index", "error", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
return &liteFile{db: db}, flags | vfs.OPEN_READONLY, nil
|
||||
}
|
||||
return nil, flags, sqlite3.CANTOPEN
|
||||
}
|
||||
|
||||
func (liteVFS) Delete(name string, dirSync bool) error {
|
||||
// notest // used to delete journals
|
||||
return sqlite3.IOERR_DELETE_NOENT
|
||||
}
|
||||
|
||||
func (liteVFS) Access(name string, flag vfs.AccessFlag) (bool, error) {
|
||||
// notest // used to check for journals
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (liteVFS) FullPathname(name string) (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
type liteFile struct {
|
||||
db *liteDB
|
||||
conn *sqlite3.Conn
|
||||
pages *pageIndex
|
||||
txid ltx.TXID
|
||||
pageSize uint32
|
||||
}
|
||||
|
||||
func (f *liteFile) Close() error { return nil }
|
||||
|
||||
func (f *liteFile) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
ctx := f.context()
|
||||
pages, txid := f.pages, f.txid
|
||||
if pages == nil {
|
||||
pages, txid, err = f.db.pollReplica(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pgno := uint32(1)
|
||||
if off >= 512 {
|
||||
pgno += uint32(off / int64(f.pageSize))
|
||||
}
|
||||
|
||||
elem, ok := pages.Get(pgno)
|
||||
if !ok {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
data, err := f.db.cache.getOrFetch(pgno, elem.MaxTXID, func() (any, error) {
|
||||
_, data, err := litestream.FetchPage(ctx, f.db.client, elem.Level, elem.MinTXID, elem.MaxTXID, elem.Offset, elem.Size)
|
||||
return data, err
|
||||
})
|
||||
if err != nil {
|
||||
f.db.opts.Logger.Error("fetch page", "error", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Update the first page to pretend we are in journal mode,
|
||||
// load the page size and track changes to the database.
|
||||
if pgno == 1 && len(data) >= 100 &&
|
||||
data[18] >= 1 && data[19] >= 1 &&
|
||||
data[18] <= 3 && data[19] <= 3 {
|
||||
data[18], data[19] = 0x01, 0x01
|
||||
binary.BigEndian.PutUint32(data[24:28], uint32(txid))
|
||||
f.pageSize = uint32(256 * binary.LittleEndian.Uint16(data[16:18]))
|
||||
}
|
||||
|
||||
n = copy(p, data[off%int64(len(data)):])
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (f *liteFile) WriteAt(b []byte, off int64) (n int, err error) {
|
||||
// notest // OPEN_READONLY
|
||||
return 0, sqlite3.IOERR_WRITE
|
||||
}
|
||||
|
||||
func (f *liteFile) Truncate(size int64) error {
|
||||
// notest // OPEN_READONLY
|
||||
return sqlite3.IOERR_TRUNCATE
|
||||
}
|
||||
|
||||
func (f *liteFile) Sync(flag vfs.SyncFlag) error {
|
||||
// notest // OPEN_READONLY
|
||||
return sqlite3.IOERR_FSYNC
|
||||
}
|
||||
|
||||
func (f *liteFile) Size() (size int64, err error) {
|
||||
if max := f.pages.Max(); max != nil {
|
||||
size = int64(max.Key()) * int64(f.pageSize)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *liteFile) Lock(lock vfs.LockLevel) (err error) {
|
||||
if lock >= vfs.LOCK_RESERVED {
|
||||
return sqlite3.IOERR_LOCK
|
||||
}
|
||||
f.pages, f.txid, err = f.db.pollReplica(f.context())
|
||||
return err
|
||||
}
|
||||
|
||||
func (f *liteFile) Unlock(lock vfs.LockLevel) error {
|
||||
f.pages, f.txid = nil, 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *liteFile) CheckReservedLock() (bool, error) {
|
||||
// notest // used to check for hot journals
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (f *liteFile) SectorSize() int {
|
||||
// notest // safe default
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *liteFile) DeviceCharacteristics() vfs.DeviceCharacteristic {
|
||||
// notest // safe default
|
||||
return 0
|
||||
}
|
||||
|
||||
func (f *liteFile) SetDB(conn any) {
|
||||
f.conn = conn.(*sqlite3.Conn)
|
||||
}
|
||||
|
||||
func (f *liteFile) context() context.Context {
|
||||
if f.conn != nil {
|
||||
return f.conn.GetInterrupt()
|
||||
}
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
type liteDB struct {
|
||||
client litestream.ReplicaClient
|
||||
opts ReplicaOptions
|
||||
cache pageCache
|
||||
pages *pageIndex // +checklocks:mtx
|
||||
lastPoll time.Time // +checklocks:mtx
|
||||
txids levelTXIDs // +checklocks:mtx
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
func (f *liteDB) buildIndex(ctx context.Context) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
// Skip if we already have an index.
|
||||
if f.pages != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build the index from scratch from a Litestream restore plan.
|
||||
infos, err := litestream.CalcRestorePlan(ctx, f.client, 0, time.Time{}, f.opts.Logger)
|
||||
if err != nil {
|
||||
if !errors.Is(err, litestream.ErrTxNotAvailable) {
|
||||
return fmt.Errorf("calc restore plan: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
err := f.updateInfo(ctx, info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
f.lastPoll = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *liteDB) pollReplica(ctx context.Context) (*pageIndex, ltx.TXID, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
// Limit polling interval.
|
||||
if time.Since(f.lastPoll) < f.opts.PollInterval {
|
||||
return f.pages, f.txids[0], nil
|
||||
}
|
||||
|
||||
for level := range pollLevels(f.opts.MinLevel) {
|
||||
if err := f.updateLevel(ctx, level); err != nil {
|
||||
f.opts.Logger.Error("cannot poll replica", "error", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
f.lastPoll = time.Now()
|
||||
return f.pages, f.txids[0], nil
|
||||
}
|
||||
|
||||
// +checklocks:f.mtx
|
||||
func (f *liteDB) updateLevel(ctx context.Context, level int) error {
|
||||
var nextTXID ltx.TXID
|
||||
// Snapshots must start from scratch,
|
||||
// other levels can start from where they were left.
|
||||
if level != litestream.SnapshotLevel {
|
||||
nextTXID = f.txids[level] + 1
|
||||
}
|
||||
|
||||
// Start reading from the next LTX file after the current position.
|
||||
itr, err := f.client.LTXFiles(ctx, level, nextTXID, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ltx files: %w", err)
|
||||
}
|
||||
defer itr.Close()
|
||||
|
||||
// Build an update across all new LTX files.
|
||||
for itr.Next() {
|
||||
info := itr.Item()
|
||||
|
||||
// Skip LTX files already fully loaded into the index.
|
||||
if info.MaxTXID <= f.txids[level] {
|
||||
continue
|
||||
}
|
||||
|
||||
err := f.updateInfo(ctx, info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := itr.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return itr.Close()
|
||||
}
|
||||
|
||||
// +checklocks:f.mtx
|
||||
func (f *liteDB) updateInfo(ctx context.Context, info *ltx.FileInfo) error {
|
||||
idx, err := litestream.FetchPageIndex(ctx, f.client, info)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetch page index: %w", err)
|
||||
}
|
||||
|
||||
// Replace pages in the index with new pages.
|
||||
for k, v := range idx {
|
||||
// Patch avoids mutating the index for an unmodified page.
|
||||
f.pages = f.pages.Patch(k, func(node *pageIndex) (ltx.PageIndexElem, bool) {
|
||||
return v, node == nil || v != node.Value()
|
||||
})
|
||||
}
|
||||
|
||||
// Track the MaxTXID for each level.
|
||||
maxTXID := &f.txids[info.Level]
|
||||
*maxTXID = max(*maxTXID, info.MaxTXID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func pollLevels(minLevel int) (r []int) {
|
||||
// Updating from lower to upper levels is non-racy,
|
||||
// since LTX files are compacted into higher levels
|
||||
// before the lower level LTX files are deleted.
|
||||
|
||||
// Also, only level 0 compactions and snapshots delete files,
|
||||
// so the intermediate levels never need to be updated.
|
||||
|
||||
if minLevel <= 0 {
|
||||
return append(r, 0, 1, litestream.SnapshotLevel)
|
||||
}
|
||||
if minLevel >= litestream.SnapshotLevel {
|
||||
return append(r, litestream.SnapshotLevel)
|
||||
}
|
||||
return append(r, minLevel, litestream.SnapshotLevel)
|
||||
}
|
||||
|
||||
// Type aliases; these are a mouthful.
|
||||
type pageIndex = wbt.Tree[uint32, ltx.PageIndexElem]
|
||||
type levelTXIDs = [litestream.SnapshotLevel + 1]ltx.TXID
|
||||
@@ -1,34 +0,0 @@
|
||||
package litestream
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/benbjohnson/litestream"
|
||||
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
)
|
||||
|
||||
func Test_pollLevels(t *testing.T) {
|
||||
tests := []struct {
|
||||
minLevel int
|
||||
want []int
|
||||
}{
|
||||
{minLevel: -1, want: []int{0, 1, litestream.SnapshotLevel}},
|
||||
{minLevel: 0, want: []int{0, 1, litestream.SnapshotLevel}},
|
||||
{minLevel: 1, want: []int{1, litestream.SnapshotLevel}},
|
||||
{minLevel: 2, want: []int{2, litestream.SnapshotLevel}},
|
||||
{minLevel: 3, want: []int{3, litestream.SnapshotLevel}},
|
||||
{minLevel: litestream.SnapshotLevel, want: []int{litestream.SnapshotLevel}},
|
||||
{minLevel: litestream.SnapshotLevel + 1, want: []int{litestream.SnapshotLevel}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(strconv.Itoa(tt.minLevel), func(t *testing.T) {
|
||||
got := pollLevels(tt.minLevel)
|
||||
if !slices.Equal(got, tt.want) {
|
||||
t.Errorf("pollLevels() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,8 +14,8 @@ var (
|
||||
// https://sqlite.org/c3ref/auto_extension.html
|
||||
func AutoExtension(entryPoint func(*Conn) error) {
|
||||
extRegistryMtx.Lock()
|
||||
defer extRegistryMtx.Unlock()
|
||||
extRegistry = append(extRegistry, entryPoint)
|
||||
extRegistryMtx.Unlock()
|
||||
}
|
||||
|
||||
func initExtensions(c *Conn) error {
|
||||
|
||||
@@ -3,6 +3,7 @@ package sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"math/bits"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -268,11 +269,10 @@ func (a *arena) mark() (reset func()) {
|
||||
ptrs := len(a.ptrs)
|
||||
next := a.next
|
||||
return func() {
|
||||
rest := a.ptrs[ptrs:]
|
||||
for _, ptr := range a.ptrs[:ptrs] {
|
||||
for _, ptr := range a.ptrs[ptrs:] {
|
||||
a.sqlt.free(ptr)
|
||||
}
|
||||
a.ptrs = rest
|
||||
a.ptrs = a.ptrs[:ptrs]
|
||||
a.next = next
|
||||
}
|
||||
}
|
||||
@@ -351,5 +351,25 @@ func exportCallbacks(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
|
||||
util.ExportFuncII(env, "go_cur_eof", cursorEOFCallback)
|
||||
util.ExportFuncIIII(env, "go_cur_column", cursorColumnCallback)
|
||||
util.ExportFuncIII(env, "go_cur_rowid", cursorRowIDCallback)
|
||||
// Math functions.
|
||||
util.ExportFuncDD(env, "exp", math.Exp)
|
||||
util.ExportFuncDD(env, "log", math.Log)
|
||||
util.ExportFuncDD(env, "log2", math.Log2)
|
||||
util.ExportFuncDD(env, "log10", math.Log10)
|
||||
util.ExportFuncDD(env, "cos", math.Cos)
|
||||
util.ExportFuncDD(env, "cosh", math.Cosh)
|
||||
util.ExportFuncDD(env, "sin", math.Sin)
|
||||
util.ExportFuncDD(env, "sinh", math.Sinh)
|
||||
util.ExportFuncDD(env, "tan", math.Tan)
|
||||
util.ExportFuncDD(env, "tanh", math.Tanh)
|
||||
util.ExportFuncDD(env, "acos", math.Acos)
|
||||
util.ExportFuncDD(env, "acosh", math.Acosh)
|
||||
util.ExportFuncDD(env, "asin", math.Asin)
|
||||
util.ExportFuncDD(env, "asinh", math.Asinh)
|
||||
util.ExportFuncDD(env, "atan", math.Atan)
|
||||
util.ExportFuncDD(env, "atanh", math.Atanh)
|
||||
util.ExportFuncDDD(env, "atan2", math.Atan2)
|
||||
util.ExportFuncDDD(env, "fmod", math.Mod)
|
||||
util.ExportFuncDDD(env, "pow", math.Pow)
|
||||
return env
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# handle, and interrupt, sqlite3_busy_timeout.
|
||||
--- sqlite3.c.orig
|
||||
+++ sqlite3.c
|
||||
@@ -186667,7 +186667,7 @@
|
||||
@@ -188066,7 +188066,7 @@
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
if( ms>0 ){
|
||||
|
||||
+3
-6
@@ -3,11 +3,11 @@ set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
curl -#OL "https://sqlite.org/2025/sqlite-autoconf-3510000.tar.gz"
|
||||
curl -#OL "https://sqlite.org/2026/sqlite-autoconf-3510300.tar.gz"
|
||||
|
||||
# Verify download.
|
||||
if hash=$(openssl dgst -sha3-256 sqlite-autoconf-*.tar.gz); then
|
||||
if ! [[ $hash =~ fa52f9cc74dbca004aa650ae698036a3350611f672649e165078f4eae21d6a2e ]]; then
|
||||
if ! [[ $hash =~ 581215771b32ea4c4062e6fb9842c4aa43d0a7fb2b6670ff6fa4ebb807781204 ]]; then
|
||||
echo $hash
|
||||
exit 1
|
||||
fi
|
||||
@@ -15,15 +15,12 @@ fi 2> /dev/null
|
||||
|
||||
tar xzf sqlite-autoconf-*.tar.gz
|
||||
|
||||
# To test a snapshot instead:
|
||||
# curl -# https://sqlite.org/snapshot/sqlite-snapshot-202410081727.tar.gz | tar xz
|
||||
|
||||
mv sqlite-*/sqlite3.c .
|
||||
mv sqlite-*/sqlite3.h .
|
||||
mv sqlite-*/sqlite3ext.h .
|
||||
rm -r sqlite-*
|
||||
|
||||
GITHUB_TAG="https://github.com/sqlite/sqlite/raw/version-3.51.0"
|
||||
GITHUB_TAG="https://github.com/sqlite/sqlite/raw/version-3.51.3"
|
||||
|
||||
mkdir -p ext/
|
||||
cd ext/
|
||||
|
||||
+4
-1
@@ -3,5 +3,8 @@ set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
ROOT=../
|
||||
WASI_SDK="$ROOT/tools/wasi-sdk/bin"
|
||||
|
||||
shopt -s extglob
|
||||
clang-format -i !(sqlite3*).@(c|h)
|
||||
"$WASI_SDK/clang-format" --style=Google -i !(sqlite3*).@(c|h)
|
||||
|
||||
+22
-22
@@ -4,22 +4,22 @@
|
||||
#include "include.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
void go_collation_needed(void *, sqlite3 *, int, const char *);
|
||||
void go_collation_needed(void*, sqlite3*, int, const char*);
|
||||
|
||||
int go_compare(go_handle, int, const void *, int, const void *);
|
||||
int go_compare(go_handle, int, const void*, int, const void*);
|
||||
|
||||
void go_func(sqlite3_context *, go_handle, int, sqlite3_value **);
|
||||
void go_func(sqlite3_context*, go_handle, int, sqlite3_value**);
|
||||
|
||||
void go_step(sqlite3_context *, go_handle *, go_handle, int, sqlite3_value **);
|
||||
void go_value(sqlite3_context *, go_handle *, go_handle, bool);
|
||||
void go_inverse(sqlite3_context *, go_handle, int, sqlite3_value **);
|
||||
void go_step(sqlite3_context*, go_handle*, go_handle, int, sqlite3_value**);
|
||||
void go_value(sqlite3_context*, go_handle*, go_handle, bool);
|
||||
void go_inverse(sqlite3_context*, go_handle, int, sqlite3_value**);
|
||||
|
||||
void go_func_wrapper(sqlite3_context *ctx, int nArg, sqlite3_value **pArg) {
|
||||
void go_func_wrapper(sqlite3_context* ctx, int nArg, sqlite3_value** pArg) {
|
||||
go_func(ctx, sqlite3_user_data(ctx), nArg, pArg);
|
||||
}
|
||||
|
||||
void go_step_wrapper(sqlite3_context *ctx, int nArg, sqlite3_value **pArg) {
|
||||
go_handle *agg = sqlite3_aggregate_context(ctx, 4);
|
||||
void go_step_wrapper(sqlite3_context* ctx, int nArg, sqlite3_value** pArg) {
|
||||
go_handle* agg = sqlite3_aggregate_context(ctx, 4);
|
||||
go_handle data = NULL;
|
||||
if (agg == NULL || *agg == NULL) {
|
||||
data = sqlite3_user_data(ctx);
|
||||
@@ -27,8 +27,8 @@ void go_step_wrapper(sqlite3_context *ctx, int nArg, sqlite3_value **pArg) {
|
||||
go_step(ctx, agg, data, nArg, pArg);
|
||||
}
|
||||
|
||||
void go_value_wrapper(sqlite3_context *ctx) {
|
||||
go_handle *agg = sqlite3_aggregate_context(ctx, 4);
|
||||
void go_value_wrapper(sqlite3_context* ctx) {
|
||||
go_handle* agg = sqlite3_aggregate_context(ctx, 4);
|
||||
go_handle data = NULL;
|
||||
if (agg == NULL || *agg == NULL) {
|
||||
data = sqlite3_user_data(ctx);
|
||||
@@ -36,8 +36,8 @@ void go_value_wrapper(sqlite3_context *ctx) {
|
||||
go_value(ctx, agg, data, /*final=*/false);
|
||||
}
|
||||
|
||||
void go_final_wrapper(sqlite3_context *ctx) {
|
||||
go_handle *agg = sqlite3_aggregate_context(ctx, 0);
|
||||
void go_final_wrapper(sqlite3_context* ctx) {
|
||||
go_handle* agg = sqlite3_aggregate_context(ctx, 0);
|
||||
go_handle data = NULL;
|
||||
if (agg == NULL || *agg == NULL) {
|
||||
data = sqlite3_user_data(ctx);
|
||||
@@ -45,17 +45,17 @@ void go_final_wrapper(sqlite3_context *ctx) {
|
||||
go_value(ctx, agg, data, /*final=*/true);
|
||||
}
|
||||
|
||||
void go_inverse_wrapper(sqlite3_context *ctx, int nArg, sqlite3_value **pArg) {
|
||||
go_handle *agg = sqlite3_aggregate_context(ctx, 0);
|
||||
void go_inverse_wrapper(sqlite3_context* ctx, int nArg, sqlite3_value** pArg) {
|
||||
go_handle* agg = sqlite3_aggregate_context(ctx, 0);
|
||||
go_inverse(ctx, *agg, nArg, pArg);
|
||||
}
|
||||
|
||||
int sqlite3_collation_needed_go(sqlite3 *db, bool enable) {
|
||||
int sqlite3_collation_needed_go(sqlite3* db, bool enable) {
|
||||
return sqlite3_collation_needed(db, /*arg=*/NULL,
|
||||
enable ? go_collation_needed : NULL);
|
||||
}
|
||||
|
||||
int sqlite3_create_collation_go(sqlite3 *db, const char *name, go_handle app) {
|
||||
int sqlite3_create_collation_go(sqlite3* db, const char* name, go_handle app) {
|
||||
if (app == NULL) {
|
||||
return sqlite3_create_collation_v2(db, name, SQLITE_UTF8, NULL, NULL, NULL);
|
||||
}
|
||||
@@ -65,7 +65,7 @@ int sqlite3_create_collation_go(sqlite3 *db, const char *name, go_handle app) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3_create_function_go(sqlite3 *db, const char *name, int argc,
|
||||
int sqlite3_create_function_go(sqlite3* db, const char* name, int argc,
|
||||
int flags, go_handle app) {
|
||||
if (app == NULL) {
|
||||
return sqlite3_create_function_v2(db, name, argc, SQLITE_UTF8 | flags, NULL,
|
||||
@@ -76,7 +76,7 @@ int sqlite3_create_function_go(sqlite3 *db, const char *name, int argc,
|
||||
/*final=*/NULL, go_destroy);
|
||||
}
|
||||
|
||||
int sqlite3_create_aggregate_function_go(sqlite3 *db, const char *name,
|
||||
int sqlite3_create_aggregate_function_go(sqlite3* db, const char* name,
|
||||
int argc, int flags, go_handle app) {
|
||||
if (app == NULL) {
|
||||
return sqlite3_create_function_v2(db, name, argc, SQLITE_UTF8 | flags, NULL,
|
||||
@@ -87,7 +87,7 @@ int sqlite3_create_aggregate_function_go(sqlite3 *db, const char *name,
|
||||
go_final_wrapper, go_destroy);
|
||||
}
|
||||
|
||||
int sqlite3_create_window_function_go(sqlite3 *db, const char *name, int argc,
|
||||
int sqlite3_create_window_function_go(sqlite3* db, const char* name, int argc,
|
||||
int flags, go_handle app) {
|
||||
if (app == NULL) {
|
||||
return sqlite3_create_window_function(db, name, argc, SQLITE_UTF8 | flags,
|
||||
@@ -98,6 +98,6 @@ int sqlite3_create_window_function_go(sqlite3 *db, const char *name, int argc,
|
||||
go_final_wrapper, go_value_wrapper, go_inverse_wrapper, go_destroy);
|
||||
}
|
||||
|
||||
void sqlite3_set_auxdata_go(sqlite3_context *ctx, int i, go_handle aux) {
|
||||
void sqlite3_set_auxdata_go(sqlite3_context* ctx, int i, go_handle aux) {
|
||||
sqlite3_set_auxdata(ctx, i, aux, go_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
+25
-25
@@ -2,56 +2,56 @@
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
int go_progress_handler(void *);
|
||||
int go_busy_handler(void *, int);
|
||||
int go_progress_handler(void*);
|
||||
int go_busy_handler(void*, int);
|
||||
int go_busy_timeout(int count, int tmout);
|
||||
|
||||
int go_commit_hook(void *);
|
||||
void go_rollback_hook(void *);
|
||||
void go_update_hook(void *, int, char const *, char const *, sqlite3_int64);
|
||||
int go_wal_hook(void *, sqlite3 *, const char *, int);
|
||||
int go_trace(unsigned, void *, void *, void *);
|
||||
int go_authorizer(void *, int, const char *, const char *, const char *,
|
||||
const char *);
|
||||
int go_commit_hook(void*);
|
||||
void go_rollback_hook(void*);
|
||||
void go_update_hook(void*, int, char const*, char const*, sqlite3_int64);
|
||||
int go_wal_hook(void*, sqlite3*, const char*, int);
|
||||
int go_trace(unsigned, void*, void*, void*);
|
||||
int go_authorizer(void*, int, const char*, const char*, const char*,
|
||||
const char*);
|
||||
|
||||
void go_log(void *, int, const char *);
|
||||
void go_log(void*, int, const char*);
|
||||
|
||||
unsigned int go_autovacuum_pages(void *, const char *, unsigned int,
|
||||
unsigned int, unsigned int);
|
||||
unsigned int go_autovacuum_pages(void*, const char*, unsigned int, unsigned int,
|
||||
unsigned int);
|
||||
|
||||
void sqlite3_log_go(int iErrCode, const char *zMsg) {
|
||||
void sqlite3_log_go(int iErrCode, const char* zMsg) {
|
||||
sqlite3_log(iErrCode, "%s", zMsg);
|
||||
}
|
||||
|
||||
void sqlite3_progress_handler_go(sqlite3 *db, int n) {
|
||||
void sqlite3_progress_handler_go(sqlite3* db, int n) {
|
||||
sqlite3_progress_handler(db, n, go_progress_handler, /*arg=*/NULL);
|
||||
}
|
||||
|
||||
int sqlite3_busy_handler_go(sqlite3 *db, bool enable) {
|
||||
int sqlite3_busy_handler_go(sqlite3* db, bool enable) {
|
||||
return sqlite3_busy_handler(db, enable ? go_busy_handler : NULL, /*arg=*/db);
|
||||
}
|
||||
|
||||
void sqlite3_commit_hook_go(sqlite3 *db, bool enable) {
|
||||
void sqlite3_commit_hook_go(sqlite3* db, bool enable) {
|
||||
sqlite3_commit_hook(db, enable ? go_commit_hook : NULL, /*arg=*/db);
|
||||
}
|
||||
|
||||
void sqlite3_rollback_hook_go(sqlite3 *db, bool enable) {
|
||||
void sqlite3_rollback_hook_go(sqlite3* db, bool enable) {
|
||||
sqlite3_rollback_hook(db, enable ? go_rollback_hook : NULL, /*arg=*/db);
|
||||
}
|
||||
|
||||
void sqlite3_update_hook_go(sqlite3 *db, bool enable) {
|
||||
void sqlite3_update_hook_go(sqlite3* db, bool enable) {
|
||||
sqlite3_update_hook(db, enable ? go_update_hook : NULL, /*arg=*/db);
|
||||
}
|
||||
|
||||
void sqlite3_wal_hook_go(sqlite3 *db, bool enable) {
|
||||
void sqlite3_wal_hook_go(sqlite3* db, bool enable) {
|
||||
sqlite3_wal_hook(db, enable ? go_wal_hook : NULL, /*arg=*/NULL);
|
||||
}
|
||||
|
||||
int sqlite3_set_authorizer_go(sqlite3 *db, bool enable) {
|
||||
int sqlite3_set_authorizer_go(sqlite3* db, bool enable) {
|
||||
return sqlite3_set_authorizer(db, enable ? go_authorizer : NULL, /*arg=*/db);
|
||||
}
|
||||
|
||||
int sqlite3_trace_go(sqlite3 *db, unsigned mask) {
|
||||
int sqlite3_trace_go(sqlite3* db, unsigned mask) {
|
||||
return sqlite3_trace_v2(db, mask, go_trace, /*arg=*/db);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ int sqlite3_config_log_go(bool enable) {
|
||||
/*arg=*/NULL);
|
||||
}
|
||||
|
||||
int sqlite3_autovacuum_pages_go(sqlite3 *db, go_handle app) {
|
||||
int sqlite3_autovacuum_pages_go(sqlite3* db, go_handle app) {
|
||||
if (app == NULL) {
|
||||
return sqlite3_autovacuum_pages(db, NULL, NULL, NULL);
|
||||
}
|
||||
@@ -69,8 +69,8 @@ int sqlite3_autovacuum_pages_go(sqlite3 *db, go_handle app) {
|
||||
|
||||
#ifndef sqliteBusyCallback
|
||||
|
||||
static int sqliteBusyCallback(void *ptr, int count) {
|
||||
return go_busy_timeout(count, ((sqlite3 *)ptr)->busyTimeout);
|
||||
static int sqliteBusyCallback(void* ptr, int count) {
|
||||
return go_busy_timeout(count, ((sqlite3*)ptr)->busyTimeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -5,8 +5,8 @@
|
||||
|
||||
// https://github.com/JuliaLang/julia/blob/v1.9.4/src/julia.h#L67-L68
|
||||
#define container_of(ptr, type, member) \
|
||||
((type *)((char *)(ptr) - offsetof(type, member)))
|
||||
((type*)((char*)(ptr) - offsetof(type, member)))
|
||||
|
||||
typedef void *go_handle;
|
||||
typedef void* go_handle;
|
||||
void go_destroy(go_handle);
|
||||
static_assert(sizeof(go_handle) == 4, "Unexpected size");
|
||||
static_assert(sizeof(go_handle) == 4, "Unexpected size");
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# SQLite Libc
|
||||
|
||||
This is a minimal libc that offers just enough
|
||||
to compile SQLite for wasm32 with nostdlib.
|
||||
|
||||
The allocator is either Doug Lea's malloc or a bump allocator,
|
||||
and math is provided by the host side.
|
||||
@@ -0,0 +1,3 @@
|
||||
#ifndef assert
|
||||
#define assert(ignore) ((void)0)
|
||||
#endif
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
touch empty.S
|
||||
./build.sh empty.S
|
||||
go test -bench=.
|
||||
rm -f empty.S
|
||||
|
||||
./build.sh
|
||||
go test -bench=.
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
ROOT=../../
|
||||
BINARYEN="$ROOT/tools/binaryen/bin"
|
||||
WASI_SDK="$ROOT/tools/wasi-sdk/bin"
|
||||
SRCS="${1:-libc.c}"
|
||||
"../tools.sh"
|
||||
|
||||
trap 'rm -f libc.c libc.tmp' EXIT
|
||||
cat << EOF > libc.c
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
EOF
|
||||
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -g0 -O2 \
|
||||
-Wall -Wextra -Wno-unused-parameter -Wno-unused-function \
|
||||
-o libc.wasm -I. "$SRCS" \
|
||||
-mexec-model=reactor \
|
||||
-mmutable-globals -mnontrapping-fptoint \
|
||||
-msimd128 -mbulk-memory -msign-ext \
|
||||
-mreference-types -mmultivalue \
|
||||
-mno-extended-const \
|
||||
-fno-stack-protector \
|
||||
-Wl,-z,stack-size=4096 \
|
||||
-Wl,--stack-first \
|
||||
-Wl,--import-undefined \
|
||||
-Wl,--initial-memory=16777216 \
|
||||
-Wl,--export=memchr \
|
||||
-Wl,--export=memcmp \
|
||||
-Wl,--export=memcpy \
|
||||
-Wl,--export=memmove \
|
||||
-Wl,--export=memrchr \
|
||||
-Wl,--export=memset \
|
||||
-Wl,--export=strchr \
|
||||
-Wl,--export=strchrnul \
|
||||
-Wl,--export=strcspn \
|
||||
-Wl,--export=strlen \
|
||||
-Wl,--export=strrchr \
|
||||
-Wl,--export=strspn \
|
||||
-Wl,--export=qsort
|
||||
|
||||
"$BINARYEN/wasm-ctor-eval" -g -c _initialize libc.wasm -o libc.tmp
|
||||
"$BINARYEN/wasm-opt" -g libc.tmp -o libc.wasm \
|
||||
--low-memory-unused --generate-global-effects --converge -O3 \
|
||||
--enable-mutable-globals --enable-nontrapping-float-to-int \
|
||||
--enable-simd --enable-bulk-memory --enable-sign-ext \
|
||||
--enable-reference-types --enable-multivalue \
|
||||
--strip --strip-debug --strip-producers
|
||||
|
||||
"$BINARYEN/wasm-dis" -o libc.wat libc.wasm
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <ctype.h>
|
||||
|
||||
int(isalnum)(int c) { return isalpha(c) || isdigit(c); }
|
||||
int(isalpha)(int c) { return ((unsigned)c | 32) - 'a' < 26; }
|
||||
int(isascii)(int c) { return !(c & ~0x7f); }
|
||||
int(isblank)(int c) { return (c == ' ' || c == '\t'); }
|
||||
int(iscntrl)(int c) { return (unsigned)c < 0x20 || c == 0x7f; }
|
||||
int(isdigit)(int c) { return (unsigned)c - '0' < 10; }
|
||||
int(isgraph)(int c) { return (unsigned)c - 0x21 < 0x5e; }
|
||||
int(islower)(int c) { return (unsigned)c - 'a' < 26; }
|
||||
int(isprint)(int c) { return (unsigned)c - 0x20 < 0x5f; }
|
||||
int(ispunct)(int c) { return isgraph(c) && !isalnum(c); }
|
||||
int(isspace)(int c) { return c == ' ' || (unsigned)c - '\t' < 5; }
|
||||
int(isupper)(int c) { return (unsigned)c - 'A' < 26; }
|
||||
int(isxdigit)(int c) { return isdigit(c) || ((unsigned)c | 32) - 'a' < 6; }
|
||||
|
||||
int(tolower)(int c) { return isupper(c) ? c | 32 : c; }
|
||||
int(toupper)(int c) { return islower(c) ? c & 0x5f : c; }
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
int isalnum(int);
|
||||
int isalpha(int);
|
||||
int isascii(int);
|
||||
int isblank(int);
|
||||
int iscntrl(int);
|
||||
int isdigit(int);
|
||||
int isgraph(int);
|
||||
int islower(int);
|
||||
int isprint(int);
|
||||
int ispunct(int);
|
||||
int isspace(int);
|
||||
int isupper(int);
|
||||
int isxdigit(int);
|
||||
|
||||
int tolower(int);
|
||||
int toupper(int);
|
||||
|
||||
#define isalpha(a) (0 ? isalpha(a) : (((unsigned)(a) | 32) - 'a') < 26)
|
||||
#define isdigit(a) (0 ? isdigit(a) : ((unsigned)(a) - '0') < 10)
|
||||
#define isgraph(a) (0 ? isgraph(a) : ((unsigned)(a) - 0x21) < 0x5e)
|
||||
#define islower(a) (0 ? islower(a) : ((unsigned)(a) - 'a') < 26)
|
||||
#define isprint(a) (0 ? isprint(a) : ((unsigned)(a) - 0x20) < 0x5f)
|
||||
#define isupper(a) (0 ? isupper(a) : ((unsigned)(a) - 'A') < 26)
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
curl -#OL "https://gee.cs.oswego.edu/pub/misc/malloc.c"
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
ROOT=../../
|
||||
WASI_SDK="$ROOT/tools/wasi-sdk/bin"
|
||||
|
||||
shopt -s extglob
|
||||
"$WASI_SDK/clang-format" --style=Google -i !(malloc).@(c|h)
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "ctype.c"
|
||||
#include "math.c"
|
||||
#include "stdlib.c"
|
||||
#include "string.c"
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,832 +0,0 @@
|
||||
package libc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "embed"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
//go:embed libc.wasm
|
||||
var binary []byte
|
||||
|
||||
const (
|
||||
page = 64 * 1024
|
||||
size = 1024 * 1024 * 4
|
||||
ptr1 = 1024 * 1024
|
||||
ptr2 = ptr1 + size
|
||||
)
|
||||
|
||||
var (
|
||||
memory []byte
|
||||
module api.Module
|
||||
memset api.Function
|
||||
memcpy api.Function
|
||||
memchr api.Function
|
||||
memcmp api.Function
|
||||
strlen api.Function
|
||||
strchr api.Function
|
||||
strspn api.Function
|
||||
strrchr api.Function
|
||||
strcspn api.Function
|
||||
stack [8]uint64
|
||||
)
|
||||
|
||||
func call(fn api.Function, arg ...uint64) uint64 {
|
||||
copy(stack[:], arg)
|
||||
err := fn.CallWithStack(context.Background(), stack[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return stack[0]
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
ctx := context.Background()
|
||||
|
||||
runtime := wazero.NewRuntime(ctx)
|
||||
mod, err := runtime.Instantiate(ctx, binary)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
module = mod
|
||||
memset = mod.ExportedFunction("memset")
|
||||
memcpy = mod.ExportedFunction("memcpy")
|
||||
memchr = mod.ExportedFunction("memchr")
|
||||
memcmp = mod.ExportedFunction("memcmp")
|
||||
strlen = mod.ExportedFunction("strlen")
|
||||
strchr = mod.ExportedFunction("strchr")
|
||||
strspn = mod.ExportedFunction("strspn")
|
||||
strrchr = mod.ExportedFunction("strrchr")
|
||||
strcspn = mod.ExportedFunction("strcspn")
|
||||
memory, _ = mod.Memory().Read(0, mod.Memory().Size())
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func Benchmark_memset(b *testing.B) {
|
||||
clear(memory)
|
||||
|
||||
b.SetBytes(size)
|
||||
for b.Loop() {
|
||||
call(memset, ptr1, 3, size)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_memcpy(b *testing.B) {
|
||||
clear(memory)
|
||||
fill(memory[ptr2:ptr2+size], 5)
|
||||
|
||||
b.SetBytes(size)
|
||||
for b.Loop() {
|
||||
call(memcpy, ptr1, ptr2, size)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_strlen(b *testing.B) {
|
||||
clear(memory)
|
||||
fill(memory[ptr1:ptr1+size-1], 5)
|
||||
|
||||
b.SetBytes(size)
|
||||
for b.Loop() {
|
||||
call(strlen, ptr1)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_memchr(b *testing.B) {
|
||||
clear(memory)
|
||||
fill(memory[ptr1:ptr1+size/2], 7)
|
||||
fill(memory[ptr1+size/2:ptr1+size], 5)
|
||||
|
||||
b.SetBytes(size/2 + 1)
|
||||
for b.Loop() {
|
||||
call(memchr, ptr1, 5, size)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_strchr(b *testing.B) {
|
||||
clear(memory)
|
||||
fill(memory[ptr1:ptr1+size/2], 7)
|
||||
fill(memory[ptr1+size/2:ptr1+size-1], 5)
|
||||
|
||||
b.SetBytes(size/2 + 1)
|
||||
for b.Loop() {
|
||||
call(strchr, ptr1, 5)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_strrchr(b *testing.B) {
|
||||
clear(memory)
|
||||
fill(memory[ptr1:ptr1+size/2], 5)
|
||||
fill(memory[ptr1+size/2:ptr1+size-1], 7)
|
||||
|
||||
b.SetBytes(size/2 + 1)
|
||||
for b.Loop() {
|
||||
call(strrchr, ptr1, 5)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_memcmp(b *testing.B) {
|
||||
clear(memory)
|
||||
fill(memory[ptr1:ptr1+size], 7)
|
||||
fill(memory[ptr2:ptr2+size/2], 7)
|
||||
fill(memory[ptr2+size/2:ptr2+size], 5)
|
||||
|
||||
b.SetBytes(size/2 + 1)
|
||||
for b.Loop() {
|
||||
call(memcmp, ptr1, ptr2, size)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_strspn(b *testing.B) {
|
||||
clear(memory)
|
||||
fill(memory[ptr1:ptr1+size/2], 7)
|
||||
fill(memory[ptr1+size/2:ptr1+size-1], 5)
|
||||
memory[ptr2+0] = 3
|
||||
memory[ptr2+1] = 5
|
||||
memory[ptr2+2] = 7
|
||||
memory[ptr2+3] = 9
|
||||
|
||||
b.SetBytes(size)
|
||||
for b.Loop() {
|
||||
call(strspn, ptr1, ptr2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_strcspn(b *testing.B) {
|
||||
clear(memory)
|
||||
fill(memory[ptr1:ptr1+size/2], 7)
|
||||
fill(memory[ptr1+size/2:ptr1+size-1], 5)
|
||||
memory[ptr2+0] = 3
|
||||
memory[ptr2+1] = 9
|
||||
|
||||
b.SetBytes(size)
|
||||
for b.Loop() {
|
||||
call(strcspn, ptr1, ptr2)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_strlen(t *testing.T) {
|
||||
for length := range 64 {
|
||||
for alignment := range 24 {
|
||||
ptr := (page - 8) + alignment
|
||||
|
||||
clear(memory[:2*page])
|
||||
fill(memory[ptr:ptr+length], 5)
|
||||
|
||||
got := call(strlen, uint64(ptr))
|
||||
if uint32(got) != uint32(length) {
|
||||
t.Errorf("strlen(%d) = %d, want %d",
|
||||
ptr, uint32(got), uint32(length))
|
||||
}
|
||||
|
||||
memory[ptr-1] = 5
|
||||
got = call(strlen, uint64(ptr))
|
||||
if uint32(got) != uint32(length) {
|
||||
t.Errorf("strlen(%d) = %d, want %d",
|
||||
ptr, uint32(got), uint32(length))
|
||||
}
|
||||
}
|
||||
|
||||
clear(memory)
|
||||
ptr := len(memory) - length - 1
|
||||
fill(memory[ptr:ptr+length], 5)
|
||||
|
||||
got := call(strlen, uint64(ptr))
|
||||
if uint32(got) != uint32(length) {
|
||||
t.Errorf("strlen(%d) = %d, want %d",
|
||||
ptr, uint32(got), uint32(length))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_memchr(t *testing.T) {
|
||||
for length := range 64 {
|
||||
for pos := range length + 2 {
|
||||
for alignment := range 24 {
|
||||
ptr := (page - 8) + alignment
|
||||
want := 0
|
||||
if pos < length {
|
||||
want = ptr + pos
|
||||
}
|
||||
|
||||
clear(memory[:2*page])
|
||||
fill(memory[ptr:ptr+max(pos, length)], 5)
|
||||
memory[ptr+pos] = 7
|
||||
|
||||
if pos >= 0 {
|
||||
memory[ptr+pos+2] = 7
|
||||
}
|
||||
|
||||
got := call(memchr, uint64(ptr), 7, uint64(length))
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("memchr(%d, %d, %d) = %d, want %d",
|
||||
ptr, 7, uint64(length), uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear(memory)
|
||||
ptr := len(memory) - length
|
||||
fill(memory[ptr:ptr+length], 5)
|
||||
memory[len(memory)-1] = 7
|
||||
|
||||
var want int
|
||||
if length != 0 {
|
||||
want = len(memory) - 1
|
||||
got := call(memchr, uint64(ptr), 7, math.MaxUint32)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("memchr(%d, %d, %d) = %d, want %d",
|
||||
ptr, 7, uint32(math.MaxUint32), uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
|
||||
got := call(memchr, uint64(ptr), 7, uint64(length))
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("memchr(%d, %d, %d) = %d, want %d",
|
||||
ptr, 7, uint64(length), uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_strchr(t *testing.T) {
|
||||
for length := range 64 {
|
||||
for pos := range length + 2 {
|
||||
for alignment := range 24 {
|
||||
ptr := (page - 8) + alignment
|
||||
want := 0
|
||||
if pos < length {
|
||||
want = ptr + pos
|
||||
}
|
||||
|
||||
clear(memory[:2*page])
|
||||
fill(memory[ptr:ptr+max(pos, length)], 5)
|
||||
memory[ptr+pos] = 7
|
||||
memory[ptr+pos+1] = 7
|
||||
memory[ptr+length] = 0
|
||||
|
||||
got := call(strchr, uint64(ptr), 7)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strchr(%d, %d) = %d, want %d",
|
||||
ptr, 7, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear(memory)
|
||||
ptr := len(memory) - length
|
||||
fill(memory[ptr:ptr+length], 5)
|
||||
memory[len(memory)-1] = 7
|
||||
|
||||
want := len(memory) - 1
|
||||
if length == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
got := call(strchr, uint64(ptr), 7)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strchr(%d, %d) = %d, want %d",
|
||||
ptr, 7, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_strrchr(t *testing.T) {
|
||||
for length := range 64 {
|
||||
for pos := range length + 2 {
|
||||
for alignment := range 24 {
|
||||
ptr := (page - 8) + alignment
|
||||
want := 0
|
||||
if pos < length {
|
||||
want = ptr + pos
|
||||
} else if length > 0 {
|
||||
want = ptr
|
||||
}
|
||||
|
||||
clear(memory[:2*page])
|
||||
fill(memory[ptr:ptr+max(pos, length)], 5)
|
||||
memory[ptr] = 7
|
||||
memory[ptr+pos] = 7
|
||||
memory[ptr+length] = 0
|
||||
|
||||
got := call(strrchr, uint64(ptr), 7)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strrchr(%d, %d) = %d, want %d",
|
||||
ptr, 7, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr := len(memory) - length
|
||||
want := len(memory) - 2
|
||||
if length <= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
clear(memory)
|
||||
fill(memory[ptr:ptr+length], 5)
|
||||
memory[ptr] = 7
|
||||
memory[len(memory)-2] = 7
|
||||
memory[len(memory)-1] = 0
|
||||
|
||||
got := call(strrchr, uint64(ptr), 7)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strrchr(%d, %d) = %d, want %d",
|
||||
ptr, 7, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
compareTest1 = "" +
|
||||
"\x94\x63\x8f\x01\x74\x63\x8f\x01\x54\x63\x8f\x01\x34\x63\x8f\x01" +
|
||||
"\xb4\xf2\x93\x01\x94\xf2\x93\x01\x54\xf1\x93\x01\x34\xf1\x93\x01" +
|
||||
"\x14\xf1\x93\x01\x14\xf2\x93\x01\x34\xf2\x93\x01\x54\xf2\x93\x01" +
|
||||
"\x74\xf2\x93\x01\x74\xf1\x93\x01\xd4\xf2\x93\x01\x94\xf1\x93\x01" +
|
||||
"\xb4\xf1\x93\x01\xd4\xf1\x93\x01\xf4\xf1\x93\x01\xf4\xf2\x93\x01" +
|
||||
"\x14\xf4\x93\x01\xf4\xf3\x93\x01\xd4\xf3\x93\x01\xb4\xf3\x93\x01" +
|
||||
"\x94\xf3\x93\x01\x74\x80\x93\x01\x54\xf3\x93\x01\x34\xf3\x93\x01" +
|
||||
"\x7f\xf3\x93\x01\x00\x01"
|
||||
compareTest2 = "" +
|
||||
"\x94\x63\x8f\x01\x74\x63\x8f\x01\x54\x63\x8f\x01\x34\x63\x8f\x01" +
|
||||
"\xb4\xf2\x93\x01\x94\xf2\x93\x01\x54\xf1\x93\x01\x34\xf1\x93\x01" +
|
||||
"\x14\xf1\x93\x01\x14\xf2\x93\x01\x34\xf2\x93\x01\x54\xf2\x93\x01" +
|
||||
"\x74\xf2\x93\x01\x74\xf1\x93\x01\xd4\xf2\x93\x01\x94\xf1\x93\x01" +
|
||||
"\xb4\xf1\x93\x01\xd4\xf1\x93\x01\xf4\xf1\x93\x01\xf4\xf2\x93\x01" +
|
||||
"\xbc\x40\x96\x01\xf4\xf3\x93\x01\xd4\xf3\x93\x01\xb4\xf3\x93\x01" +
|
||||
"\x94\xf3\x93\x01\x74\x7f\x93\x01\x54\xf3\x93\x01\x34\xf3\x93\x01" +
|
||||
"\x80\xf3\x93\x01\x00\x02"
|
||||
)
|
||||
|
||||
func Test_memcmp(t *testing.T) {
|
||||
const s1 = compareTest1
|
||||
const s2 = compareTest2
|
||||
|
||||
ptr2 := len(memory) - len(s2)
|
||||
|
||||
clear(memory)
|
||||
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(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_strspn(t *testing.T) {
|
||||
for length := range 64 {
|
||||
for pos := range length + 2 {
|
||||
for alignment := range 24 {
|
||||
ptr := (page - 8) + alignment
|
||||
want := min(pos, length)
|
||||
|
||||
clear(memory[:2*page])
|
||||
fill(memory[ptr:ptr+max(pos, length)], 5)
|
||||
memory[ptr+pos] = 7
|
||||
memory[ptr+length] = 0
|
||||
memory[128] = 7 | 128
|
||||
memory[129] = 5
|
||||
|
||||
got := call(strspn, uint64(ptr), 129)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strspn(%d, %d) = %d, want %d",
|
||||
ptr, 129, uint32(got), uint32(want))
|
||||
}
|
||||
|
||||
got = call(strspn, uint64(ptr), 128)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strspn(%d, %d) = %d, want %d",
|
||||
ptr, 128, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr := len(memory) - length
|
||||
want := length - 1
|
||||
if length == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
clear(memory)
|
||||
fill(memory[ptr:ptr+length], 5)
|
||||
memory[len(memory)-1] = 7
|
||||
memory[128] = 7 | 128
|
||||
memory[129] = 5
|
||||
|
||||
got := call(strspn, uint64(ptr), 129)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strspn(%d, %d) = %d, want %d",
|
||||
ptr, 129, uint32(got), uint32(want))
|
||||
}
|
||||
|
||||
got = call(strspn, uint64(ptr), 128)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strspn(%d, %d) = %d, want %d",
|
||||
ptr, 128, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_strcspn(t *testing.T) {
|
||||
for length := range 64 {
|
||||
for pos := range length + 2 {
|
||||
for alignment := range 24 {
|
||||
ptr := (page - 8) + alignment
|
||||
want := min(pos, length)
|
||||
|
||||
clear(memory[:2*page])
|
||||
fill(memory[ptr:ptr+max(pos, length)], 5)
|
||||
memory[ptr+pos] = 7
|
||||
memory[ptr+length] = 0
|
||||
memory[128] = 5 | 128
|
||||
memory[129] = 7
|
||||
|
||||
got := call(strcspn, uint64(ptr), 129)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strcspn(%d, %d) = %d, want %d",
|
||||
ptr, 129, uint32(got), uint32(want))
|
||||
}
|
||||
|
||||
got = call(strcspn, uint64(ptr), 128)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strcspn(%d, %d) = %d, want %d",
|
||||
ptr, 128, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr := len(memory) - length
|
||||
want := length - 1
|
||||
if length == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
clear(memory)
|
||||
fill(memory[ptr:ptr+length], 5)
|
||||
memory[len(memory)-1] = 7
|
||||
memory[128] = 5 | 128
|
||||
memory[129] = 7
|
||||
|
||||
got := call(strcspn, uint64(ptr), 129)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strcspn(%d, %d) = %d, want %d",
|
||||
ptr, 129, uint32(got), uint32(want))
|
||||
}
|
||||
|
||||
got = call(strcspn, uint64(ptr), 128)
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strcspn(%d, %d) = %d, want %d",
|
||||
ptr, 128, uint32(got), uint32(want))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type searchTest struct {
|
||||
haystk string
|
||||
needle string
|
||||
out int
|
||||
}
|
||||
|
||||
var searchTests = []searchTest{
|
||||
{"", "", 0},
|
||||
{"", "a", -1},
|
||||
{"", "foo", -1},
|
||||
{"fo", "foo", -1},
|
||||
{"foo", "baz", -1},
|
||||
{"foo", "foo", 0},
|
||||
{"oofofoofooo", "f", 2},
|
||||
{"oofofoofooo", "foo", 4},
|
||||
{"barfoobarfoo", "foo", 3},
|
||||
{"foo", "", 0},
|
||||
{"foo", "o", 1},
|
||||
{"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},
|
||||
{"oxoxoxoxoxoxoxoxoxoxox☺", "☺", 22},
|
||||
{"xx0123456789012345678901234567890123456789012345678901234567890120123456789012345678901234567890123456xxx\xed\x9f\xc0", "\xed\x9f\xc0", 105},
|
||||
{"000000000000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000001", 5},
|
||||
}
|
||||
|
||||
func Fuzz_memchr(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, s string, c, i byte) {
|
||||
if len(s) > 128 || int(i) > len(s) {
|
||||
t.SkipNow()
|
||||
}
|
||||
copy(memory[ptr1:], s)
|
||||
|
||||
got := call(memchr, ptr1+uint64(i), uint64(c), uint64(len(s)-int(i)))
|
||||
want := strings.IndexByte(s[i:], c)
|
||||
if want >= 0 {
|
||||
want = ptr1 + int(i) + want
|
||||
} else {
|
||||
want = 0
|
||||
}
|
||||
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("memchr(%q, %q) = %d, want %d",
|
||||
s[i:], c, uint32(got), uint32(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Fuzz_strchr(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, s string, c, i byte) {
|
||||
if len(s) > 128 || int(i) > len(s) {
|
||||
t.SkipNow()
|
||||
}
|
||||
copy(memory[ptr1:], s)
|
||||
memory[ptr1+len(s)] = 0
|
||||
|
||||
got := call(strchr, ptr1+uint64(i), uint64(c))
|
||||
want := bytes.IndexByte(term1(memory[ptr1+uint64(i):]), c)
|
||||
if want >= 0 {
|
||||
want = ptr1 + int(i) + want
|
||||
} else {
|
||||
want = 0
|
||||
}
|
||||
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strchr(%q, %q) = %d, want %d",
|
||||
s[i:], c, uint32(got), uint32(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Fuzz_strrchr(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, s string, c, i byte) {
|
||||
if len(s) > 128 || int(i) > len(s) {
|
||||
t.SkipNow()
|
||||
}
|
||||
copy(memory[ptr1:], s)
|
||||
memory[ptr1+len(s)] = 0
|
||||
|
||||
got := call(strrchr, ptr1+uint64(i), uint64(c))
|
||||
want := bytes.LastIndexByte(term1(memory[ptr1+uint64(i):]), c)
|
||||
if want >= 0 {
|
||||
want = ptr1 + int(i) + want
|
||||
} else {
|
||||
want = 0
|
||||
}
|
||||
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strrchr(%q, %q) = %d, want %d",
|
||||
s[i:], c, uint32(got), uint32(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Fuzz_memcmp(f *testing.F) {
|
||||
const s1 = compareTest1
|
||||
const s2 = compareTest2
|
||||
|
||||
for i := range len(compareTest1) + 1 {
|
||||
f.Add(s1[i:], s2[i:])
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, s1, s2 string) {
|
||||
if len(s1) > 128 || len(s1) != len(s2) {
|
||||
t.SkipNow()
|
||||
}
|
||||
copy(memory[ptr1:], s1)
|
||||
copy(memory[ptr2:], s2)
|
||||
|
||||
got := call(memcmp, uint64(ptr1), uint64(ptr2), uint64(len(s1)))
|
||||
want := strings.Compare(s1, s2)
|
||||
|
||||
if sign(int32(got)) != want {
|
||||
t.Errorf("memcmp(%q, %q) = %d, want %d",
|
||||
s1, s2, uint32(got), uint32(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Fuzz_strspn(f *testing.F) {
|
||||
for _, t := range searchTests {
|
||||
f.Add(t.haystk, t.needle)
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, s, chars string) {
|
||||
if len(s) > 128 || len(chars) > 128 {
|
||||
t.SkipNow()
|
||||
}
|
||||
copy(memory[ptr1:], s)
|
||||
copy(memory[ptr2:], chars)
|
||||
memory[ptr1+len(s)] = 0
|
||||
memory[ptr2+len(chars)] = 0
|
||||
|
||||
got := call(strspn, uint64(ptr1), uint64(ptr2))
|
||||
|
||||
s = term(s)
|
||||
chars = term(chars)
|
||||
want := indexNotByte(s, chars)
|
||||
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strspn(%v, %v) = %d, want %d",
|
||||
[]byte(memory[ptr1:ptr1+len(s)]),
|
||||
[]byte(memory[ptr2:ptr2+len(chars)]),
|
||||
uint32(got), uint32(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Fuzz_strcspn(f *testing.F) {
|
||||
for _, t := range searchTests {
|
||||
f.Add(t.haystk, t.needle)
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, s, chars string) {
|
||||
if len(s) > 128 || len(chars) > 128 {
|
||||
t.SkipNow()
|
||||
}
|
||||
copy(memory[ptr1:], s)
|
||||
copy(memory[ptr2:], chars)
|
||||
memory[ptr1+len(s)] = 0
|
||||
memory[ptr2+len(chars)] = 0
|
||||
|
||||
got := call(strcspn, uint64(ptr1), uint64(ptr2))
|
||||
|
||||
s = term(s)
|
||||
chars = term(chars)
|
||||
want := indexAnyByte(s, chars)
|
||||
|
||||
if uint32(got) != uint32(want) {
|
||||
t.Errorf("strcspn(%q, %q) = %d, want %d",
|
||||
s, chars, uint32(got), uint32(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func sign(x int32) int {
|
||||
switch {
|
||||
case x > 0:
|
||||
return +1
|
||||
case x < 0:
|
||||
return -1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func fill(s []byte, v byte) {
|
||||
for i := range s {
|
||||
s[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
func term[T interface{ []byte | string }](s T) T {
|
||||
for i, c := range []byte(s) {
|
||||
if c == 0 {
|
||||
return s[:i]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func term1[T interface{ []byte | string }](s T) T {
|
||||
for i, c := range []byte(s) {
|
||||
if c == 0 {
|
||||
return s[:i+1]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func indexNotByte(s, chars string) int {
|
||||
for i, c := range []byte(s) {
|
||||
if strings.IndexByte(chars, c) < 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func indexAnyByte(s, chars string) int {
|
||||
for i, c := range []byte(s) {
|
||||
if strings.IndexByte(chars, c) >= 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(s)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
// A simple bump allocator that never frees memory.
|
||||
// Takes over the initial heap, then grows it as needed.
|
||||
// Assumes that new memory is zero-initialized,
|
||||
// and that the heap base is 16 byte aligned.
|
||||
// It allocates in 16 byte chunks and keeps no size metadata.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PAGESIZE 65536
|
||||
|
||||
extern char __heap_base[];
|
||||
extern char __heap_end[];
|
||||
|
||||
static void* __arena_beg = __heap_base;
|
||||
static void* __arena_end = __heap_end;
|
||||
|
||||
__attribute__((always_inline)) void free(void*) {}
|
||||
|
||||
void* malloc(size_t size) {
|
||||
if (size == 0) return NULL;
|
||||
size = __builtin_align_up(size, 16);
|
||||
|
||||
size_t avail = __arena_end - __arena_beg;
|
||||
if (size > avail) {
|
||||
size_t npages = (size - avail + PAGESIZE - 1) / PAGESIZE;
|
||||
size_t old = __builtin_wasm_memory_grow(0, npages);
|
||||
if (old == SIZE_MAX) return NULL;
|
||||
__arena_end += npages * PAGESIZE;
|
||||
}
|
||||
|
||||
void* res = __arena_beg;
|
||||
__arena_beg += size;
|
||||
return res;
|
||||
}
|
||||
|
||||
void* calloc(size_t nelem, size_t elsize) { return malloc(nelem * elsize); }
|
||||
|
||||
void* aligned_alloc(size_t align, size_t size) {
|
||||
// Ensure non-zero power-of-two.
|
||||
if (align <= 0 || (align & (align - 1))) return NULL;
|
||||
align = (align - (size_t)__arena_beg) & (align - 1);
|
||||
return malloc(size + align) + align;
|
||||
}
|
||||
|
||||
void* realloc(void* ptr, size_t size) {
|
||||
if (ptr == NULL) return malloc(size);
|
||||
// No need to move the first chunk.
|
||||
if (size <= 16) return ptr;
|
||||
|
||||
size_t copy = __arena_beg - ptr;
|
||||
void* res = malloc(size);
|
||||
if (copy > size) copy = size;
|
||||
__builtin_memcpy(res, ptr, copy);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#define PAGESIZE 65536
|
||||
|
||||
void* sbrk(intptr_t increment) {
|
||||
if (increment == 0) return (void*)(__builtin_wasm_memory_size(0) * PAGESIZE);
|
||||
if (increment < 0 || increment % PAGESIZE != 0) abort();
|
||||
|
||||
size_t old = __builtin_wasm_memory_grow(0, (size_t)increment / PAGESIZE);
|
||||
if (old == SIZE_MAX) return (void*)old;
|
||||
return (void*)(old * PAGESIZE);
|
||||
}
|
||||
|
||||
#define LACKS_ERRNO_H
|
||||
#define LACKS_FCNTL_H
|
||||
#define LACKS_SCHED_H
|
||||
#define LACKS_STRINGS_H
|
||||
#define LACKS_SYS_MMAN_H
|
||||
#define LACKS_SYS_PARAM_H
|
||||
#define LACKS_SYS_TYPES_H
|
||||
#define LACKS_TIME_H
|
||||
#define LACKS_UNISTD_H
|
||||
|
||||
#define HAVE_MMAP 0
|
||||
#define MALLOC_ALIGNMENT 16
|
||||
#define MALLOC_FAILURE_ACTION
|
||||
#define MORECORE_CANNOT_TRIM 1
|
||||
#define NO_MALLINFO 1
|
||||
#define NO_MALLOC_STATS 1
|
||||
#define USE_BUILTIN_FFS 1
|
||||
#define USE_LOCKS 0
|
||||
|
||||
#define ffs(i) (__builtin_ffs(i))
|
||||
|
||||
#define ENOMEM 7 // SQLITE_NOMEM
|
||||
#define EINVAL 21 // SQLITE_MISUSE
|
||||
|
||||
#pragma clang diagnostic ignored "-Weverything"
|
||||
|
||||
#include "malloc.c"
|
||||
|
||||
extern char __heap_base[];
|
||||
extern char __heap_end[];
|
||||
|
||||
// Initialize dlmalloc to be able to use the memory between
|
||||
// __heap_base and __heap_end.
|
||||
static void try_init_allocator(void) {
|
||||
if (is_initialized(gm)) __builtin_trap();
|
||||
ensure_initialization();
|
||||
|
||||
size_t heap_size = __heap_end - __heap_base;
|
||||
if (heap_size <= MIN_CHUNK_SIZE + TOP_FOOT_SIZE + MALLOC_ALIGNMENT) return;
|
||||
|
||||
gm->least_addr = __heap_base;
|
||||
gm->seg.base = __heap_base;
|
||||
gm->seg.size = heap_size;
|
||||
gm->footprint = heap_size;
|
||||
gm->max_footprint = heap_size;
|
||||
gm->magic = mparams.magic;
|
||||
gm->release_checks = MAX_RELEASE_CHECK_RATE;
|
||||
|
||||
init_bins(gm);
|
||||
init_top(gm, (mchunkptr)__heap_base, heap_size - TOP_FOOT_SIZE);
|
||||
}
|
||||
|
||||
__attribute__((alias("memalign"))) void* aligned_alloc(size_t align,
|
||||
size_t size);
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <math.h>
|
||||
|
||||
__attribute__((always_inline)) double(ceil)(double x) {
|
||||
return __builtin_ceil(x);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) double(floor)(double x) {
|
||||
return __builtin_floor(x);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) double(sqrt)(double x) {
|
||||
return __builtin_sqrt(x);
|
||||
}
|
||||
|
||||
__attribute__((always_inline)) double(trunc)(double x) {
|
||||
return __builtin_trunc(x);
|
||||
}
|
||||
+34
-32
@@ -1,36 +1,38 @@
|
||||
#include_next <math.h> // the system math.h
|
||||
#pragma once
|
||||
|
||||
#ifndef _WASM_SIMD128_MATH_H
|
||||
#define _WASM_SIMD128_MATH_H
|
||||
double acos(double);
|
||||
double acosh(double);
|
||||
double asin(double);
|
||||
double asinh(double);
|
||||
double atan(double);
|
||||
double atan2(double, double);
|
||||
double atanh(double);
|
||||
double ceil(double);
|
||||
double cos(double);
|
||||
double cosh(double);
|
||||
double exp(double);
|
||||
double fabs(double);
|
||||
double floor(double);
|
||||
double fmod(double, double);
|
||||
double log(double);
|
||||
double log10(double);
|
||||
double log2(double);
|
||||
double pow(double, double);
|
||||
double sin(double);
|
||||
double sinh(double);
|
||||
double tan(double);
|
||||
double tanh(double);
|
||||
double trunc(double);
|
||||
double sqrt(double);
|
||||
|
||||
#include <wasm_simd128.h>
|
||||
#define ceil(x) (__builtin_ceil(x))
|
||||
#define floor(x) (__builtin_floor(x))
|
||||
#define sqrt(x) (__builtin_sqrt(x))
|
||||
#define trunc(x) (__builtin_trunc(x))
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define INFINITY __builtin_inf()
|
||||
|
||||
#ifdef __wasm_relaxed_simd__
|
||||
|
||||
// This header assumes "relaxed fused multiply-add"
|
||||
// is both faster and more precise.
|
||||
|
||||
#define FP_FAST_FMA 1
|
||||
|
||||
__attribute__((weak))
|
||||
double fma(double x, double y, double z) {
|
||||
// If we get a software implementation from the host,
|
||||
// this is enough to short circuit it on the 2nd lane.
|
||||
const v128_t wx = wasm_f64x2_replace_lane(b, 0, x);
|
||||
const v128_t wy = wasm_f64x2_splat(y);
|
||||
const v128_t wz = wasm_f64x2_splat(z);
|
||||
const v128_t wr = wasm_f64x2_relaxed_madd(wx, wy, wz);
|
||||
return wasm_f64x2_extract_lane(wr, 0);
|
||||
}
|
||||
|
||||
#endif // __wasm_relaxed_simd__
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _WASM_SIMD128_MATH_H
|
||||
#define isfinite(x) (__builtin_isfinite(x))
|
||||
#define isinf(x) (__builtin_isinf(x))
|
||||
#define isnan(x) (__builtin_isnan(x))
|
||||
#define isnormal(x) (__builtin_isnormal(x))
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
void abort(void) { __builtin_trap(); }
|
||||
|
||||
// Shellsort with Gonnet & Baeza-Yates gap sequence.
|
||||
// Simple, no recursion, doesn't use the C stack.
|
||||
// Clang auto-vectorizes the inner loop.
|
||||
|
||||
void qsort(void* base, size_t nel, size_t width,
|
||||
int (*comp)(const void*, const void*)) {
|
||||
// If nel is zero, we're required to do nothing.
|
||||
// If it's one, the array is already sorted.
|
||||
size_t wnel = width * nel;
|
||||
size_t gap = nel;
|
||||
while (gap > 1) {
|
||||
// Use 64-bit unsigned arithmetic to avoid intermediate overflow.
|
||||
// Absent overflow, gap will be strictly less than its previous value.
|
||||
// Once it is one or zero, set it to one: do a final pass, and stop.
|
||||
gap = (5ull * gap - 1) / 11;
|
||||
if (gap == 0) gap = 1;
|
||||
|
||||
// It'd be undefined behavior for wnel to overflow a size_t;
|
||||
// or if width is zero: the base pointer would be invalid.
|
||||
// Since gap is stricly less than nel, we can assume
|
||||
// wgap is strictly less than wnel.
|
||||
size_t wgap = width * gap;
|
||||
__builtin_assume(wgap < wnel);
|
||||
for (size_t i = wgap; i < wnel; i += width) {
|
||||
// Even without overflow flags, the overflow builtin helps the compiler.
|
||||
for (size_t j = i; !__builtin_sub_overflow(j, wgap, &j);) {
|
||||
char* a = j + (char*)base;
|
||||
char* b = a + wgap;
|
||||
if (comp(a, b) <= 0) break;
|
||||
|
||||
// This well known loop is automatically vectorized.
|
||||
size_t s = width;
|
||||
do {
|
||||
char tmp = *a;
|
||||
*a++ = *b;
|
||||
*b++ = tmp;
|
||||
} while (--s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-53
@@ -1,57 +1,13 @@
|
||||
#include_next <stdlib.h> // the system stdlib.h
|
||||
#pragma once
|
||||
|
||||
#ifndef _WASM_SIMD128_STDLIB_H
|
||||
#define _WASM_SIMD128_STDLIB_H
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
__attribute__((noreturn)) void abort(void);
|
||||
|
||||
// Shellsort with Gonnet & Baeza-Yates gap sequence.
|
||||
// Simple, no recursion, doesn't use the C stack.
|
||||
// Clang auto-vectorizes the inner loop.
|
||||
void free(void*);
|
||||
__attribute__((malloc)) void* malloc(size_t);
|
||||
__attribute__((malloc)) void* calloc(size_t, size_t);
|
||||
__attribute__((malloc)) void* aligned_alloc(size_t, size_t);
|
||||
void* realloc(void*, size_t);
|
||||
|
||||
__attribute__((weak))
|
||||
void qsort(void *base, size_t nel, size_t width,
|
||||
int (*comp)(const void *, const void *)) {
|
||||
// If nel is zero, we're required to do nothing.
|
||||
// If it's one, the array is already sorted.
|
||||
size_t wnel = width * nel;
|
||||
size_t gap = nel;
|
||||
while (gap > 1) {
|
||||
// Use 64-bit unsigned arithmetic to avoid intermediate overflow.
|
||||
// Absent overflow, gap will be strictly less than its previous value.
|
||||
// Once it is one or zero, set it to one: do a final pass, and stop.
|
||||
gap = (5ull * gap - 1) / 11;
|
||||
if (gap == 0) gap = 1;
|
||||
|
||||
// It'd be undefined behavior for wnel to overflow a size_t;
|
||||
// or if width is zero: the base pointer would be invalid.
|
||||
// Since gap is stricly less than nel, we can assume
|
||||
// wgap is strictly less than wnel.
|
||||
size_t wgap = width * gap;
|
||||
__builtin_assume(wgap < wnel);
|
||||
for (size_t i = wgap; i < wnel; i += width) {
|
||||
// Even without overflow flags, the overflow builtin helps the compiler.
|
||||
for (size_t j = i; !__builtin_sub_overflow(j, wgap, &j);) {
|
||||
char *a = j + (char *)base;
|
||||
char *b = a + wgap;
|
||||
if (comp(a, b) <= 0) break;
|
||||
|
||||
// This well known loop is automatically vectorized.
|
||||
size_t s = width;
|
||||
do {
|
||||
char tmp = *a;
|
||||
*a++ = *b;
|
||||
*b++ = tmp;
|
||||
} while (--s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _WASM_SIMD128_STDLIB_H
|
||||
void qsort(void*, size_t, size_t, int (*)(const void*, const void*));
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
#ifdef __wasm_simd128__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wasm_simd128.h>
|
||||
|
||||
int memcmp(const void* vl, const void* vr, size_t n) {
|
||||
// Scalar algorithm.
|
||||
if (n < sizeof(v128_t)) {
|
||||
const unsigned char* u1 = (unsigned char*)vl;
|
||||
const unsigned char* u2 = (unsigned char*)vr;
|
||||
while (n--) {
|
||||
if (*u1 != *u2) return *u1 - *u2;
|
||||
u1++;
|
||||
u2++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// memcmp is allowed to read up to n bytes from each object.
|
||||
// Find the first different character in the objects.
|
||||
// Unaligned loads handle the case where the objects
|
||||
// have mismatching alignments.
|
||||
const v128_t* v1 = (v128_t*)vl;
|
||||
const v128_t* v2 = (v128_t*)vr;
|
||||
while (n) {
|
||||
const v128_t cmp = wasm_i8x16_eq(wasm_v128_load(v1), wasm_v128_load(v2));
|
||||
// Bitmask is slow on AArch64, all_true is much faster.
|
||||
if (!wasm_i8x16_all_true(cmp)) {
|
||||
// Find the offset of the first zero bit (little-endian).
|
||||
size_t ctz = __builtin_ctz(~wasm_i8x16_bitmask(cmp));
|
||||
const unsigned char* u1 = (unsigned char*)v1 + ctz;
|
||||
const unsigned char* u2 = (unsigned char*)v2 + ctz;
|
||||
// This may help the compiler if the function is inlined.
|
||||
__builtin_assume(*u1 - *u2 != 0);
|
||||
return *u1 - *u2;
|
||||
}
|
||||
// This makes n a multiple of sizeof(v128_t)
|
||||
// for every iteration except the first.
|
||||
size_t align = (n - 1) % sizeof(v128_t) + 1;
|
||||
v1 = (v128_t*)((char*)v1 + align);
|
||||
v2 = (v128_t*)((char*)v2 + align);
|
||||
n -= align;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* memchr(const void* s, int c, size_t n) {
|
||||
// When n is zero, a function that locates a character finds no occurrence.
|
||||
// Otherwise, decrement n to ensure sub_overflow overflows
|
||||
// when n would go equal-to-or-below zero.
|
||||
if (!n--) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// memchr must behave as if it reads characters sequentially
|
||||
// and stops as soon as a match is found.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
v128_t vc = wasm_i8x16_splat(c);
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *__builtin_launder((v128_t*)addr);
|
||||
v128_t cmp = wasm_i8x16_eq(v, vc);
|
||||
// Bitmask is slow on AArch64, any_true is much faster.
|
||||
if (wasm_v128_any_true(cmp)) {
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
// That's a match, unless it is beyond the end of the object.
|
||||
// Recall that we decremented n, so less-than-or-equal-to is correct.
|
||||
size_t ctz = __builtin_ctz(mask);
|
||||
return ctz - align <= n ? (char*)s + (addr - (uintptr_t)s + ctz) : NULL;
|
||||
}
|
||||
}
|
||||
// Decrement n; if it overflows we're done.
|
||||
if (__builtin_sub_overflow(n, sizeof(v128_t) - align, &n)) {
|
||||
return NULL;
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
void* memrchr(const void* s, int c, size_t n) {
|
||||
// memrchr is allowed to read up to n bytes from the object.
|
||||
// Search backward for the last matching character.
|
||||
const v128_t* v = (v128_t*)((char*)s + n);
|
||||
const v128_t vc = wasm_i8x16_splat(c);
|
||||
for (; n >= sizeof(v128_t); n -= sizeof(v128_t)) {
|
||||
const v128_t cmp = wasm_i8x16_eq(wasm_v128_load(--v), vc);
|
||||
// Bitmask is slow on AArch64, any_true is much faster.
|
||||
if (wasm_v128_any_true(cmp)) {
|
||||
// Find the offset of the last one bit (little-endian).
|
||||
// The leading 16 bits of the bitmask are always zero,
|
||||
// and to be ignored.
|
||||
size_t clz = __builtin_clz(wasm_i8x16_bitmask(cmp)) - 16;
|
||||
return (char*)(v + 1) - (clz + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Scalar algorithm.
|
||||
const char* a = (char*)v;
|
||||
while (n--) {
|
||||
if (*(--a) == (char)c) return (char*)a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t strlen(const char* s) {
|
||||
// strlen must stop as soon as it finds the terminator.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *__builtin_launder((v128_t*)addr);
|
||||
// Bitmask is slow on AArch64, all_true is much faster.
|
||||
if (!wasm_i8x16_all_true(v)) {
|
||||
const v128_t cmp = wasm_i8x16_eq(v, (v128_t){});
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return addr - (uintptr_t)s + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
char* strchr(const char* s, int c) {
|
||||
char* r = strchrnul(s, c);
|
||||
return *r == (char)c ? r : NULL;
|
||||
}
|
||||
|
||||
char* strchrnul(const char* s, int c) {
|
||||
// strchrnul must stop as soon as a match is found.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
v128_t vc = wasm_i8x16_splat(c);
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *__builtin_launder((v128_t*)addr);
|
||||
const v128_t cmp = wasm_i8x16_eq(v, (v128_t){}) | wasm_i8x16_eq(v, vc);
|
||||
// Bitmask is slow on AArch64, any_true is much faster.
|
||||
if (wasm_v128_any_true(cmp)) {
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return (char*)s + (addr - (uintptr_t)s + __builtin_ctz(mask));
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
char* strrchr(const char* s, int c) {
|
||||
return (char*)memrchr(s, c, strlen(s) + 1);
|
||||
}
|
||||
|
||||
int strcmp(const char* l, const char* r) {
|
||||
for (; *l == *r && *l; l++, r++);
|
||||
return *(unsigned char*)l - *(unsigned char*)r;
|
||||
}
|
||||
|
||||
int strncmp(const char* _l, const char* _r, size_t n) {
|
||||
const unsigned char *l = (void*)_l, *r = (void*)_r;
|
||||
if (!n--) return 0;
|
||||
for (; *l && *r && n && *l == *r; l++, r++, n--);
|
||||
return *l - *r;
|
||||
}
|
||||
|
||||
// SIMDized check which bytes are in a set (Geoff Langdale)
|
||||
// http://0x80.pl/notesen/2018-10-18-simd-byte-lookup.html
|
||||
|
||||
// This is the same algorithm as truffle from Hyperscan:
|
||||
// https://github.com/intel/hyperscan/blob/v5.4.2/src/nfa/truffle.c#L64-L81
|
||||
// https://github.com/intel/hyperscan/blob/v5.4.2/src/nfa/trufflecompile.cpp
|
||||
|
||||
typedef struct {
|
||||
__u8x16 lo;
|
||||
__u8x16 hi;
|
||||
} __wasm_v128_bitmap256_t;
|
||||
|
||||
__attribute__((always_inline)) static void __wasm_v128_setbit(
|
||||
__wasm_v128_bitmap256_t* bitmap, uint8_t i) {
|
||||
uint8_t hi_nibble = i >> 4;
|
||||
uint8_t lo_nibble = i & 0xf;
|
||||
bitmap->lo[lo_nibble] |= (uint8_t)(1u << (hi_nibble - 0));
|
||||
bitmap->hi[lo_nibble] |= (uint8_t)(1u << (hi_nibble - 8));
|
||||
}
|
||||
|
||||
#ifndef __wasm_relaxed_simd__
|
||||
#define wasm_i8x16_relaxed_swizzle wasm_i8x16_swizzle
|
||||
#endif
|
||||
|
||||
__attribute__((always_inline)) static v128_t __wasm_v128_chkbits(
|
||||
__wasm_v128_bitmap256_t bitmap, v128_t v) {
|
||||
v128_t hi_nibbles = wasm_u8x16_shr(v, 4);
|
||||
v128_t bitmask_lookup = wasm_u64x2_const_splat(0x8040201008040201);
|
||||
v128_t bitmask = wasm_i8x16_relaxed_swizzle(bitmask_lookup, hi_nibbles);
|
||||
|
||||
v128_t indices_0_7 = v & wasm_u8x16_const_splat(0x8f);
|
||||
v128_t indices_8_15 = indices_0_7 ^ wasm_u8x16_const_splat(0x80);
|
||||
|
||||
v128_t row_0_7 = wasm_i8x16_swizzle((v128_t)bitmap.lo, indices_0_7);
|
||||
v128_t row_8_15 = wasm_i8x16_swizzle((v128_t)bitmap.hi, indices_8_15);
|
||||
|
||||
v128_t bitsets = row_0_7 | row_8_15;
|
||||
return bitsets & bitmask;
|
||||
}
|
||||
|
||||
#undef wasm_i8x16_relaxed_swizzle
|
||||
|
||||
size_t strspn(const char* s, const char* c) {
|
||||
// strspn must stop as soon as it finds the terminator.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
|
||||
if (!c[0]) return 0;
|
||||
if (!c[1]) {
|
||||
v128_t vc = wasm_i8x16_splat(*c);
|
||||
for (;;) {
|
||||
v128_t v = *__builtin_launder((v128_t*)addr);
|
||||
v128_t cmp = wasm_i8x16_eq(v, vc);
|
||||
// Bitmask is slow on AArch64, all_true is much faster.
|
||||
if (!wasm_i8x16_all_true(cmp)) {
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = (uint16_t)~wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return addr - (uintptr_t)s + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
__wasm_v128_bitmap256_t bitmap = {};
|
||||
|
||||
for (; *c; c++) {
|
||||
// Terminator IS NOT on the bitmap.
|
||||
__wasm_v128_setbit(&bitmap, (uint8_t)*c);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *__builtin_launder((v128_t*)addr);
|
||||
v128_t found = __wasm_v128_chkbits(bitmap, v);
|
||||
// Bitmask is slow on AArch64, all_true is much faster.
|
||||
if (!wasm_i8x16_all_true(found)) {
|
||||
v128_t cmp = wasm_i8x16_eq(found, (v128_t){});
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return addr - (uintptr_t)s + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
size_t strcspn(const char* s, const char* c) {
|
||||
if (!c[0] || !c[1]) return strchrnul(s, *c) - s;
|
||||
|
||||
// strcspn must stop as soon as it finds the terminator.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
|
||||
__wasm_v128_bitmap256_t bitmap = {};
|
||||
|
||||
do {
|
||||
// Terminator IS on the bitmap.
|
||||
__wasm_v128_setbit(&bitmap, (uint8_t)*c);
|
||||
} while (*c++);
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *__builtin_launder((v128_t*)addr);
|
||||
v128_t found = __wasm_v128_chkbits(bitmap, v);
|
||||
// Bitmask is slow on AArch64, any_true is much faster.
|
||||
if (wasm_v128_any_true(found)) {
|
||||
v128_t cmp = wasm_i8x16_eq(found, (v128_t){});
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = (uint16_t)~wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return addr - (uintptr_t)s + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
size_t strlen(const char* s) {
|
||||
const char* a = s;
|
||||
for (; *s; s++);
|
||||
return s - a;
|
||||
}
|
||||
|
||||
#endif
|
||||
+16
-477
@@ -1,481 +1,20 @@
|
||||
#include_next <string.h> // the system string.h
|
||||
#pragma once
|
||||
|
||||
#ifndef _WASM_SIMD128_STRING_H
|
||||
#define _WASM_SIMD128_STRING_H
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <wasm_simd128.h>
|
||||
void* memset(void*, int, size_t);
|
||||
void* memcpy(void* restrict, const void* restrict, size_t);
|
||||
void* memmove(void*, const void*, size_t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void* memchr(const void*, int, size_t);
|
||||
void* memrchr(const void*, int, size_t);
|
||||
int memcmp(const void*, const void*, size_t);
|
||||
|
||||
#ifdef __wasm_bulk_memory__
|
||||
|
||||
// Use the builtins if compiled with bulk memory operations.
|
||||
// Clang will intrinsify using SIMD for small, constant N.
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
void *memset(void *dest, int c, size_t n) {
|
||||
return __builtin_memset(dest, c, n);
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
void *memcpy(void *__restrict dest, const void *__restrict src, size_t n) {
|
||||
return __builtin_memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
void *memmove(void *dest, const void *src, size_t n) {
|
||||
return __builtin_memmove(dest, src, n);
|
||||
}
|
||||
|
||||
#endif // __wasm_bulk_memory__
|
||||
|
||||
#ifdef __wasm_simd128__
|
||||
|
||||
__attribute__((weak))
|
||||
int memcmp(const void *vl, const void *vr, size_t n) {
|
||||
// Scalar algorithm.
|
||||
if (n < sizeof(v128_t)) {
|
||||
const unsigned char *u1 = (unsigned char *)vl;
|
||||
const unsigned char *u2 = (unsigned char *)vr;
|
||||
while (n--) {
|
||||
if (*u1 != *u2) return *u1 - *u2;
|
||||
u1++;
|
||||
u2++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// memcmp is allowed to read up to n bytes from each object.
|
||||
// Find the first different character in the objects.
|
||||
// Unaligned loads handle the case where the objects
|
||||
// have mismatching alignments.
|
||||
const v128_t *v1 = (v128_t *)vl;
|
||||
const v128_t *v2 = (v128_t *)vr;
|
||||
while (n) {
|
||||
const v128_t cmp = wasm_i8x16_eq(wasm_v128_load(v1), wasm_v128_load(v2));
|
||||
// Bitmask is slow on AArch64, all_true is much faster.
|
||||
if (!wasm_i8x16_all_true(cmp)) {
|
||||
// Find the offset of the first zero bit (little-endian).
|
||||
size_t ctz = __builtin_ctz(~wasm_i8x16_bitmask(cmp));
|
||||
const unsigned char *u1 = (unsigned char *)v1 + ctz;
|
||||
const unsigned char *u2 = (unsigned char *)v2 + ctz;
|
||||
// This may help the compiler if the function is inlined.
|
||||
__builtin_assume(*u1 - *u2 != 0);
|
||||
return *u1 - *u2;
|
||||
}
|
||||
// This makes n a multiple of sizeof(v128_t)
|
||||
// for every iteration except the first.
|
||||
size_t align = (n - 1) % sizeof(v128_t) + 1;
|
||||
v1 = (v128_t *)((char *)v1 + align);
|
||||
v2 = (v128_t *)((char *)v2 + align);
|
||||
n -= align;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void *memchr(const void *s, int c, size_t n) {
|
||||
// When n is zero, a function that locates a character finds no occurrence.
|
||||
// Otherwise, decrement n to ensure sub_overflow overflows
|
||||
// when n would go equal-to-or-below zero.
|
||||
if (!n--) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// memchr must behave as if it reads characters sequentially
|
||||
// and stops as soon as a match is found.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
v128_t vc = wasm_i8x16_splat(c);
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *(v128_t *)addr;
|
||||
v128_t cmp = wasm_i8x16_eq(v, vc);
|
||||
// Bitmask is slow on AArch64, any_true is much faster.
|
||||
if (wasm_v128_any_true(cmp)) {
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
// That's a match, unless it is beyond the end of the object.
|
||||
// Recall that we decremented n, so less-than-or-equal-to is correct.
|
||||
size_t ctz = __builtin_ctz(mask);
|
||||
return ctz - align <= n ? (char *)s + (addr - (uintptr_t)s + ctz) : NULL;
|
||||
}
|
||||
}
|
||||
// Decrement n; if it overflows we're done.
|
||||
if (__builtin_sub_overflow(n, sizeof(v128_t) - align, &n)) {
|
||||
return NULL;
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
void *memrchr(const void *s, int c, size_t n) {
|
||||
// memrchr is allowed to read up to n bytes from the object.
|
||||
// Search backward for the last matching character.
|
||||
const v128_t *v = (v128_t *)((char *)s + n);
|
||||
const v128_t vc = wasm_i8x16_splat(c);
|
||||
for (; n >= sizeof(v128_t); n -= sizeof(v128_t)) {
|
||||
const v128_t cmp = wasm_i8x16_eq(wasm_v128_load(--v), vc);
|
||||
// Bitmask is slow on AArch64, any_true is much faster.
|
||||
if (wasm_v128_any_true(cmp)) {
|
||||
// Find the offset of the last one bit (little-endian).
|
||||
// The leading 16 bits of the bitmask are always zero,
|
||||
// and to be ignored.
|
||||
size_t clz = __builtin_clz(wasm_i8x16_bitmask(cmp)) - 16;
|
||||
return (char *)(v + 1) - (clz + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Scalar algorithm.
|
||||
const char *a = (char *)v;
|
||||
while (n--) {
|
||||
if (*(--a) == (char)c) return (char *)a;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
size_t strlen(const char *s) {
|
||||
// strlen must stop as soon as it finds the terminator.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *(v128_t *)addr;
|
||||
// Bitmask is slow on AArch64, all_true is much faster.
|
||||
if (!wasm_i8x16_all_true(v)) {
|
||||
const v128_t cmp = wasm_i8x16_eq(v, (v128_t){});
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return addr - (uintptr_t)s + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
static char *__strchrnul(const char *s, int c) {
|
||||
// strchrnul must stop as soon as a match is found.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
v128_t vc = wasm_i8x16_splat(c);
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *(v128_t *)addr;
|
||||
const v128_t cmp = wasm_i8x16_eq(v, (v128_t){}) | wasm_i8x16_eq(v, vc);
|
||||
// Bitmask is slow on AArch64, any_true is much faster.
|
||||
if (wasm_v128_any_true(cmp)) {
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return (char *)s + (addr - (uintptr_t)s + __builtin_ctz(mask));
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
char *strchrnul(const char *s, int c) {
|
||||
// For finding the terminator, strlen is faster.
|
||||
if (__builtin_constant_p(c) && (char)c == 0) {
|
||||
return (char *)s + strlen(s);
|
||||
}
|
||||
return __strchrnul(s, c);
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
char *strchr(const char *s, int c) {
|
||||
// For finding the terminator, strlen is faster.
|
||||
if (__builtin_constant_p(c) && (char)c == 0) {
|
||||
return (char *)s + strlen(s);
|
||||
}
|
||||
char *r = __strchrnul(s, c);
|
||||
return *r == (char)c ? r : NULL;
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
char *strrchr(const char *s, int c) {
|
||||
// For finding the terminator, strlen is faster.
|
||||
if (__builtin_constant_p(c) && (char)c == 0) {
|
||||
return (char *)s + strlen(s);
|
||||
}
|
||||
// This could also be implemented in a single pass using strchr,
|
||||
// advancing to the next match until no more matches are found.
|
||||
// That would be suboptimal with lots of consecutive matches.
|
||||
return (char *)memrchr(s, c, strlen(s) + 1);
|
||||
}
|
||||
|
||||
// SIMDized check which bytes are in a set (Geoff Langdale)
|
||||
// http://0x80.pl/notesen/2018-10-18-simd-byte-lookup.html
|
||||
|
||||
// This is the same algorithm as truffle from Hyperscan:
|
||||
// https://github.com/intel/hyperscan/blob/v5.4.2/src/nfa/truffle.c#L64-L81
|
||||
// https://github.com/intel/hyperscan/blob/v5.4.2/src/nfa/trufflecompile.cpp
|
||||
|
||||
typedef struct {
|
||||
__u8x16 lo;
|
||||
__u8x16 hi;
|
||||
} __wasm_v128_bitmap256_t;
|
||||
|
||||
__attribute__((always_inline))
|
||||
static void __wasm_v128_setbit(__wasm_v128_bitmap256_t *bitmap, uint8_t i) {
|
||||
uint8_t hi_nibble = i >> 4;
|
||||
uint8_t lo_nibble = i & 0xf;
|
||||
bitmap->lo[lo_nibble] |= (uint8_t)(1u << (hi_nibble - 0));
|
||||
bitmap->hi[lo_nibble] |= (uint8_t)(1u << (hi_nibble - 8));
|
||||
}
|
||||
|
||||
#ifndef __wasm_relaxed_simd__
|
||||
|
||||
#define wasm_i8x16_relaxed_swizzle wasm_i8x16_swizzle
|
||||
|
||||
#endif // __wasm_relaxed_simd__
|
||||
|
||||
__attribute__((always_inline))
|
||||
static v128_t __wasm_v128_chkbits(__wasm_v128_bitmap256_t bitmap, v128_t v) {
|
||||
v128_t hi_nibbles = wasm_u8x16_shr(v, 4);
|
||||
v128_t bitmask_lookup = wasm_u64x2_const_splat(0x8040201008040201);
|
||||
v128_t bitmask = wasm_i8x16_relaxed_swizzle(bitmask_lookup, hi_nibbles);
|
||||
|
||||
v128_t indices_0_7 = v & wasm_u8x16_const_splat(0x8f);
|
||||
v128_t indices_8_15 = indices_0_7 ^ wasm_u8x16_const_splat(0x80);
|
||||
|
||||
v128_t row_0_7 = wasm_i8x16_swizzle((v128_t)bitmap.lo, indices_0_7);
|
||||
v128_t row_8_15 = wasm_i8x16_swizzle((v128_t)bitmap.hi, indices_8_15);
|
||||
|
||||
v128_t bitsets = row_0_7 | row_8_15;
|
||||
return bitsets & bitmask;
|
||||
}
|
||||
|
||||
#undef wasm_i8x16_relaxed_swizzle
|
||||
|
||||
__attribute__((weak))
|
||||
size_t strspn(const char *s, const char *c) {
|
||||
// strspn must stop as soon as it finds the terminator.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
|
||||
if (!c[0]) return 0;
|
||||
if (!c[1]) {
|
||||
v128_t vc = wasm_i8x16_splat(*c);
|
||||
for (;;) {
|
||||
v128_t v = *(v128_t *)addr;
|
||||
v128_t cmp = wasm_i8x16_eq(v, vc);
|
||||
// Bitmask is slow on AArch64, all_true is much faster.
|
||||
if (!wasm_i8x16_all_true(cmp)) {
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = (uint16_t)~wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return addr - (uintptr_t)s + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
__wasm_v128_bitmap256_t bitmap = {};
|
||||
|
||||
for (; *c; c++) {
|
||||
// Terminator IS NOT on the bitmap.
|
||||
__wasm_v128_setbit(&bitmap, (uint8_t)*c);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *(v128_t *)addr;
|
||||
v128_t found = __wasm_v128_chkbits(bitmap, v);
|
||||
// Bitmask is slow on AArch64, all_true is much faster.
|
||||
if (!wasm_i8x16_all_true(found)) {
|
||||
v128_t cmp = wasm_i8x16_eq(found, (v128_t){});
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return addr - (uintptr_t)s + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
size_t strcspn(const char *s, const char *c) {
|
||||
if (!c[0] || !c[1]) return __strchrnul(s, *c) - s;
|
||||
|
||||
// strcspn must stop as soon as it finds the terminator.
|
||||
// Aligning ensures out of bounds loads are safe.
|
||||
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
|
||||
uintptr_t addr = (uintptr_t)s - align;
|
||||
|
||||
__wasm_v128_bitmap256_t bitmap = {};
|
||||
|
||||
do {
|
||||
// Terminator IS on the bitmap.
|
||||
__wasm_v128_setbit(&bitmap, (uint8_t)*c);
|
||||
} while (*c++);
|
||||
|
||||
for (;;) {
|
||||
v128_t v = *(v128_t *)addr;
|
||||
v128_t found = __wasm_v128_chkbits(bitmap, v);
|
||||
// Bitmask is slow on AArch64, any_true is much faster.
|
||||
if (wasm_v128_any_true(found)) {
|
||||
v128_t cmp = wasm_i8x16_eq(found, (v128_t){});
|
||||
// Clear the bits corresponding to align (little-endian)
|
||||
// so we can count trailing zeros.
|
||||
int mask = (uint16_t)~wasm_i8x16_bitmask(cmp) >> align << align;
|
||||
// At least one bit will be set, unless align cleared them.
|
||||
// Knowing this helps the compiler if it unrolls the loop.
|
||||
__builtin_assume(mask || align);
|
||||
// If the mask became zero because of align,
|
||||
// it's as if we didn't find anything.
|
||||
if (mask) {
|
||||
// Find the offset of the first one bit (little-endian).
|
||||
return addr - (uintptr_t)s + __builtin_ctz(mask);
|
||||
}
|
||||
}
|
||||
align = 0;
|
||||
addr += sizeof(v128_t);
|
||||
}
|
||||
}
|
||||
|
||||
// Given the above SIMD implementations,
|
||||
// these are best implemented as
|
||||
// small wrappers over those functions.
|
||||
|
||||
// Simple wrappers already in musl:
|
||||
// - mempcpy
|
||||
// - strcat
|
||||
// - strlcat
|
||||
// - strdup
|
||||
// - strndup
|
||||
// - strnlen
|
||||
// - strpbrk
|
||||
// - strsep
|
||||
// - strtok
|
||||
|
||||
__attribute__((weak))
|
||||
void *memccpy(void *__restrict dest, const void *__restrict src, int c,
|
||||
size_t n) {
|
||||
const void *m = memchr(src, c, n);
|
||||
if (m != NULL) {
|
||||
n = (char *)m - (char *)src + 1;
|
||||
m = (char *)dest + n;
|
||||
}
|
||||
memcpy(dest, src, n);
|
||||
return (void *)m;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t n) {
|
||||
size_t slen = strlen(src);
|
||||
if (n--) {
|
||||
if (n > slen) n = slen;
|
||||
memcpy(dest, src, n);
|
||||
dest[n] = 0;
|
||||
}
|
||||
return slen;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
char *strncat(char *__restrict dest, const char *__restrict src, size_t n) {
|
||||
size_t strnlen(const char *s, size_t n);
|
||||
size_t dlen = strlen(dest);
|
||||
size_t slen = strnlen(src, n);
|
||||
memcpy(dest + dlen, src, slen);
|
||||
dest[dlen + slen] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
static char *__stpcpy(char *__restrict dest, const char *__restrict src) {
|
||||
size_t slen = strlen(src);
|
||||
memcpy(dest, src, slen + 1);
|
||||
return dest + slen;
|
||||
}
|
||||
|
||||
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);
|
||||
memset(dest + slen, 0, n - slen);
|
||||
return dest + slen;
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
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;
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
char *stpncpy(char *__restrict dest, const char *__restrict src, size_t n) {
|
||||
return __stpncpy(dest, src, n);
|
||||
}
|
||||
|
||||
__attribute__((weak, always_inline))
|
||||
char *strncpy(char *__restrict dest, const char *__restrict src, size_t n) {
|
||||
__stpncpy(dest, src, n);
|
||||
return dest;
|
||||
}
|
||||
|
||||
#endif // __wasm_simd128__
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _WASM_SIMD128_STRING_H
|
||||
size_t strlen(const char*);
|
||||
size_t strspn(const char*, const char*);
|
||||
size_t strcspn(const char*, const char*);
|
||||
char* strchr(const char*, int);
|
||||
char* strrchr(const char*, int);
|
||||
char* strchrnul(const char*, int);
|
||||
int strcmp(const char*, const char*);
|
||||
int strncmp(const char*, const char*, size_t);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
typedef long long time_t;
|
||||
|
||||
struct tm {
|
||||
int tm_sec;
|
||||
int tm_min;
|
||||
int tm_hour;
|
||||
int tm_mday;
|
||||
int tm_mon;
|
||||
int tm_year;
|
||||
int tm_wday;
|
||||
int tm_yday;
|
||||
int tm_isdst;
|
||||
};
|
||||
+6
-2
@@ -18,8 +18,12 @@
|
||||
#include "time.c"
|
||||
#include "vfs.c"
|
||||
#include "vtab.c"
|
||||
// Libc
|
||||
#include "libc.c"
|
||||
#include "malloc_sbrk.c"
|
||||
|
||||
__attribute__((constructor)) void init() {
|
||||
void _initialize() {
|
||||
try_init_allocator();
|
||||
sqlite3_initialize();
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_base_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_decimal_init);
|
||||
@@ -29,4 +33,4 @@ __attribute__((constructor)) void init() {
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_spellfix_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_uint_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_time_init);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -4,14 +4,14 @@
|
||||
|
||||
#define GO_POINTER_TYPE "github.com/ncruces/go-sqlite3.Pointer"
|
||||
|
||||
int sqlite3_bind_pointer_go(sqlite3_stmt *stmt, int i, go_handle app) {
|
||||
int sqlite3_bind_pointer_go(sqlite3_stmt* stmt, int i, go_handle app) {
|
||||
return sqlite3_bind_pointer(stmt, i, app, GO_POINTER_TYPE, go_destroy);
|
||||
}
|
||||
|
||||
void sqlite3_result_pointer_go(sqlite3_context *ctx, go_handle app) {
|
||||
void sqlite3_result_pointer_go(sqlite3_context* ctx, go_handle app) {
|
||||
sqlite3_result_pointer(ctx, app, GO_POINTER_TYPE, go_destroy);
|
||||
}
|
||||
|
||||
go_handle sqlite3_value_pointer_go(sqlite3_value *val) {
|
||||
go_handle sqlite3_value_pointer_go(sqlite3_value* val) {
|
||||
return sqlite3_value_pointer(val, GO_POINTER_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
|
||||
// Platform Configuration
|
||||
|
||||
#define SQLITE_OS_OTHER 1
|
||||
#define SQLITE_BYTEORDER 1234
|
||||
#define SQLITE_MAX_PATHLEN 4096
|
||||
#define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
|
||||
|
||||
#define HAVE_INT8_T 1
|
||||
#define HAVE_INT16_T 1
|
||||
@@ -16,25 +19,20 @@
|
||||
#define HAVE_UINT64_T 1
|
||||
#define HAVE_UINTPTR_T 1
|
||||
#define HAVE_STDINT_H 1
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
#define HAVE_LOG2 1
|
||||
#define HAVE_LOG10 1
|
||||
#define HAVE_ISNAN 1
|
||||
|
||||
#define HAVE_STRCHRNUL 1
|
||||
|
||||
#define HAVE_USLEEP 1
|
||||
#define HAVE_NANOSLEEP 1
|
||||
|
||||
#define HAVE_GMTIME_R 1
|
||||
#define HAVE_LOCALTIME_S 1
|
||||
|
||||
#define HAVE_MALLOC_H 1
|
||||
#define HAVE_MALLOC_USABLE_SIZE 1
|
||||
// Implemented in malloc.c.
|
||||
size_t malloc_usable_size(void*);
|
||||
|
||||
// Implemented in hooks.c.
|
||||
static int sqliteBusyCallback(void *, int);
|
||||
static int sqliteBusyCallback(void*, int);
|
||||
|
||||
// Implemented in vfs.c.
|
||||
int localtime_s(struct tm *const pTm, time_t const *const pTime);
|
||||
int localtime_s(struct tm* const pTm, time_t const* const pTime);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#define SQLITE_OMIT_SHARED_CACHE
|
||||
#define SQLITE_OMIT_AUTOINIT
|
||||
|
||||
// We need these:
|
||||
// #define SQLITE_DEFAULT_MEMSTATUS 0
|
||||
// #define SQLITE_MAX_EXPR_DEPTH 0
|
||||
// #define SQLITE_USE_ALLOCA
|
||||
@@ -28,11 +27,8 @@
|
||||
#define SQLITE_ENABLE_SETLK_TIMEOUT 2
|
||||
#define SQLITE_ENABLE_STAT4 1
|
||||
|
||||
// We have our own memdb VFS.
|
||||
// To avoid interactions between the two,
|
||||
// omit sqlite3_serialize/sqlite3_deserialize,
|
||||
// which we also don't wrap.
|
||||
#define SQLITE_OMIT_DESERIALIZE
|
||||
#define SQLITE_OMIT_LOAD_EXTENSION
|
||||
|
||||
// Amalgamated Extensions
|
||||
|
||||
@@ -43,4 +39,4 @@
|
||||
#define SQLITE_ENABLE_GEOPOLY 1
|
||||
|
||||
#define SQLITE_SOUNDEX
|
||||
#define SQLITE_UNTESTABLE
|
||||
#define SQLITE_UNTESTABLE
|
||||
|
||||
+6
-6
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
int sqlite3_exec_go(sqlite3_stmt *stmt) {
|
||||
int sqlite3_exec_go(sqlite3_stmt* stmt) {
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW);
|
||||
return sqlite3_reset(stmt);
|
||||
}
|
||||
@@ -11,19 +11,19 @@ union sqlite3_data {
|
||||
sqlite3_int64 i;
|
||||
double d;
|
||||
struct {
|
||||
const void *ptr;
|
||||
const void* ptr;
|
||||
int len;
|
||||
};
|
||||
};
|
||||
|
||||
int sqlite3_columns_go(sqlite3_stmt *stmt, int nCol, char *aType,
|
||||
union sqlite3_data *aData) {
|
||||
int sqlite3_columns_go(sqlite3_stmt* stmt, int nCol, char* aType,
|
||||
union sqlite3_data* aData) {
|
||||
if (nCol != sqlite3_column_count(stmt)) {
|
||||
return SQLITE_MISUSE;
|
||||
}
|
||||
bool check = false;
|
||||
for (int i = 0; i < nCol; ++i) {
|
||||
const void *ptr = NULL;
|
||||
const void* ptr = NULL;
|
||||
switch (aType[i] = sqlite3_column_type(stmt, i)) {
|
||||
default: // SQLITE_NULL
|
||||
aData[i] = (union sqlite3_data){};
|
||||
@@ -55,4 +55,4 @@ static_assert(offsetof(union sqlite3_data, i) == 0, "Unexpected offset");
|
||||
static_assert(offsetof(union sqlite3_data, d) == 0, "Unexpected offset");
|
||||
static_assert(offsetof(union sqlite3_data, ptr) == 0, "Unexpected offset");
|
||||
static_assert(offsetof(union sqlite3_data, len) == 4, "Unexpected offset");
|
||||
static_assert(sizeof(union sqlite3_data) == 8, "Unexpected size");
|
||||
static_assert(sizeof(union sqlite3_data) == 8, "Unexpected size");
|
||||
|
||||
+13
-7
@@ -2,22 +2,28 @@
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
int sqlite3_bind_text_go(sqlite3_stmt *stmt, int i, const char *zData,
|
||||
#ifdef SQLITE_UTF8_ZT
|
||||
#define ENCODING SQLITE_UTF8_ZT
|
||||
#else
|
||||
#define ENCODING SQLITE_UTF8
|
||||
#endif
|
||||
|
||||
int sqlite3_bind_text_go(sqlite3_stmt* stmt, int i, const char* zData,
|
||||
sqlite3_uint64 nData) {
|
||||
return sqlite3_bind_text64(stmt, i, zData, nData, &sqlite3_free, SQLITE_UTF8);
|
||||
return sqlite3_bind_text64(stmt, i, zData, nData, &sqlite3_free, ENCODING);
|
||||
}
|
||||
|
||||
int sqlite3_bind_blob_go(sqlite3_stmt *stmt, int i, const char *zData,
|
||||
int sqlite3_bind_blob_go(sqlite3_stmt* stmt, int i, const char* zData,
|
||||
sqlite3_uint64 nData) {
|
||||
return sqlite3_bind_blob64(stmt, i, zData, nData, &sqlite3_free);
|
||||
}
|
||||
|
||||
void sqlite3_result_text_go(sqlite3_context *ctx, const char *zData,
|
||||
void sqlite3_result_text_go(sqlite3_context* ctx, const char* zData,
|
||||
sqlite3_uint64 nData) {
|
||||
sqlite3_result_text64(ctx, zData, nData, &sqlite3_free, SQLITE_UTF8);
|
||||
sqlite3_result_text64(ctx, zData, nData, &sqlite3_free, ENCODING);
|
||||
}
|
||||
|
||||
void sqlite3_result_blob_go(sqlite3_context *ctx, const void *zData,
|
||||
void sqlite3_result_blob_go(sqlite3_context* ctx, const void* zData,
|
||||
sqlite3_uint64 nData) {
|
||||
sqlite3_result_blob64(ctx, zData, nData, &sqlite3_free);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -3,8 +3,8 @@
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
static int time_collation(void *pArg, int nKey1, const void *pKey1, int nKey2,
|
||||
const void *pKey2) {
|
||||
static int time_collation(void* pArg, int nKey1, const void* pKey1, int nKey2,
|
||||
const void* pKey2) {
|
||||
UNUSED_PARAMETER(pArg);
|
||||
|
||||
// Remove a Z suffix if one key is no longer than the other.
|
||||
@@ -12,8 +12,8 @@ static int time_collation(void *pArg, int nKey1, const void *pKey1, int nKey2,
|
||||
// This avoids making different keys equal.
|
||||
const int nK1 = nKey1;
|
||||
const int nK2 = nKey2;
|
||||
const char *pK1 = (const char *)pKey1;
|
||||
const char *pK2 = (const char *)pKey2;
|
||||
const char* pK1 = (const char*)pKey1;
|
||||
const char* pK2 = (const char*)pKey2;
|
||||
if (nK1 && nK1 <= nK2 && pK1[nK1 - 1] == 'Z') {
|
||||
nKey1--;
|
||||
}
|
||||
@@ -29,10 +29,10 @@ static int time_collation(void *pArg, int nKey1, const void *pKey1, int nKey2,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3_time_init(sqlite3 *db, char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi) {
|
||||
int sqlite3_time_init(sqlite3* db, char** pzErrMsg,
|
||||
const sqlite3_api_routines* pApi) {
|
||||
UNUSED_PARAMETER2(pzErrMsg, pApi);
|
||||
sqlite3_create_collation_v2(db, "time", SQLITE_UTF8, /*arg=*/NULL,
|
||||
time_collation, /*destroy=*/NULL);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -26,14 +26,15 @@ elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-$WASI_SDK.tar.gz"
|
||||
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_125/binaryen-version_125-$BINARYEN.tar.gz"
|
||||
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-32/wasi-sdk-32.0-$WASI_SDK.tar.gz"
|
||||
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_127/binaryen-version_127-$BINARYEN.tar.gz"
|
||||
|
||||
# Download tools
|
||||
rm -rf "$ROOT/tools"
|
||||
mkdir -p "$ROOT/tools"
|
||||
[ -d "$ROOT/tools/wasi-sdk" ] || curl -#L "$WASI_SDK" | tar xzC "$ROOT/tools" &
|
||||
[ -d "$ROOT/tools/binaryen" ] || curl -#L "$BINARYEN" | tar xzC "$ROOT/tools" &
|
||||
curl -#L "$WASI_SDK" | tar xzC "$ROOT/tools" &
|
||||
curl -#L "$BINARYEN" | tar xzC "$ROOT/tools" &
|
||||
wait
|
||||
|
||||
[ -d "$ROOT/tools/wasi-sdk" ] || mv "$ROOT/tools/wasi-sdk"* "$ROOT/tools/wasi-sdk"
|
||||
[ -d "$ROOT/tools/binaryen" ] || mv "$ROOT/tools/binaryen"* "$ROOT/tools/binaryen"
|
||||
mv "$ROOT/tools/wasi-sdk"* "$ROOT/tools/wasi-sdk"
|
||||
mv "$ROOT/tools/binaryen"* "$ROOT/tools/binaryen"
|
||||
|
||||
+39
-39
@@ -5,40 +5,40 @@
|
||||
#include "include.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
int go_vfs_find(const char *zVfsName);
|
||||
int go_localtime(struct tm *, sqlite3_int64);
|
||||
int go_vfs_find(const char* zVfsName);
|
||||
int go_localtime(struct tm*, sqlite3_int64);
|
||||
|
||||
int go_randomness(sqlite3_vfs *, int nByte, char *zOut);
|
||||
int go_sleep(sqlite3_vfs *, int microseconds);
|
||||
int go_current_time_64(sqlite3_vfs *, sqlite3_int64 *);
|
||||
int go_randomness(sqlite3_vfs*, int nByte, char* zOut);
|
||||
int go_sleep(sqlite3_vfs*, int microseconds);
|
||||
int go_current_time_64(sqlite3_vfs*, sqlite3_int64*);
|
||||
|
||||
int go_open(sqlite3_vfs *, sqlite3_filename zName, sqlite3_file *, int flags,
|
||||
int *pOutFlags, int *pOutVFS);
|
||||
int go_delete(sqlite3_vfs *, const char *zName, int syncDir);
|
||||
int go_access(sqlite3_vfs *, const char *zName, int flags, int *pResOut);
|
||||
int go_full_pathname(sqlite3_vfs *, const char *zName, int nOut, char *zOut);
|
||||
int go_open(sqlite3_vfs*, sqlite3_filename zName, sqlite3_file*, int flags,
|
||||
int* pOutFlags, int* pOutVFS);
|
||||
int go_delete(sqlite3_vfs*, const char* zName, int syncDir);
|
||||
int go_access(sqlite3_vfs*, const char* zName, int flags, int* pResOut);
|
||||
int go_full_pathname(sqlite3_vfs*, const char* zName, int nOut, char* zOut);
|
||||
|
||||
int go_close(sqlite3_file *);
|
||||
int go_read(sqlite3_file *, void *, int iAmt, sqlite3_int64 iOfst);
|
||||
int go_write(sqlite3_file *, const void *, int iAmt, sqlite3_int64 iOfst);
|
||||
int go_truncate(sqlite3_file *, sqlite3_int64 size);
|
||||
int go_sync(sqlite3_file *, int flags);
|
||||
int go_file_size(sqlite3_file *, sqlite3_int64 *pSize);
|
||||
int go_file_control(sqlite3_file *, int op, void *pArg);
|
||||
int go_sector_size(sqlite3_file *file);
|
||||
int go_device_characteristics(sqlite3_file *file);
|
||||
int go_close(sqlite3_file*);
|
||||
int go_read(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
||||
int go_write(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst);
|
||||
int go_truncate(sqlite3_file*, sqlite3_int64 size);
|
||||
int go_sync(sqlite3_file*, int flags);
|
||||
int go_file_size(sqlite3_file*, sqlite3_int64* pSize);
|
||||
int go_file_control(sqlite3_file*, int op, void* pArg);
|
||||
int go_sector_size(sqlite3_file* file);
|
||||
int go_device_characteristics(sqlite3_file* file);
|
||||
|
||||
int go_lock(sqlite3_file *, int eLock);
|
||||
int go_unlock(sqlite3_file *, int eLock);
|
||||
int go_check_reserved_lock(sqlite3_file *, int *pResOut);
|
||||
int go_lock(sqlite3_file*, int eLock);
|
||||
int go_unlock(sqlite3_file*, int eLock);
|
||||
int go_check_reserved_lock(sqlite3_file*, int* pResOut);
|
||||
|
||||
int go_shm_map(sqlite3_file *, int iPg, int pgsz, int, void volatile **);
|
||||
int go_shm_lock(sqlite3_file *, int offset, int n, int flags);
|
||||
int go_shm_unmap(sqlite3_file *, int deleteFlag);
|
||||
void go_shm_barrier(sqlite3_file *);
|
||||
int go_shm_map(sqlite3_file*, int iPg, int pgsz, int, void volatile**);
|
||||
int go_shm_lock(sqlite3_file*, int offset, int n, int flags);
|
||||
int go_shm_unmap(sqlite3_file*, int deleteFlag);
|
||||
void go_shm_barrier(sqlite3_file*);
|
||||
|
||||
static int go_open_wrapper(sqlite3_vfs *vfs, sqlite3_filename zName,
|
||||
sqlite3_file *file, int flags, int *pOutFlags) {
|
||||
static int go_open_wrapper(sqlite3_vfs* vfs, sqlite3_filename zName,
|
||||
sqlite3_file* file, int flags, int* pOutFlags) {
|
||||
static const sqlite3_io_methods go_io[2] = {
|
||||
{
|
||||
.iVersion = 1,
|
||||
@@ -89,7 +89,7 @@ struct go_file {
|
||||
go_handle handle;
|
||||
};
|
||||
|
||||
sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName) {
|
||||
sqlite3_vfs* sqlite3_vfs_find(const char* zVfsName) {
|
||||
// The default VFS.
|
||||
if (!zVfsName || !strcmp(zVfsName, "os")) {
|
||||
static sqlite3_vfs os_vfs = {
|
||||
@@ -115,18 +115,18 @@ sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static sqlite3_vfs *go_vfs_list;
|
||||
static sqlite3_vfs* go_vfs_list;
|
||||
|
||||
// Do we already have a C wrapper for the Go VFS?
|
||||
for (sqlite3_vfs *it = go_vfs_list; it; it = it->pNext) {
|
||||
for (sqlite3_vfs* it = go_vfs_list; it; it = it->pNext) {
|
||||
if (!strcmp(zVfsName, it->zName)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete C wrappers that are no longer needed.
|
||||
for (sqlite3_vfs **ptr = &go_vfs_list; *ptr;) {
|
||||
sqlite3_vfs *it = *ptr;
|
||||
for (sqlite3_vfs** ptr = &go_vfs_list; *ptr;) {
|
||||
sqlite3_vfs* it = *ptr;
|
||||
if (go_vfs_find(it->zName)) {
|
||||
ptr = &it->pNext;
|
||||
} else {
|
||||
@@ -136,10 +136,10 @@ sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName) {
|
||||
}
|
||||
|
||||
// Create a new C wrapper.
|
||||
sqlite3_vfs *head = go_vfs_list;
|
||||
sqlite3_vfs* head = go_vfs_list;
|
||||
size_t vfsNameLen = strlen(zVfsName);
|
||||
go_vfs_list = malloc(sizeof(sqlite3_vfs) + vfsNameLen + 1);
|
||||
char *name = (char *)(go_vfs_list + 1);
|
||||
char* name = (char*)(go_vfs_list + 1);
|
||||
memcpy(name, zVfsName, vfsNameLen + 1);
|
||||
*go_vfs_list = (sqlite3_vfs){
|
||||
.iVersion = 2,
|
||||
@@ -160,16 +160,16 @@ sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName) {
|
||||
return go_vfs_list;
|
||||
}
|
||||
|
||||
int localtime_s(struct tm *const pTm, time_t const *const pTime) {
|
||||
int localtime_s(struct tm* const pTm, time_t const* const pTime) {
|
||||
return go_localtime(pTm, (sqlite3_int64)*pTime);
|
||||
}
|
||||
|
||||
int sqlite3_os_init() { return SQLITE_OK; }
|
||||
|
||||
int sqlite3_invoke_busy_handler_go(sqlite3_int64 token) {
|
||||
void **ap = (void **)&token;
|
||||
return ((int (*)(void *))(ap[0]))(ap[1]);
|
||||
void** ap = (void**)&token;
|
||||
return ((int (*)(void*))(ap[0]))(ap[1]);
|
||||
}
|
||||
|
||||
static_assert(offsetof(sqlite3_vfs, zName) == 16, "Unexpected offset");
|
||||
static_assert(offsetof(struct go_file, handle) == 4, "Unexpected offset");
|
||||
static_assert(offsetof(struct go_file, handle) == 4, "Unexpected offset");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Remove VFS registration. Go handles it.
|
||||
--- sqlite3.c.orig
|
||||
+++ sqlite3.c
|
||||
@@ -27176,7 +27176,7 @@
|
||||
@@ -27363,7 +27363,7 @@
|
||||
sqlite3_free(p);
|
||||
return sqlite3_os_init();
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
/*
|
||||
** The list of all registered VFS implementations.
|
||||
*/
|
||||
@@ -27273,7 +27273,7 @@
|
||||
@@ -27460,7 +27460,7 @@
|
||||
sqlite3_mutex_leave(mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
+60
-60
@@ -14,40 +14,40 @@
|
||||
#define SQLITE_VTAB_SAVEPOINTER_GO /**/ 0x080
|
||||
#define SQLITE_VTAB_SHADOWTABS_GO /***/ 0x100
|
||||
|
||||
int go_vtab_create(sqlite3_module *, int argc, const char *const *argv,
|
||||
sqlite3_vtab **, char **pzErr);
|
||||
int go_vtab_connect(sqlite3_module *, int argc, const char *const *argv,
|
||||
sqlite3_vtab **, char **pzErr);
|
||||
int go_vtab_create(sqlite3_module*, int argc, const char* const* argv,
|
||||
sqlite3_vtab**, char** pzErr);
|
||||
int go_vtab_connect(sqlite3_module*, int argc, const char* const* argv,
|
||||
sqlite3_vtab**, char** pzErr);
|
||||
|
||||
int go_vtab_disconnect(sqlite3_vtab *);
|
||||
int go_vtab_destroy(sqlite3_vtab *);
|
||||
int go_vtab_best_index(sqlite3_vtab *, sqlite3_index_info *);
|
||||
int go_cur_open(sqlite3_vtab *, sqlite3_vtab_cursor **);
|
||||
int go_vtab_disconnect(sqlite3_vtab*);
|
||||
int go_vtab_destroy(sqlite3_vtab*);
|
||||
int go_vtab_best_index(sqlite3_vtab*, sqlite3_index_info*);
|
||||
int go_cur_open(sqlite3_vtab*, sqlite3_vtab_cursor**);
|
||||
|
||||
int go_cur_close(sqlite3_vtab_cursor *);
|
||||
int go_cur_filter(sqlite3_vtab_cursor *, int idxNum, const char *idxStr,
|
||||
int argc, sqlite3_value **argv);
|
||||
int go_cur_next(sqlite3_vtab_cursor *);
|
||||
int go_cur_eof(sqlite3_vtab_cursor *);
|
||||
int go_cur_column(sqlite3_vtab_cursor *, sqlite3_context *, int);
|
||||
int go_cur_rowid(sqlite3_vtab_cursor *, sqlite3_int64 *pRowid);
|
||||
int go_cur_close(sqlite3_vtab_cursor*);
|
||||
int go_cur_filter(sqlite3_vtab_cursor*, int idxNum, const char* idxStr,
|
||||
int argc, sqlite3_value** argv);
|
||||
int go_cur_next(sqlite3_vtab_cursor*);
|
||||
int go_cur_eof(sqlite3_vtab_cursor*);
|
||||
int go_cur_column(sqlite3_vtab_cursor*, sqlite3_context*, int);
|
||||
int go_cur_rowid(sqlite3_vtab_cursor*, sqlite3_int64* pRowid);
|
||||
|
||||
int go_vtab_update(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *);
|
||||
int go_vtab_rename(sqlite3_vtab *, const char *zNew);
|
||||
int go_vtab_find_function(sqlite3_vtab *, int nArg, const char *zName,
|
||||
go_handle *pxFunc);
|
||||
int go_vtab_update(sqlite3_vtab*, int, sqlite3_value**, sqlite3_int64*);
|
||||
int go_vtab_rename(sqlite3_vtab*, const char* zNew);
|
||||
int go_vtab_find_function(sqlite3_vtab*, int nArg, const char* zName,
|
||||
go_handle* pxFunc);
|
||||
|
||||
int go_vtab_begin(sqlite3_vtab *);
|
||||
int go_vtab_sync(sqlite3_vtab *);
|
||||
int go_vtab_commit(sqlite3_vtab *);
|
||||
int go_vtab_rollback(sqlite3_vtab *);
|
||||
int go_vtab_begin(sqlite3_vtab*);
|
||||
int go_vtab_sync(sqlite3_vtab*);
|
||||
int go_vtab_commit(sqlite3_vtab*);
|
||||
int go_vtab_rollback(sqlite3_vtab*);
|
||||
|
||||
int go_vtab_savepoint(sqlite3_vtab *, int);
|
||||
int go_vtab_release(sqlite3_vtab *, int);
|
||||
int go_vtab_rollback_to(sqlite3_vtab *, int);
|
||||
int go_vtab_savepoint(sqlite3_vtab*, int);
|
||||
int go_vtab_release(sqlite3_vtab*, int);
|
||||
int go_vtab_rollback_to(sqlite3_vtab*, int);
|
||||
|
||||
int go_vtab_integrity(sqlite3_vtab *, const char *zSchema, const char *zTabName,
|
||||
int mFlags, char **pzErr);
|
||||
int go_vtab_integrity(sqlite3_vtab*, const char* zSchema, const char* zTabName,
|
||||
int mFlags, char** pzErr);
|
||||
|
||||
struct go_module {
|
||||
go_handle handle;
|
||||
@@ -64,23 +64,23 @@ struct go_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
};
|
||||
|
||||
static void go_mod_destroy(void *pAux) {
|
||||
struct go_module *mod = pAux;
|
||||
void *handle = mod->handle;
|
||||
static void go_mod_destroy(void* pAux) {
|
||||
struct go_module* mod = pAux;
|
||||
void* handle = mod->handle;
|
||||
free(mod);
|
||||
go_destroy(handle);
|
||||
}
|
||||
|
||||
static int go_vtab_create_wrapper(sqlite3 *db, void *pAux, int argc,
|
||||
const char *const *argv,
|
||||
sqlite3_vtab **ppVTab, char **pzErr) {
|
||||
static int go_vtab_create_wrapper(sqlite3* db, void* pAux, int argc,
|
||||
const char* const* argv,
|
||||
sqlite3_vtab** ppVTab, char** pzErr) {
|
||||
UNUSED_PARAMETER(db);
|
||||
|
||||
struct go_vtab *vtab = calloc(1, sizeof(struct go_vtab));
|
||||
struct go_vtab* vtab = calloc(1, sizeof(struct go_vtab));
|
||||
if (vtab == NULL) return SQLITE_NOMEM;
|
||||
*ppVTab = &vtab->base;
|
||||
|
||||
struct go_module *mod = pAux;
|
||||
struct go_module* mod = pAux;
|
||||
int rc = go_vtab_create(&mod->base, argc, argv, ppVTab, pzErr);
|
||||
if (rc) {
|
||||
if (*pzErr) *pzErr = sqlite3_mprintf("%s", *pzErr);
|
||||
@@ -89,16 +89,16 @@ static int go_vtab_create_wrapper(sqlite3 *db, void *pAux, int argc,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_vtab_connect_wrapper(sqlite3 *db, void *pAux, int argc,
|
||||
const char *const *argv,
|
||||
sqlite3_vtab **ppVTab, char **pzErr) {
|
||||
static int go_vtab_connect_wrapper(sqlite3* db, void* pAux, int argc,
|
||||
const char* const* argv,
|
||||
sqlite3_vtab** ppVTab, char** pzErr) {
|
||||
UNUSED_PARAMETER(db);
|
||||
|
||||
struct go_vtab *vtab = calloc(1, sizeof(struct go_vtab));
|
||||
struct go_vtab* vtab = calloc(1, sizeof(struct go_vtab));
|
||||
if (vtab == NULL) return SQLITE_NOMEM;
|
||||
*ppVTab = &vtab->base;
|
||||
|
||||
struct go_module *mod = pAux;
|
||||
struct go_module* mod = pAux;
|
||||
int rc = go_vtab_connect(&mod->base, argc, argv, ppVTab, pzErr);
|
||||
if (rc) {
|
||||
free(vtab);
|
||||
@@ -107,23 +107,23 @@ static int go_vtab_connect_wrapper(sqlite3 *db, void *pAux, int argc,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_vtab_disconnect_wrapper(sqlite3_vtab *pVTab) {
|
||||
struct go_vtab *vtab = container_of(pVTab, struct go_vtab, base);
|
||||
static int go_vtab_disconnect_wrapper(sqlite3_vtab* pVTab) {
|
||||
struct go_vtab* vtab = container_of(pVTab, struct go_vtab, base);
|
||||
int rc = go_vtab_disconnect(pVTab);
|
||||
free(vtab);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_vtab_destroy_wrapper(sqlite3_vtab *pVTab) {
|
||||
struct go_vtab *vtab = container_of(pVTab, struct go_vtab, base);
|
||||
static int go_vtab_destroy_wrapper(sqlite3_vtab* pVTab) {
|
||||
struct go_vtab* vtab = container_of(pVTab, struct go_vtab, base);
|
||||
int rc = go_vtab_destroy(pVTab);
|
||||
free(vtab);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_cur_open_wrapper(sqlite3_vtab *pVTab,
|
||||
sqlite3_vtab_cursor **ppCursor) {
|
||||
struct go_cursor *cur = calloc(1, sizeof(struct go_cursor));
|
||||
static int go_cur_open_wrapper(sqlite3_vtab* pVTab,
|
||||
sqlite3_vtab_cursor** ppCursor) {
|
||||
struct go_cursor* cur = calloc(1, sizeof(struct go_cursor));
|
||||
if (cur == NULL) return SQLITE_NOMEM;
|
||||
*ppCursor = &cur->base;
|
||||
|
||||
@@ -132,16 +132,16 @@ static int go_cur_open_wrapper(sqlite3_vtab *pVTab,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_cur_close_wrapper(sqlite3_vtab_cursor *pCursor) {
|
||||
struct go_cursor *cur = container_of(pCursor, struct go_cursor, base);
|
||||
static int go_cur_close_wrapper(sqlite3_vtab_cursor* pCursor) {
|
||||
struct go_cursor* cur = container_of(pCursor, struct go_cursor, base);
|
||||
int rc = go_cur_close(pCursor);
|
||||
free(cur);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_vtab_find_function_wrapper(
|
||||
sqlite3_vtab *pVTab, int nArg, const char *zName,
|
||||
void (**pxFunc)(sqlite3_context *, int, sqlite3_value **), void **ppArg) {
|
||||
sqlite3_vtab* pVTab, int nArg, const char* zName,
|
||||
void (**pxFunc)(sqlite3_context*, int, sqlite3_value**), void** ppArg) {
|
||||
go_handle handle;
|
||||
int rc = go_vtab_find_function(pVTab, nArg, zName, &handle);
|
||||
if (rc) {
|
||||
@@ -151,23 +151,23 @@ static int go_vtab_find_function_wrapper(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_vtab_integrity_wrapper(sqlite3_vtab *pVTab, const char *zSchema,
|
||||
const char *zTabName, int mFlags,
|
||||
char **pzErr) {
|
||||
static int go_vtab_integrity_wrapper(sqlite3_vtab* pVTab, const char* zSchema,
|
||||
const char* zTabName, int mFlags,
|
||||
char** pzErr) {
|
||||
int rc = go_vtab_integrity(pVTab, zSchema, zTabName, mFlags, pzErr);
|
||||
if (rc && *pzErr) *pzErr = sqlite3_mprintf("%s", *pzErr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int go_vtab_shadown_name_wrapper(const char *zName) { return true; }
|
||||
static int go_vtab_shadown_name_wrapper(const char* zName) { return true; }
|
||||
|
||||
int sqlite3_create_module_go(sqlite3 *db, const char *zName, int flags,
|
||||
int sqlite3_create_module_go(sqlite3* db, const char* zName, int flags,
|
||||
go_handle handle) {
|
||||
if (handle == NULL) {
|
||||
return sqlite3_create_module_v2(db, zName, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
struct go_module *mod = malloc(sizeof(struct go_module));
|
||||
struct go_module* mod = malloc(sizeof(struct go_module));
|
||||
if (mod == NULL) {
|
||||
go_destroy(handle);
|
||||
return SQLITE_NOMEM;
|
||||
@@ -229,7 +229,7 @@ int sqlite3_create_module_go(sqlite3 *db, const char *zName, int flags,
|
||||
return sqlite3_create_module_v2(db, zName, &mod->base, mod, go_mod_destroy);
|
||||
}
|
||||
|
||||
int sqlite3_vtab_config_go(sqlite3 *db, int op, int constraint) {
|
||||
int sqlite3_vtab_config_go(sqlite3* db, int op, int constraint) {
|
||||
return sqlite3_vtab_config(db, op, constraint);
|
||||
}
|
||||
|
||||
@@ -241,4 +241,4 @@ static_assert(sizeof(struct sqlite3_index_info) == 72, "Unexpected size");
|
||||
static_assert(sizeof(struct sqlite3_index_orderby) == 8, "Unexpected size");
|
||||
static_assert(sizeof(struct sqlite3_index_constraint) == 12, "Unexpected size");
|
||||
static_assert(sizeof(struct sqlite3_index_constraint_usage) == 8,
|
||||
"Unexpected size");
|
||||
"Unexpected size");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user