Compare commits

...

12 Commits

Author SHA1 Message Date
Nuno Cruces
2640c9fb54 SQLite 3.50.3. 2025-07-17 19:42:01 +01:00
Nuno Cruces
9719d4b0e3 Better tests. 2025-07-17 01:11:16 +01:00
Nuno Cruces
b21c69dc1f Fix mode. 2025-07-17 00:50:39 +01:00
Nuno Cruces
b0f8ff44a5 Support subtype. 2025-07-17 00:47:04 +01:00
Nuno Cruces
f37bca6a80 Speedup memdb. 2025-07-15 23:08:41 +01:00
Nuno Cruces
b4e8fcb752 Avoid UB. 2025-07-15 15:52:39 +01:00
dependabot[bot]
14b98a5d05 Bump golang.org/x/crypto from 0.39.0 to 0.40.0 (#295)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.39.0 to 0.40.0.
- [Commits](https://github.com/golang/crypto/compare/v0.39.0...v0.40.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-11 00:30:01 +01:00
Nuno Cruces
36a62264f9 Remove unneeded generics. 2025-07-10 13:05:55 +01:00
Nuno Cruces
33ea564f38 Deps. 2025-07-10 00:50:14 +01:00
Nuno Cruces
5c55d8692f Fix bitset. 2025-07-04 17:10:40 +01:00
Nuno Cruces
be2f3036b4 SQLite 3.50.2. 2025-06-30 12:29:54 +01:00
Nuno Cruces
784f82f42f Avoid UB. 2025-06-25 15:27:11 +01:00
48 changed files with 737 additions and 695 deletions

View File

@@ -185,12 +185,12 @@ const (
type FunctionFlag uint32
const (
DETERMINISTIC FunctionFlag = 0x000000800
DIRECTONLY FunctionFlag = 0x000080000
INNOCUOUS FunctionFlag = 0x000200000
SELFORDER1 FunctionFlag = 0x002000000
// SUBTYPE FunctionFlag = 0x000100000
// RESULT_SUBTYPE FunctionFlag = 0x001000000
DETERMINISTIC FunctionFlag = 0x000000800
DIRECTONLY FunctionFlag = 0x000080000
SUBTYPE FunctionFlag = 0x000100000
INNOCUOUS FunctionFlag = 0x000200000
RESULT_SUBTYPE FunctionFlag = 0x001000000
SELFORDER1 FunctionFlag = 0x002000000
)
// StmtStatus name counter values associated with the [Stmt.Status] method.

View File

@@ -227,6 +227,14 @@ func (ctx Context) ResultError(err error) {
}
}
// ResultSubtype sets the subtype of the result of the function.
//
// https://sqlite.org/c3ref/result_subtype.html
func (ctx Context) ResultSubtype(t uint) {
ctx.c.call("sqlite3_result_subtype",
stk_t(ctx.handle), stk_t(uint32(t)))
}
// VTabNoChange may return true if a column is being fetched as part
// of an update during which the column value will not change.
//

View File

@@ -546,8 +546,8 @@ func (s *stmt) setupBindings(args []driver.NamedValue) (err error) {
err = s.Stmt.BindTime(id, a, s.tmWrite)
case util.JSON:
err = s.Stmt.BindJSON(id, a.Value)
case util.PointerUnwrap:
err = s.Stmt.BindPointer(id, util.UnwrapPointer(a))
case util.Pointer:
err = s.Stmt.BindPointer(id, a.Value)
case nil:
err = s.Stmt.BindNull(id)
default:
@@ -565,7 +565,7 @@ func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error {
switch arg.Value.(type) {
case bool, int, int64, float64, string, []byte,
time.Time, sqlite3.ZeroBlob,
util.JSON, util.PointerUnwrap,
util.JSON, util.Pointer,
nil:
return nil
default:

View File

@@ -1,6 +1,6 @@
# Embeddable Wasm build of SQLite
This folder includes an embeddable Wasm build of SQLite 3.50.1 for use with
This folder includes an embeddable Wasm build of SQLite 3.50.3 for use with
[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
The following optional features are compiled in:

Binary file not shown.

View File

@@ -13,8 +13,8 @@ mkdir -p build/ext/
cp "$ROOT"/sqlite3/*.[ch] build/
cp "$ROOT"/sqlite3/*.patch build/
# https://sqlite.org/src/info/93740658c8c6f531
curl -# https://sqlite.org/src/tarball/sqlite.tar.gz?r=93740658c8 | tar xz
# https://sqlite.org/src/info/ba2174bdca7d1d1a
curl -# https://sqlite.org/src/tarball/sqlite.tar.gz?r=ba2174bdca | tar xz
cd sqlite
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then

View File

@@ -4,11 +4,11 @@ go 1.23.0
toolchain go1.24.0
require github.com/ncruces/go-sqlite3 v0.26.0
require github.com/ncruces/go-sqlite3 v0.26.3
require (
github.com/ncruces/julianday v1.0.0 // indirect
github.com/ncruces/sort v0.1.5 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/sys v0.34.0 // indirect
)

View File

@@ -1,12 +1,12 @@
github.com/ncruces/go-sqlite3 v0.26.0 h1:dY6ASfuhSEbtSge6kJwjyJVC7bXCpgEVOycmdboKJek=
github.com/ncruces/go-sqlite3 v0.26.0/go.mod h1:46HIzeCQQ+aNleAxCli+vpA2tfh7ttSnw24kQahBc1o=
github.com/ncruces/go-sqlite3 v0.26.3 h1:WFkQj4KNMhbqiBPGDrVpK74w1DzcxQu3wYpmdWAvfYM=
github.com/ncruces/go-sqlite3 v0.26.3/go.mod h1:XFTPtFIo1DmGCh+XVP8KGn9b/o2f+z0WZuT09x2N6eo=
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.5 h1:fiFWXXAqKI8QckPf/6hu/bGFwcEPrirIOFaJqWujs4k=
github.com/ncruces/sort v0.1.5/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=

View File

@@ -98,6 +98,7 @@ sqlite3_result_error_toobig
sqlite3_result_int64
sqlite3_result_null
sqlite3_result_pointer_go
sqlite3_result_subtype
sqlite3_result_text_go
sqlite3_result_value
sqlite3_result_zeroblob64
@@ -126,6 +127,7 @@ sqlite3_value_int64
sqlite3_value_nochange
sqlite3_value_numeric_type
sqlite3_value_pointer_go
sqlite3_value_subtype
sqlite3_value_text
sqlite3_value_type
sqlite3_vtab_collation

View File

@@ -19,7 +19,7 @@ func Test_init(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if version != "3.50.1" {
if version != "3.50.3" {
t.Error(version)
}
}

Binary file not shown.

View File

@@ -144,18 +144,16 @@ func Test_readblob(t *testing.T) {
}
}
if stmt.Step() {
got := stmt.ColumnText(0)
if got != tt.want1 {
t.Errorf("got %q", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != tt.want1 {
t.Errorf("got %q", got)
}
if stmt.Step() {
got := stmt.ColumnText(0)
if got != tt.want2 {
t.Errorf("got %q", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != tt.want2 {
t.Errorf("got %q", got)
}
err = stmt.Err()

View File

@@ -147,20 +147,21 @@ func TestAffinity(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if got := stmt.ColumnText(0); got != "1" {
t.Errorf("got %q want 1", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "1" {
t.Errorf("got %q want 1", got)
}
if stmt.Step() {
if got := stmt.ColumnText(0); got != "0.1" {
t.Errorf("got %q want 0.1", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "0.1" {
t.Errorf("got %q want 0.1", got)
}
if stmt.Step() {
if got := stmt.ColumnText(0); got != "e" {
t.Errorf("got %q want e", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "e" {
t.Errorf("got %q want e", got)
}
}

View File

@@ -141,10 +141,10 @@ func TestRegister(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %d, want 3", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %d, want 3", got)
}
err = db.Exec(`ALTER TABLE v_x RENAME TO v_y`)

View File

@@ -92,7 +92,9 @@ func TestRegister(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
x := stmt.ColumnInt(0)
y := stmt.ColumnInt(1)
hypot := stmt.ColumnInt(2)

View File

@@ -37,7 +37,9 @@ func TestRegister_boolean(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnBool(0); got != true {
t.Errorf("got %v, want true", got)
}

View File

@@ -19,8 +19,8 @@ type mode struct {
func (m mode) Value(ctx sqlite3.Context) {
var (
max = 0
typ = sqlite3.NULL
max uint
i64 int64
f64 float64
str string
@@ -32,7 +32,6 @@ func (m mode) Value(ctx sqlite3.Context) {
i64 = k
}
}
f64 = float64(i64)
for k, v := range m.reals {
if v > max || v == max && k < f64 {
typ = sqlite3.FLOAT
@@ -66,33 +65,45 @@ func (m mode) Value(ctx sqlite3.Context) {
}
}
func (b *mode) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
func (m *mode) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
switch arg[0].Type() {
case sqlite3.INTEGER:
b.ints.add(arg[0].Int64())
if m.reals == nil {
m.ints.add(arg[0].Int64())
break
}
fallthrough
case sqlite3.FLOAT:
b.reals.add(arg[0].Float())
m.reals.add(arg[0].Float())
for k, v := range m.ints {
m.reals[float64(k)] += v
}
m.ints = nil
case sqlite3.TEXT:
b.texts.add(arg[0].Text())
m.texts.add(arg[0].Text())
case sqlite3.BLOB:
b.blobs.add(string(arg[0].RawBlob()))
m.blobs.add(string(arg[0].RawBlob()))
}
}
func (b *mode) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
func (m *mode) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
switch arg[0].Type() {
case sqlite3.INTEGER:
b.ints.del(arg[0].Int64())
if m.reals == nil {
m.ints.del(arg[0].Int64())
break
}
fallthrough
case sqlite3.FLOAT:
b.reals.del(arg[0].Float())
m.reals.del(arg[0].Float())
case sqlite3.TEXT:
b.texts.del(arg[0].Text())
m.texts.del(arg[0].Text())
case sqlite3.BLOB:
b.blobs.del(string(arg[0].RawBlob()))
m.blobs.del(string(arg[0].RawBlob()))
}
}
type counter[T comparable] map[T]int
type counter[T comparable] map[T]uint
func (c *counter[T]) add(k T) {
if (*c) == nil {
@@ -102,11 +113,9 @@ func (c *counter[T]) add(k T) {
}
func (c counter[T]) del(k T) {
switch n := c[k]; n {
default:
c[k] = n - 1
case 1:
if n := c[k]; n == 1 {
delete(c, k)
case 0:
} else {
c[k] = n - 1
}
}

View File

@@ -21,10 +21,10 @@ func TestRegister_mode(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %v, want 3", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 3 {
t.Errorf("got %v, want 3", got)
}
stmt.Close()
@@ -32,10 +32,10 @@ func TestRegister_mode(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %v, want 1", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %v, want 1", got)
}
stmt.Close()
@@ -43,10 +43,10 @@ func TestRegister_mode(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 2.5 {
t.Errorf("got %v, want 2.5", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 2.5 {
t.Errorf("got %v, want 2.5", got)
}
stmt.Close()
@@ -54,21 +54,22 @@ func TestRegister_mode(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnText(0); got != "red" {
t.Errorf("got %q, want red", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "red" {
t.Errorf("got %q, want red", got)
}
stmt.Close()
stmt, _, err = db.Prepare(`SELECT mode(column1) FROM (VALUES (X'cafebabe'), ('green'), ('blue'), (X'cafebabe'))`)
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnText(0); got != "\xca\xfe\xba\xbe" {
t.Errorf("got %q, want cafebabe", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnText(0); got != "\xca\xfe\xba\xbe" {
t.Errorf("got %q, want cafebabe", got)
}
stmt.Close()
@@ -82,4 +83,20 @@ func TestRegister_mode(t *testing.T) {
for stmt.Step() {
}
stmt.Close()
stmt, _, err = db.Prepare(`SELECT mode(column1) FROM (VALUES (?), (?), (?), (?), (?))`)
if err != nil {
t.Fatal(err)
}
stmt.BindInt(1, 1)
stmt.BindInt(2, 1)
stmt.BindInt(3, 2)
stmt.BindFloat(4, 2)
stmt.BindFloat(5, 2)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 2 {
t.Errorf("got %v, want 2", got)
}
stmt.Close()
}

View File

@@ -38,7 +38,9 @@ func TestRegister_percentile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnFloat(0); got != 10 {
t.Errorf("got %v, want 10", got)
}
@@ -65,30 +67,30 @@ func TestRegister_percentile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 5.5 {
t.Errorf("got %v, want 5.5", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 5.5 {
t.Errorf("got %v, want 5.5", got)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 7 {
t.Errorf("got %v, want 7", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 7 {
t.Errorf("got %v, want 7", got)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 10 {
t.Errorf("got %v, want 10", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 10 {
t.Errorf("got %v, want 10", got)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 14.5 {
t.Errorf("got %v, want 14.5", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 14.5 {
t.Errorf("got %v, want 14.5", got)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 16 {
t.Errorf("got %v, want 16", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnFloat(0); got != 16 {
t.Errorf("got %v, want 16", got)
}
stmt.Close()
@@ -103,7 +105,9 @@ func TestRegister_percentile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnFloat(0); got != 4 {
t.Errorf("got %v, want 4", got)
}
@@ -134,7 +138,9 @@ func TestRegister_percentile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Error("want NULL")
}

View File

@@ -58,8 +58,11 @@ import (
// Register registers statistics functions.
func Register(db *sqlite3.Conn) error {
const flags = sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS
const order = sqlite3.SELFORDER1 | flags
const (
flags = sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS
json = sqlite3.RESULT_SUBTYPE | flags
order = sqlite3.SELFORDER1 | flags
)
return errors.Join(
db.CreateWindowFunction("var_pop", 1, flags, newVariance(var_pop)),
db.CreateWindowFunction("var_samp", 1, flags, newVariance(var_samp)),
@@ -81,7 +84,7 @@ func Register(db *sqlite3.Conn) error {
db.CreateWindowFunction("regr_slope", 2, flags, newCovariance(regr_slope)),
db.CreateWindowFunction("regr_intercept", 2, flags, newCovariance(regr_intercept)),
db.CreateWindowFunction("regr_count", 2, flags, newCovariance(regr_count)),
db.CreateWindowFunction("regr_json", 2, flags, newCovariance(regr_json)),
db.CreateWindowFunction("regr_json", 2, json, newCovariance(regr_json)),
db.CreateWindowFunction("median", 1, order, newPercentile(median)),
db.CreateWindowFunction("percentile", 2, order, newPercentile(percentile_100)),
db.CreateWindowFunction("percentile_cont", 2, order, newPercentile(percentile_cont)),
@@ -227,6 +230,7 @@ func (fn *covariance) Value(ctx sqlite3.Context) {
case regr_json:
var buf [128]byte
ctx.ResultRawText(fn.regr_json(buf[:0]))
ctx.ResultSubtype('J')
return
}
ctx.ResultFloat(r)

View File

@@ -34,10 +34,10 @@ func TestRegister_variance(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}
stmt.Close()
@@ -57,7 +57,9 @@ func TestRegister_variance(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnFloat(0); got != 40 {
t.Errorf("got %v, want 40", got)
}
@@ -131,7 +133,9 @@ func TestRegister_covariance(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnInt(0); got != 0 {
t.Errorf("got %v, want 0", got)
}
@@ -156,49 +160,50 @@ func TestRegister_covariance(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if stmt.Step() {
if got := stmt.ColumnFloat(0); got != 0.9881049293224639 {
t.Errorf("got %v, want 0.9881049293224639", got)
}
if got := stmt.ColumnFloat(1); got != 21.25 {
t.Errorf("got %v, want 21.25", got)
}
if got := stmt.ColumnFloat(2); got != 17 {
t.Errorf("got %v, want 17", got)
}
if got := stmt.ColumnFloat(3); got != 4.2 {
t.Errorf("got %v, want 4.2", got)
}
if got := stmt.ColumnFloat(4); got != 75 {
t.Errorf("got %v, want 75", got)
}
if got := stmt.ColumnFloat(5); got != 14.8 {
t.Errorf("got %v, want 14.8", got)
}
if got := stmt.ColumnFloat(6); got != 500 {
t.Errorf("got %v, want 500", got)
}
if got := stmt.ColumnFloat(7); got != 85 {
t.Errorf("got %v, want 85", got)
}
if got := stmt.ColumnFloat(8); got != 0.17 {
t.Errorf("got %v, want 0.17", got)
}
if got := stmt.ColumnFloat(9); got != -8.55 {
t.Errorf("got %v, want -8.55", got)
}
if got := stmt.ColumnFloat(10); got != 0.9763513513513513 {
t.Errorf("got %v, want 0.9763513513513513", got)
}
if got := stmt.ColumnInt(11); got != 5 {
t.Errorf("got %v, want 5", got)
}
var a map[string]float64
if err := stmt.ColumnJSON(12, &a); err != nil {
t.Error(err)
} else if got := a["count"]; got != 5 {
t.Errorf("got %v, want 5", got)
}
if !stmt.Step() {
t.Fatal(stmt.Err())
}
if got := stmt.ColumnFloat(0); got != 0.9881049293224639 {
t.Errorf("got %v, want 0.9881049293224639", got)
}
if got := stmt.ColumnFloat(1); got != 21.25 {
t.Errorf("got %v, want 21.25", got)
}
if got := stmt.ColumnFloat(2); got != 17 {
t.Errorf("got %v, want 17", got)
}
if got := stmt.ColumnFloat(3); got != 4.2 {
t.Errorf("got %v, want 4.2", got)
}
if got := stmt.ColumnFloat(4); got != 75 {
t.Errorf("got %v, want 75", got)
}
if got := stmt.ColumnFloat(5); got != 14.8 {
t.Errorf("got %v, want 14.8", got)
}
if got := stmt.ColumnFloat(6); got != 500 {
t.Errorf("got %v, want 500", got)
}
if got := stmt.ColumnFloat(7); got != 85 {
t.Errorf("got %v, want 85", got)
}
if got := stmt.ColumnFloat(8); got != 0.17 {
t.Errorf("got %v, want 0.17", got)
}
if got := stmt.ColumnFloat(9); got != -8.55 {
t.Errorf("got %v, want -8.55", got)
}
if got := stmt.ColumnFloat(10); got != 0.9763513513513513 {
t.Errorf("got %v, want 0.9763513513513513", got)
}
if got := stmt.ColumnInt(11); got != 5 {
t.Errorf("got %v, want 5", got)
}
var a map[string]float64
if err := stmt.ColumnJSON(12, &a); err != nil {
t.Error(err)
} else if got := a["count"]; got != 5 {
t.Errorf("got %v, want 5", got)
}
stmt.Close()
@@ -248,7 +253,9 @@ func Benchmark_average(b *testing.B) {
b.Fatal(err)
}
if stmt.Step() {
if !stmt.Step() {
b.Fatal(stmt.Err())
} else {
want := float64(b.N) / 2
if got := stmt.ColumnFloat(0); got != want {
b.Errorf("got %v, want %v", got, want)
@@ -282,7 +289,9 @@ func Benchmark_variance(b *testing.B) {
b.Fatal(err)
}
if stmt.Step() && b.N > 100 {
if !stmt.Step() {
b.Fatal(stmt.Err())
} else if b.N > 100 {
want := float64(b.N*b.N) / 12
if got := stmt.ColumnFloat(0); want > (got-want)*float64(b.N) {
b.Errorf("got %v, want %v", got, want)

View File

@@ -26,11 +26,10 @@ func TestRegister(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
return stmt.ColumnText(0)
if !stmt.Step() {
t.Fatal(stmt.Err())
}
t.Fatal(stmt.Err())
return ""
return stmt.ColumnText(0)
}
Register(db)

8
go.mod
View File

@@ -8,16 +8,16 @@ require (
github.com/ncruces/julianday v1.0.0
github.com/ncruces/sort v0.1.5
github.com/tetratelabs/wazero v1.9.0
golang.org/x/crypto v0.39.0
golang.org/x/sys v0.33.0
golang.org/x/crypto v0.40.0
golang.org/x/sys v0.34.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/sync v0.15.0 // test
golang.org/x/text v0.26.0 // ext/unicode
golang.org/x/sync v0.16.0 // test
golang.org/x/text v0.27.0 // ext/unicode
lukechampine.com/adiantum v1.1.1 // vfs/adiantum
)

16
go.sum
View File

@@ -10,13 +10,13 @@ github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIw
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
lukechampine.com/adiantum v1.1.1 h1:4fp6gTxWCqpEbLy40ExiYDDED3oUNWx5cTqBCtPdZqA=
lukechampine.com/adiantum v1.1.1/go.mod h1:LrAYVnTYLnUtE/yMp5bQr0HstAf060YUF8nM0B6+rUw=

View File

@@ -5,7 +5,7 @@ go 1.23.0
toolchain go1.24.0
require (
github.com/ncruces/go-sqlite3 v0.26.0
github.com/ncruces/go-sqlite3 v0.26.3
gorm.io/gorm v1.30.0
)
@@ -14,6 +14,6 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/ncruces/julianday v1.0.0 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
)

View File

@@ -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.26.0 h1:dY6ASfuhSEbtSge6kJwjyJVC7bXCpgEVOycmdboKJek=
github.com/ncruces/go-sqlite3 v0.26.0/go.mod h1:46HIzeCQQ+aNleAxCli+vpA2tfh7ttSnw24kQahBc1o=
github.com/ncruces/go-sqlite3 v0.26.3 h1:WFkQj4KNMhbqiBPGDrVpK74w1DzcxQu3wYpmdWAvfYM=
github.com/ncruces/go-sqlite3 v0.26.3/go.mod h1:XFTPtFIo1DmGCh+XVP8KGn9b/o2f+z0WZuT09x2N6eo=
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.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=

View File

@@ -1,11 +1,3 @@
package util
type Pointer[T any] struct{ Value T }
func (p Pointer[T]) unwrap() any { return p.Value }
type PointerUnwrap interface{ unwrap() any }
func UnwrapPointer(p PointerUnwrap) any {
return p.unwrap()
}
type Pointer struct{ Value any }

View File

@@ -1,13 +0,0 @@
package util
import (
"math"
"testing"
)
func TestUnwrapPointer(t *testing.T) {
p := Pointer[float64]{Value: math.Pi}
if got := UnwrapPointer(p); got != math.Pi {
t.Errorf("want π, got %v", got)
}
}

View File

@@ -8,6 +8,6 @@ import "github.com/ncruces/go-sqlite3/internal/util"
// [Value.Pointer], or [Context.ResultPointer].
//
// https://sqlite.org/bindptr.html
func Pointer[T any](value T) any {
return util.Pointer[T]{Value: value}
func Pointer(value any) any {
return util.Pointer{Value: value}
}

View File

@@ -2,7 +2,7 @@
# handle, and interrupt, sqlite3_busy_timeout.
--- sqlite3.c.orig
+++ sqlite3.c
@@ -183364,7 +183364,7 @@
@@ -184447,7 +184447,7 @@
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
if( ms>0 ){
@@ -10,4 +10,4 @@
+ sqlite3_busy_handler(db, (int(*)(void*,int))sqliteBusyCallback,
(void*)db);
db->busyTimeout = ms;
}else{
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT

View File

@@ -3,7 +3,7 @@ set -euo pipefail
cd -P -- "$(dirname -- "$0")"
curl -#OL "https://sqlite.org/2025/sqlite-amalgamation-3500100.zip"
curl -#OL "https://sqlite.org/2025/sqlite-amalgamation-3500300.zip"
unzip -d . sqlite-amalgamation-*.zip
mv sqlite-amalgamation-*/sqlite3.c .
mv sqlite-amalgamation-*/sqlite3.h .
@@ -19,30 +19,30 @@ rm -rf sqlite-amalgamation-*
mkdir -p ext/
cd ext/
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/ext/misc/anycollseq.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/ext/misc/base64.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/ext/misc/decimal.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/ext/misc/ieee754.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/ext/misc/regexp.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/ext/misc/series.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/ext/misc/spellfix.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/ext/misc/uint.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/ext/misc/anycollseq.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/ext/misc/base64.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/ext/misc/decimal.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/ext/misc/ieee754.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/ext/misc/regexp.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/ext/misc/series.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/ext/misc/spellfix.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/ext/misc/uint.c"
cd ~-
cd ../vfs/tests/mptest/testdata/
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/mptest/config01.test"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/mptest/config02.test"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/mptest/crash01.test"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/mptest/crash02.subtest"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/mptest/multiwrite01.test"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/mptest/config01.test"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/mptest/config02.test"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/mptest/crash01.test"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/mptest/crash02.subtest"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/mptest/multiwrite01.test"
cd ~-
cd ../vfs/tests/mptest/wasm/
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/mptest/mptest.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/mptest/mptest.c"
cd ~-
cd ../vfs/tests/speedtest1/wasm/
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.1/test/speedtest1.c"
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.50.3/test/speedtest1.c"
cd ~-
cat *.patch | patch -p0 --no-backup-if-mismatch

View File

@@ -13,7 +13,6 @@ trap 'rm -f libc.c libc.tmp' EXIT
cat << EOF > libc.c
#include <stdlib.h>
#include <string.h>
#include <strings.h>
EOF
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -g0 -O2 \

Binary file not shown.

View File

@@ -627,9 +627,8 @@
(func $memchr (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $5 v128)
(local $6 v128)
(local $7 v128)
(block $block2
(block $block
(br_if $block
@@ -653,13 +652,8 @@
(br_if $block1
(i32.eqz
(v128.any_true
(local.tee $6
(local.tee $5
(i8x16.eq
(local.tee $7
(i8x16.splat
(local.get $1)
)
)
(v128.load
(local.tee $2
(i32.and
@@ -668,6 +662,11 @@
)
)
)
(local.tee $6
(i8x16.splat
(local.get $1)
)
)
)
)
)
@@ -675,10 +674,10 @@
)
(br_if $block1
(i32.eqz
(local.tee $5
(local.tee $1
(i32.and
(i8x16.bitmask
(local.get $6)
(local.get $5)
)
(i32.shl
(i32.const -1)
@@ -688,14 +687,14 @@
)
)
)
(local.set $1
(local.set $0
(local.get $4)
)
(br $block2)
)
(br_if $block
(i32.gt_u
(local.tee $1
(local.tee $0
(i32.sub
(i32.add
(local.get $3)
@@ -709,29 +708,26 @@
)
(local.set $2
(i32.add
(i32.sub
(local.get $0)
(local.get $3)
)
(local.get $2)
(i32.const 16)
)
)
(loop $label
(if
(v128.any_true
(local.tee $6
(local.tee $5
(i8x16.eq
(local.get $7)
(v128.load
(local.get $2)
)
(local.get $6)
)
)
)
(then
(local.set $5
(local.set $1
(i8x16.bitmask
(local.get $6)
(local.get $5)
)
)
(local.set $3
@@ -748,10 +744,10 @@
)
(br_if $label
(i32.ge_u
(local.get $1)
(local.tee $1
(local.get $0)
(local.tee $0
(i32.sub
(local.get $1)
(local.get $0)
(i32.const 16)
)
)
@@ -766,19 +762,19 @@
(select
(i32.add
(local.get $2)
(local.tee $0
(local.tee $1
(i32.ctz
(local.get $5)
(local.get $1)
)
)
)
(i32.const 0)
(i32.le_u
(i32.sub
(local.get $0)
(local.get $1)
(local.get $3)
)
(local.get $1)
(local.get $0)
)
)
)
@@ -898,9 +894,9 @@
(local $1 i32)
(local $2 i32)
(local $3 v128)
(block $block1
(block $block
(br_if $block
(block $block
(if
(i32.eqz
(i8x16.all_true
(local.tee $3
(v128.load
@@ -914,8 +910,8 @@
)
)
)
(br_if $block
(i32.eqz
(then
(br_if $block
(local.tee $2
(i32.and
(i8x16.bitmask
@@ -935,23 +931,20 @@
)
)
)
(br $block1)
)
(loop $label
(local.set $3
(v128.load offset=16
(local.get $1)
)
)
(local.set $1
(i32.add
(local.get $1)
(i32.const 16)
)
)
(br_if $label
(i8x16.all_true
(local.get $3)
(local.tee $3
(v128.load
(local.tee $1
(i32.add
(local.get $1)
(i32.const 16)
)
)
)
)
)
)
)
@@ -984,7 +977,7 @@
(local.tee $2
(v128.or
(i8x16.eq
(local.tee $2
(local.tee $3
(v128.load
(local.tee $4
(i32.and
@@ -997,12 +990,12 @@
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
(i8x16.eq
(local.get $3)
(local.tee $3
(i8x16.splat
(local.get $1)
)
)
(local.get $2)
)
)
)
@@ -1027,29 +1020,27 @@
)
)
(loop $label
(local.set $2
(v128.load offset=16
(local.get $4)
)
)
(local.set $4
(i32.add
(local.get $4)
(i32.const 16)
)
)
(br_if $label
(i32.eqz
(v128.any_true
(local.tee $2
(v128.or
(i8x16.eq
(local.get $2)
(local.tee $2
(v128.load
(local.tee $4
(i32.add
(local.get $4)
(i32.const 16)
)
)
)
)
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
(i8x16.eq
(local.get $3)
(local.get $2)
(local.get $3)
)
)
)
@@ -1080,7 +1071,7 @@
(local.tee $2
(v128.or
(i8x16.eq
(local.tee $2
(local.tee $3
(v128.load
(local.tee $4
(i32.and
@@ -1093,12 +1084,12 @@
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
(i8x16.eq
(local.get $3)
(local.tee $3
(i8x16.splat
(local.get $1)
)
)
(local.get $2)
)
)
)
@@ -1123,29 +1114,27 @@
)
)
(loop $label
(local.set $2
(v128.load offset=16
(local.get $4)
)
)
(local.set $4
(i32.add
(local.get $4)
(i32.const 16)
)
)
(br_if $label
(i32.eqz
(v128.any_true
(local.tee $2
(v128.or
(i8x16.eq
(local.get $2)
(local.tee $2
(v128.load
(local.tee $4
(i32.add
(local.get $4)
(i32.const 16)
)
)
)
)
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
(i8x16.eq
(local.get $3)
(local.get $2)
(local.get $3)
)
)
)
@@ -1196,14 +1185,15 @@
(local $2 v128)
(local $3 v128)
(local $4 v128)
(local $5 i32)
(local $5 v128)
(local $6 i32)
(local $7 i32)
(local $8 i32)
(local $9 i32)
(local $10 i32)
(if
(i32.eqz
(local.tee $5
(local.tee $7
(i32.load8_u
(local.get $1)
)
@@ -1221,7 +1211,7 @@
(i32.const -16)
)
)
(local.set $8
(local.set $9
(i32.and
(local.get $0)
(i32.const 15)
@@ -1244,11 +1234,11 @@
(local.get $4)
)
(i32.store8
(local.tee $9
(local.tee $10
(i32.or
(local.tee $7
(local.tee $8
(i32.and
(local.get $5)
(local.get $7)
(i32.const 15)
)
)
@@ -1257,17 +1247,14 @@
)
(i32.or
(i32.load8_u
(local.get $9)
(local.get $10)
)
(i32.shl
(i32.const 1)
(local.tee $5
(i32.and
(i32.shr_u
(local.get $5)
(i32.const 4)
)
(i32.const 15)
(local.tee $7
(i32.shr_u
(local.get $7)
(i32.const 4)
)
)
)
@@ -1278,26 +1265,26 @@
(local.get $3)
)
(i32.store8
(local.tee $7
(local.tee $8
(i32.or
(local.get $7)
(local.get $8)
(i32.const 4080)
)
)
(i32.or
(i32.load8_u
(local.get $7)
(local.get $8)
)
(i32.shl
(i32.const 1)
(i32.sub
(local.get $5)
(local.get $7)
(i32.const 8)
)
)
)
)
(local.set $5
(local.set $7
(i32.load8_u
(local.get $1)
)
@@ -1319,7 +1306,7 @@
)
)
(br_if $label
(local.get $5)
(local.get $7)
)
)
(block $block
@@ -1331,22 +1318,24 @@
(v128.and
(v128.or
(i8x16.swizzle
(local.get $4)
(v128.and
(local.tee $2
(v128.load
(local.get $6)
(local.get $3)
(v128.xor
(local.tee $5
(v128.and
(local.tee $2
(v128.load
(local.get $6)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
(v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)
)
)
(i8x16.swizzle
(local.get $3)
(v128.and
(local.get $2)
(v128.const i32x4 0x0f0f0f0f 0x0f0f0f0f 0x0f0f0f0f 0x0f0f0f0f)
)
(local.get $4)
(local.get $5)
)
)
(local.tee $2
@@ -1376,7 +1365,7 @@
)
(i32.shl
(i32.const -1)
(local.get $8)
(local.get $9)
)
)
)
@@ -1384,17 +1373,6 @@
)
)
(loop $label1
(local.set $2
(v128.load offset=16
(local.get $6)
)
)
(local.set $6
(i32.add
(local.get $6)
(i32.const 16)
)
)
(br_if $label1
(i8x16.all_true
(local.tee $2
@@ -1402,18 +1380,29 @@
(v128.and
(v128.or
(i8x16.swizzle
(local.get $4)
(v128.and
(local.get $2)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
(local.get $3)
(v128.xor
(local.tee $5
(v128.and
(local.tee $2
(v128.load
(local.tee $6
(i32.add
(local.get $6)
(i32.const 16)
)
)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
)
)
(v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)
)
)
(i8x16.swizzle
(local.get $3)
(v128.and
(local.get $2)
(v128.const i32x4 0x0f0f0f0f 0x0f0f0f0f 0x0f0f0f0f 0x0f0f0f0f)
)
(local.get $4)
(local.get $5)
)
)
(local.tee $2
@@ -1454,27 +1443,27 @@
)
)
)
(block $block2
(block $block1
(br_if $block1
(block $block1
(if
(i32.eqz
(i8x16.all_true
(local.tee $3
(i8x16.eq
(local.tee $4
(i8x16.splat
(local.get $5)
)
)
(v128.load
(local.get $6)
)
(local.tee $4
(i8x16.splat
(local.get $7)
)
)
)
)
)
)
(br_if $block1
(i32.eqz
(local.tee $5
(then
(br_if $block1
(local.tee $1
(i32.and
(i32.xor
(i8x16.bitmask
@@ -1484,43 +1473,33 @@
)
(i32.shl
(i32.const -1)
(local.get $8)
(local.get $9)
)
)
)
)
)
(local.set $1
(local.get $6)
)
(br $block2)
)
(loop $label2
(local.set $3
(v128.load offset=16
(local.get $6)
)
)
(local.set $6
(local.tee $1
(i32.add
(local.get $6)
(i32.const 16)
)
)
)
(br_if $label2
(i8x16.all_true
(local.tee $3
(i8x16.eq
(v128.load
(local.tee $6
(i32.add
(local.get $6)
(i32.const 16)
)
)
)
(local.get $4)
(local.get $3)
)
)
)
)
)
(local.set $5
(local.set $1
(i32.xor
(i8x16.bitmask
(local.get $3)
@@ -1531,10 +1510,10 @@
)
(i32.add
(i32.ctz
(local.get $5)
(local.get $1)
)
(i32.sub
(local.get $1)
(local.get $6)
(local.get $0)
)
)
@@ -1543,14 +1522,14 @@
(local $2 v128)
(local $3 v128)
(local $4 v128)
(local $5 i32)
(local $5 v128)
(local $6 i32)
(local $7 i32)
(local $8 i32)
(local $9 i32)
(block $block
(if
(local.tee $6
(local.tee $7
(i32.load8_u
(local.get $1)
)
@@ -1569,9 +1548,9 @@
(local.tee $3
(v128.or
(i8x16.eq
(local.tee $3
(local.tee $4
(v128.load
(local.tee $5
(local.tee $6
(i32.and
(local.get $0)
(i32.const -16)
@@ -1582,12 +1561,12 @@
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
(i8x16.eq
(local.get $4)
(local.tee $4
(i8x16.splat
(local.get $6)
(local.get $7)
)
)
(local.get $3)
)
)
)
@@ -1612,29 +1591,27 @@
)
)
(loop $label
(local.set $3
(v128.load offset=16
(local.get $5)
)
)
(local.set $5
(i32.add
(local.get $5)
(i32.const 16)
)
)
(br_if $label
(i32.eqz
(v128.any_true
(local.tee $3
(v128.or
(i8x16.eq
(local.get $3)
(local.tee $3
(v128.load
(local.tee $6
(i32.add
(local.get $6)
(i32.const 16)
)
)
)
)
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
(i8x16.eq
(local.get $4)
(local.get $3)
(local.get $4)
)
)
)
@@ -1649,38 +1626,33 @@
)
)
(return
(i32.sub
(i32.add
(local.get $5)
(i32.ctz
(local.get $1)
)
(i32.add
(i32.ctz
(local.get $1)
)
(i32.sub
(local.get $6)
(local.get $0)
)
(local.get $0)
)
)
)
(local.set $8
(i32.sub
(i32.const 0)
(local.tee $7
(i32.and
(local.get $0)
(i32.const 15)
)
)
(i32.and
(local.get $0)
(i32.const 15)
)
)
(loop $label1
(v128.store
(i32.const 4080)
(local.get $3)
(local.get $4)
)
(i32.store8
(i32.or
(local.tee $6
(local.tee $7
(i32.and
(local.tee $5
(local.tee $6
(i32.load8_u
(local.get $1)
)
@@ -1693,7 +1665,7 @@
(i32.or
(i32.load8_u
(i32.or
(local.get $6)
(local.get $7)
(i32.const 4080)
)
)
@@ -1702,7 +1674,7 @@
(i32.sub
(local.tee $9
(i32.shr_u
(local.get $5)
(local.get $6)
(i32.const 4)
)
)
@@ -1713,18 +1685,18 @@
)
(v128.store
(i32.const 4064)
(local.get $4)
(local.get $3)
)
(i32.store8
(local.tee $6
(local.tee $7
(i32.or
(local.get $6)
(local.get $7)
(i32.const 4064)
)
)
(i32.or
(i32.load8_u
(local.get $6)
(local.get $7)
)
(i32.shl
(i32.const 1)
@@ -1738,18 +1710,18 @@
(i32.const 1)
)
)
(local.set $3
(local.set $4
(v128.load
(i32.const 4080)
)
)
(local.set $4
(local.set $3
(v128.load
(i32.const 4064)
)
)
(br_if $label1
(local.get $5)
(local.get $6)
)
)
(block $block2
@@ -1761,26 +1733,28 @@
(v128.or
(i8x16.swizzle
(local.get $4)
(v128.and
(local.tee $2
(v128.load
(local.tee $5
(i32.add
(local.get $0)
(local.get $8)
(v128.xor
(local.tee $5
(v128.and
(local.tee $2
(v128.load
(local.tee $6
(i32.and
(local.get $0)
(i32.const -16)
)
)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
(v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)
)
)
(i8x16.swizzle
(local.get $3)
(v128.and
(local.get $2)
(v128.const i32x4 0x0f0f0f0f 0x0f0f0f0f 0x0f0f0f0f 0x0f0f0f0f)
)
(local.get $5)
)
)
(local.tee $2
@@ -1806,7 +1780,7 @@
)
(i32.shl
(i32.const -1)
(local.get $7)
(local.get $8)
)
)
)
@@ -1814,17 +1788,6 @@
)
)
(loop $label2
(local.set $2
(v128.load offset=16
(local.get $5)
)
)
(local.set $5
(i32.add
(local.get $5)
(i32.const 16)
)
)
(br_if $label2
(i32.eqz
(v128.any_true
@@ -1834,17 +1797,28 @@
(v128.or
(i8x16.swizzle
(local.get $4)
(v128.and
(local.get $2)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
(v128.xor
(local.tee $5
(v128.and
(local.tee $2
(v128.load
(local.tee $6
(i32.add
(local.get $6)
(i32.const 16)
)
)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
)
)
(v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)
)
)
(i8x16.swizzle
(local.get $3)
(v128.and
(local.get $2)
(v128.const i32x4 0x0f0f0f0f 0x0f0f0f0f 0x0f0f0f0f 0x0f0f0f0f)
)
(local.get $5)
)
)
(local.tee $2
@@ -1875,7 +1849,7 @@
(local.get $1)
)
(i32.sub
(local.get $5)
(local.get $6)
(local.get $0)
)
)

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
_ "embed"
"math"
"os"
"strings"
"testing"
@@ -230,6 +231,10 @@ func Test_memchr(t *testing.T) {
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",
@@ -243,9 +248,14 @@ func Test_memchr(t *testing.T) {
fill(memory[ptr:ptr+length], 5)
memory[len(memory)-1] = 7
want := len(memory) - 1
if length == 0 {
want = 0
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))
@@ -398,7 +408,7 @@ func Test_strspn(t *testing.T) {
fill(memory[ptr:ptr+max(pos, length)], 5)
memory[ptr+pos] = 7
memory[ptr+length] = 0
memory[128] = 3
memory[128] = 7 | 128
memory[129] = 5
got := call(strspn, uint64(ptr), 129)
@@ -424,7 +434,7 @@ func Test_strspn(t *testing.T) {
clear(memory)
fill(memory[ptr:ptr+length], 5)
memory[len(memory)-1] = 7
memory[128] = 3
memory[128] = 7 | 128
memory[129] = 5
got := call(strspn, uint64(ptr), 129)
@@ -452,7 +462,7 @@ func Test_strcspn(t *testing.T) {
fill(memory[ptr:ptr+max(pos, length)], 5)
memory[ptr+pos] = 7
memory[ptr+length] = 0
memory[128] = 3
memory[128] = 5 | 128
memory[129] = 7
got := call(strcspn, uint64(ptr), 129)
@@ -478,7 +488,7 @@ func Test_strcspn(t *testing.T) {
clear(memory)
fill(memory[ptr:ptr+length], 5)
memory[len(memory)-1] = 7
memory[128] = 3
memory[128] = 5 | 128
memory[129] = 7
got := call(strcspn, uint64(ptr), 129)
@@ -751,8 +761,10 @@ func Fuzz_strspn(f *testing.F) {
}
if uint32(got) != uint32(want) {
t.Errorf("strspn(%q, %q) = %d, want %d",
s, chars, 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))
}
})
}

View File

@@ -3,11 +3,8 @@
#ifndef _WASM_SIMD128_STRING_H
#define _WASM_SIMD128_STRING_H
#include <ctype.h>
#include <stdint.h>
#include <strings.h>
#include <wasm_simd128.h>
#include <__macro_PAGESIZE.h>
#ifdef __cplusplus
extern "C" {
@@ -17,7 +14,6 @@ extern "C" {
// Use the builtins if compiled with bulk memory operations.
// Clang will intrinsify using SIMD for small, constant N.
// For everything else, this helps inlining.
__attribute__((weak, always_inline))
void *memset(void *dest, int c, size_t n) {
@@ -80,7 +76,7 @@ int memcmp(const void *vl, const void *vr, size_t n) {
return 0;
}
__attribute__((weak, noinline))
__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
@@ -91,30 +87,30 @@ void *memchr(const void *s, int c, size_t n) {
// memchr must behave as if it reads characters sequentially
// and stops as soon as a match is found.
// Aligning ensures loads beyond the first match are safe.
// Volatile avoids compiler tricks around out of bounds loads.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const volatile v128_t *v = (v128_t *)((char *)s - align);
const v128_t vc = wasm_i8x16_splat(c);
uintptr_t addr = (uintptr_t)s - align;
v128_t vc = wasm_i8x16_splat(c);
for (;;) {
const v128_t cmp = wasm_i8x16_eq(*v, vc);
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 alignment (little-endian)
// 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 we cleared them.
// Knowing this helps the compiler.
// 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 is zero because of alignment,
// 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 *)v + ctz : NULL;
return ctz - align <= n ? (char *)s + (addr - (uintptr_t)s + ctz) : NULL;
}
}
// Decrement n; if it overflows we're done.
@@ -122,11 +118,11 @@ void *memchr(const void *s, int c, size_t n) {
return NULL;
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
__attribute__((weak, noinline))
__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.
@@ -137,8 +133,10 @@ void *memrchr(const void *s, int c, size_t n) {
// 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).
size_t clz = __builtin_clz(wasm_i8x16_bitmask(cmp)) - 15;
return (char *)(v + 1) - clz;
// 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);
}
}
@@ -150,64 +148,63 @@ void *memrchr(const void *s, int c, size_t n) {
return NULL;
}
__attribute__((weak, noinline))
__attribute__((weak))
size_t strlen(const char *s) {
// strlen must stop as soon as it finds the terminator.
// Aligning ensures loads beyond the terminator are safe.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const volatile v128_t *v = (v128_t *)(s - align);
uintptr_t addr = (uintptr_t)s - align;
for (;;) {
const v128_t vv = *v;
v128_t v = *(v128_t *)addr;
// Bitmask is slow on AArch64, all_true is much faster.
if (!wasm_i8x16_all_true(vv)) {
const v128_t cmp = wasm_i8x16_eq(vv, (v128_t){});
// Clear the bits corresponding to alignment (little-endian)
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 we cleared them.
// Knowing this helps the compiler.
// 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 is zero because of alignment,
// 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 *)v - s + __builtin_ctz(mask);
return addr - (uintptr_t)s + __builtin_ctz(mask);
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
static char *__strchrnul(const char *s, int c) {
// strchrnul must stop as soon as it finds the terminator.
// Aligning ensures loads beyond the terminator are safe.
// Volatile avoids compiler tricks around out of bounds loads.
// 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);
const volatile v128_t *v = (v128_t *)(s - align);
const v128_t vc = wasm_i8x16_splat(c);
uintptr_t addr = (uintptr_t)s - align;
v128_t vc = wasm_i8x16_splat(c);
for (;;) {
const v128_t vv = *v;
const v128_t cmp = wasm_i8x16_eq(vv, (v128_t){}) | wasm_i8x16_eq(vv, vc);
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 alignment (little-endian)
// 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 we cleared them.
// Knowing this helps the compiler.
// 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 is zero because of alignment,
// 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 *)v + __builtin_ctz(mask);
return (char *)s + (addr - (uintptr_t)s + __builtin_ctz(mask));
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
@@ -246,16 +243,16 @@ char *strrchr(const char *s, int c) {
// http://0x80.pl/notesen/2018-10-18-simd-byte-lookup.html
typedef struct {
__u8x16 l;
__u8x16 h;
__u8x16 lo;
__u8x16 hi;
} __wasm_v128_bitmap256_t;
__attribute__((always_inline))
static void __wasm_v128_setbit(__wasm_v128_bitmap256_t *bitmap, int i) {
uint8_t hi_nibble = (uint8_t)i >> 4;
uint8_t lo_nibble = (uint8_t)i & 0xf;
bitmap->l[lo_nibble] |= 1 << (hi_nibble - 0);
bitmap->h[lo_nibble] |= 1 << (hi_nibble - 8);
bitmap->lo[lo_nibble] |= (uint8_t)((uint32_t)1 << (hi_nibble - 0));
bitmap->hi[lo_nibble] |= (uint8_t)((uint32_t)1 << (hi_nibble - 8));
}
#ifndef __wasm_relaxed_simd__
@@ -266,54 +263,53 @@ static void __wasm_v128_setbit(__wasm_v128_bitmap256_t *bitmap, int i) {
__attribute__((always_inline))
static v128_t __wasm_v128_chkbits(__wasm_v128_bitmap256_t bitmap, v128_t v) {
v128_t indices_0_7 = v & wasm_u8x16_const_splat(0x8f);
v128_t indices_8_15 = (v & wasm_u8x16_const_splat(0x80)) ^ indices_0_7;
v128_t row_0_7 = wasm_i8x16_swizzle(bitmap.l, indices_0_7);
v128_t row_8_15 = wasm_i8x16_swizzle(bitmap.h, indices_8_15);
v128_t bitsets = row_0_7 | row_8_15;
v128_t hi_nibbles = wasm_u8x16_shr(v, 4);
v128_t bitmask_lookup = wasm_u8x16_const(1, 2, 4, 8, 16, 32, 64, 128, //
1, 2, 4, 8, 16, 32, 64, 128);
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(bitmap.lo, indices_0_7);
v128_t row_8_15 = wasm_i8x16_swizzle(bitmap.hi, indices_8_15);
v128_t bitsets = row_0_7 | row_8_15;
return wasm_i8x16_eq(bitsets & bitmask, bitmask);
}
#undef wasm_i8x16_relaxed_swizzle
__attribute__((weak, noinline))
__attribute__((weak))
size_t strspn(const char *s, const char *c) {
// strspn must stop as soon as it finds the terminator.
// Aligning ensures loads beyond the terminator are safe.
// Volatile avoids compiler tricks around out of bounds loads.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const volatile v128_t *v = (v128_t *)(s - align);
uintptr_t addr = (uintptr_t)s - align;
if (!c[0]) return 0;
if (!c[1]) {
const v128_t vc = wasm_i8x16_splat(*c);
v128_t vc = wasm_i8x16_splat(*c);
for (;;) {
const v128_t cmp = wasm_i8x16_eq(*v, vc);
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 alignment (little-endian)
// 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 we cleared them.
// Knowing this helps the compiler.
// 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 is zero because of alignment,
// 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 *)v - s + __builtin_ctz(mask);
return addr - (uintptr_t)s + __builtin_ctz(mask);
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
@@ -325,36 +321,36 @@ size_t strspn(const char *s, const char *c) {
}
for (;;) {
const v128_t cmp = __wasm_v128_chkbits(bitmap, *v);
v128_t v = *(v128_t *)addr;
v128_t cmp = __wasm_v128_chkbits(bitmap, v);
// Bitmask is slow on AArch64, all_true is much faster.
if (!wasm_i8x16_all_true(cmp)) {
// Clear the bits corresponding to alignment (little-endian)
// 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 we cleared them.
// Knowing this helps the compiler.
// 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 is zero because of alignment,
// 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 *)v - s + __builtin_ctz(mask);
return addr - (uintptr_t)s + __builtin_ctz(mask);
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}
__attribute__((weak, noinline))
__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 loads beyond the terminator are safe.
// Volatile avoids compiler tricks around out of bounds loads.
// Aligning ensures out of bounds loads are safe.
uintptr_t align = (uintptr_t)s % sizeof(v128_t);
const volatile v128_t *v = (v128_t *)(s - align);
uintptr_t addr = (uintptr_t)s - align;
__wasm_v128_bitmap256_t bitmap = {};
@@ -364,24 +360,25 @@ size_t strcspn(const char *s, const char *c) {
} while (*c++);
for (;;) {
const v128_t cmp = __wasm_v128_chkbits(bitmap, *v);
v128_t v = *(v128_t *)addr;
v128_t cmp = __wasm_v128_chkbits(bitmap, v);
// Bitmask is slow on AArch64, any_true is much faster.
if (wasm_v128_any_true(cmp)) {
// Clear the bits corresponding to alignment (little-endian)
// 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 we cleared them.
// Knowing this helps the compiler.
// 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 is zero because of alignment,
// 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 *)v - s + __builtin_ctz(mask);
return addr - (uintptr_t)s + __builtin_ctz(mask);
}
}
align = 0;
v++;
addr += sizeof(v128_t);
}
}

View File

@@ -1,7 +1,7 @@
# Remove VFS registration. Go handles it.
--- sqlite3.c.orig
+++ sqlite3.c
@@ -26726,7 +26726,7 @@
@@ -26882,7 +26882,7 @@
sqlite3_free(p);
return sqlite3_os_init();
}
@@ -10,7 +10,7 @@
/*
** The list of all registered VFS implementations.
*/
@@ -26823,7 +26823,7 @@
@@ -26979,7 +26979,7 @@
sqlite3_mutex_leave(mutex);
return SQLITE_OK;
}

View File

@@ -631,7 +631,6 @@ func (s *Stmt) ColumnValue(col int) Value {
stk_t(s.handle), stk_t(col)))
return Value{
c: s.c,
unprot: true,
handle: ptr,
}
}

View File

@@ -59,7 +59,9 @@ func Test_endianness(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnInt64(0); got != value {
t.Errorf("got %d, want %d", got, value)
}
@@ -67,9 +69,5 @@ func Test_endianness(t *testing.T) {
t.Errorf("got %s, want %d", got, value)
}
}
if err != nil {
t.Fatal(err)
}
}
}

View File

@@ -64,7 +64,9 @@ func TestCreateFunction(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -73,7 +75,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -82,7 +86,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -91,7 +97,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.FLOAT {
t.Errorf("got %v, want FLOAT", got)
}
@@ -100,7 +108,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -109,7 +119,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -118,7 +130,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -127,7 +141,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -136,7 +152,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -148,7 +166,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -157,7 +177,9 @@ func TestCreateFunction(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}

View File

@@ -168,7 +168,9 @@ func TestStmt(t *testing.T) {
t.Errorf(`got %q, want "main"`, got)
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -195,7 +197,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -222,7 +226,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.INTEGER {
t.Errorf("got %v, want INTEGER", got)
}
@@ -249,7 +255,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.FLOAT {
t.Errorf("got %v, want FLOAT", got)
}
@@ -276,7 +284,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}
@@ -303,7 +313,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -328,7 +340,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -353,7 +367,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -378,7 +394,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -403,7 +421,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -428,7 +448,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.BLOB {
t.Errorf("got %v, want BLOB", got)
}
@@ -453,7 +475,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.TEXT {
t.Errorf("got %v, want TEXT", got)
}
@@ -480,7 +504,9 @@ func TestStmt(t *testing.T) {
}
}
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
if got := stmt.ColumnType(0); got != sqlite3.NULL {
t.Errorf("got %v, want NULL", got)
}
@@ -643,7 +669,9 @@ func TestStmt_ColumnValue(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
val := stmt.ColumnValue(0)
if _, err := val.InFirst(); err == nil {
t.Error("want error")
@@ -675,7 +703,9 @@ func TestStmt_Columns(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
var dest [5]any
if err := stmt.Columns(dest[:]...); err != nil {
t.Fatal(err)

View File

@@ -244,7 +244,9 @@ func TestDB_isoWeek(t *testing.T) {
tstart := time.Date(1500, 1, 1, 12, 0, 0, 0, time.UTC)
for tm := tstart; tm.Before(tend); tm = tm.AddDate(0, 0, 1) {
stmt.BindTime(1, tm, sqlite3.TimeFormatDefault)
if stmt.Step() {
if !stmt.Step() {
t.Fatal(stmt.Err())
} else {
y, w := tm.ISOWeek()
d := tm.Weekday()
if d == 0 {

View File

@@ -37,11 +37,10 @@ func TestConn_Transaction_exec(t *testing.T) {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
return stmt.ColumnInt(0)
if !stmt.Step() {
t.Fatal(stmt.Err())
}
t.Fatal(stmt.Err())
return 0
return stmt.ColumnInt(0)
}
insert := func(succeed bool) (err error) {
@@ -130,14 +129,12 @@ func TestConn_Transaction_panic(t *testing.T) {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
return
if !stmt.Step() {
t.Fatal(stmt.Err())
}
if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
t.Fatal(stmt.Err())
}()
err = panics()
@@ -213,15 +210,10 @@ func TestConn_Transaction_interrupt(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
}
@@ -333,15 +325,10 @@ func TestConn_Transaction_rollback(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
}
@@ -382,11 +369,10 @@ func TestConn_Savepoint_exec(t *testing.T) {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
return stmt.ColumnInt(0)
if !stmt.Step() {
t.Fatal(stmt.Err())
}
t.Fatal(stmt.Err())
return 0
return stmt.ColumnInt(0)
}
insert := func(succeed bool) (err error) {
@@ -469,14 +455,12 @@ func TestConn_Savepoint_panic(t *testing.T) {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
return
if !stmt.Step() {
t.Fatal(stmt.Err())
}
if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
t.Fatal(stmt.Err())
}()
err = panics()
@@ -553,15 +537,10 @@ func TestConn_Savepoint_interrupt(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
}
@@ -599,14 +578,9 @@ func TestConn_Savepoint_rollback(t *testing.T) {
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
if !stmt.Step() {
t.Fatal(stmt.Err())
} else if got := stmt.ColumnInt(0); got != 1 {
t.Errorf("got %d, want 1", got)
}
}

View File

@@ -15,15 +15,6 @@ import (
type Value struct {
c *Conn
handle ptr_t
unprot bool
copied bool
}
func (v Value) protected() stk_t {
if v.unprot {
panic(util.ValueErr)
}
return stk_t(v.handle)
}
// Dup makes a copy of the SQL value and returns a pointer to that copy.
@@ -33,7 +24,6 @@ func (v Value) Dup() *Value {
ptr := ptr_t(v.c.call("sqlite3_value_dup", stk_t(v.handle)))
return &Value{
c: v.c,
copied: true,
handle: ptr,
}
}
@@ -42,9 +32,6 @@ func (v Value) Dup() *Value {
//
// https://sqlite.org/c3ref/value_dup.html
func (dup *Value) Close() error {
if !dup.copied {
panic(util.ValueErr)
}
dup.c.call("sqlite3_value_free", stk_t(dup.handle))
dup.handle = 0
return nil
@@ -54,14 +41,21 @@ func (dup *Value) Close() error {
//
// https://sqlite.org/c3ref/value_blob.html
func (v Value) Type() Datatype {
return Datatype(v.c.call("sqlite3_value_type", v.protected()))
return Datatype(v.c.call("sqlite3_value_type", stk_t(v.handle)))
}
// Type returns the numeric datatype of the value.
// Subtype returns the subtype of the value.
//
// https://sqlite.org/c3ref/value_subtype.html
func (v Value) Subtype() uint {
return uint(uint32(v.c.call("sqlite3_value_subtype", stk_t(v.handle))))
}
// NumericType returns the numeric datatype of the value.
//
// https://sqlite.org/c3ref/value_blob.html
func (v Value) NumericType() Datatype {
return Datatype(v.c.call("sqlite3_value_numeric_type", v.protected()))
return Datatype(v.c.call("sqlite3_value_numeric_type", stk_t(v.handle)))
}
// Bool returns the value as a bool.
@@ -85,14 +79,14 @@ func (v Value) Int() int {
//
// https://sqlite.org/c3ref/value_blob.html
func (v Value) Int64() int64 {
return int64(v.c.call("sqlite3_value_int64", v.protected()))
return int64(v.c.call("sqlite3_value_int64", stk_t(v.handle)))
}
// Float returns the value as a float64.
//
// https://sqlite.org/c3ref/value_blob.html
func (v Value) Float() float64 {
f := uint64(v.c.call("sqlite3_value_double", v.protected()))
f := uint64(v.c.call("sqlite3_value_double", stk_t(v.handle)))
return math.Float64frombits(f)
}
@@ -138,7 +132,7 @@ func (v Value) Blob(buf []byte) []byte {
//
// https://sqlite.org/c3ref/value_blob.html
func (v Value) RawText() []byte {
ptr := ptr_t(v.c.call("sqlite3_value_text", v.protected()))
ptr := ptr_t(v.c.call("sqlite3_value_text", stk_t(v.handle)))
return v.rawBytes(ptr, 1)
}
@@ -148,7 +142,7 @@ func (v Value) RawText() []byte {
//
// https://sqlite.org/c3ref/value_blob.html
func (v Value) RawBlob() []byte {
ptr := ptr_t(v.c.call("sqlite3_value_blob", v.protected()))
ptr := ptr_t(v.c.call("sqlite3_value_blob", stk_t(v.handle)))
return v.rawBytes(ptr, 0)
}
@@ -157,14 +151,14 @@ func (v Value) rawBytes(ptr ptr_t, nul int32) []byte {
return nil
}
n := int32(v.c.call("sqlite3_value_bytes", v.protected()))
n := int32(v.c.call("sqlite3_value_bytes", stk_t(v.handle)))
return util.View(v.c.mod, ptr, int64(n+nul))[:n]
}
// Pointer gets the pointer associated with this value,
// or nil if it has no associated pointer.
func (v Value) Pointer() any {
ptr := ptr_t(v.c.call("sqlite3_value_pointer_go", v.protected()))
ptr := ptr_t(v.c.call("sqlite3_value_pointer_go", stk_t(v.handle)))
return util.GetHandle(v.c.ctx, ptr)
}
@@ -194,7 +188,7 @@ func (v Value) JSON(ptr any) error {
//
// https://sqlite.org/c3ref/value_blob.html
func (v Value) NoChange() bool {
b := int32(v.c.call("sqlite3_value_nochange", v.protected()))
b := int32(v.c.call("sqlite3_value_nochange", stk_t(v.handle)))
return b != 0
}
@@ -202,7 +196,7 @@ func (v Value) NoChange() bool {
//
// https://sqlite.org/c3ref/value_blob.html
func (v Value) FromBind() bool {
b := int32(v.c.call("sqlite3_value_frombind", v.protected()))
b := int32(v.c.call("sqlite3_value_frombind", stk_t(v.handle)))
return b != 0
}

View File

@@ -2,7 +2,6 @@ package memdb
import (
"io"
"runtime"
"sync"
"time"
@@ -85,9 +84,10 @@ type memDB struct {
// +checklocks:memoryMtx
refs int32
shared int32 // +checklocks:lockMtx
pending bool // +checklocks:lockMtx
reserved bool // +checklocks:lockMtx
shared int32 // +checklocks:lockMtx
pending bool // +checklocks:lockMtx
reserved bool // +checklocks:lockMtx
waiter *sync.Cond // +checklocks:lockMtx
lockMtx sync.Mutex
dataMtx sync.RWMutex
@@ -195,8 +195,6 @@ func (m *memFile) Size() (int64, error) {
return m.size, nil
}
const spinWait = 25 * time.Microsecond
func (m *memFile) Lock(lock vfs.LockLevel) error {
if m.lock >= lock {
return nil
@@ -228,13 +226,18 @@ func (m *memFile) Lock(lock vfs.LockLevel) error {
m.pending = true
}
for before := time.Now(); m.shared > 1; {
if time.Since(before) > spinWait {
return sqlite3.BUSY
if m.shared > 1 {
before := time.Now()
if m.waiter == nil {
m.waiter = sync.NewCond(&m.lockMtx)
}
defer time.AfterFunc(time.Millisecond, m.waiter.Broadcast).Stop()
for m.shared > 1 {
if time.Since(before) > time.Millisecond {
return sqlite3.BUSY
}
m.waiter.Wait()
}
m.lockMtx.Unlock()
runtime.Gosched()
m.lockMtx.Lock()
}
}
@@ -257,7 +260,9 @@ func (m *memFile) Unlock(lock vfs.LockLevel) error {
m.pending = false
}
if lock < vfs.LOCK_SHARED {
m.shared--
if m.shared--; m.pending && m.shared <= 1 && m.waiter != nil {
m.waiter.Broadcast()
}
}
m.lock = lock
return nil

Binary file not shown.