Lebesgue/Morton order.

This commit is contained in:
Nuno Cruces
2024-02-08 00:31:32 +00:00
parent b76cb33e62
commit 78cb9abefd
4 changed files with 167 additions and 1 deletions

View File

@@ -22,7 +22,7 @@ func Register(db *sqlite3.Conn) {
// and the table-valued function fsdir;
// fsys will be used to read files and list directories.
func RegisterFS(db *sqlite3.Conn, fsys fs.FS) {
db.CreateFunction("lsmode", 1, 0, lsmode)
db.CreateFunction("lsmode", 1, sqlite3.DETERMINISTIC, lsmode)
db.CreateFunction("readfile", 1, sqlite3.DIRECTONLY, readfile(fsys))
if fsys == nil {
db.CreateFunction("writefile", -1, sqlite3.DIRECTONLY, writefile)

58
ext/zorder/zorder.go Normal file
View File

@@ -0,0 +1,58 @@
// Package zorder provides functions for z-order transformations.
//
// https://sqlite.org/src/doc/tip/ext/misc/zorder.c
package zorder
import (
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
// Register registers the zorder and unzorder SQL functions.
func Register(db *sqlite3.Conn) {
flags := sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS
db.CreateFunction("zorder", -1, flags, zorder)
db.CreateFunction("unzorder", 3, flags, unzorder)
}
func zorder(ctx sqlite3.Context, arg ...sqlite3.Value) {
var x [63]int64
for i := range arg {
x[i] = arg[i].Int64()
}
if len(arg) > len(x) {
ctx.ResultError(util.ErrorString("zorder: too many parameters"))
return
}
var z int64
if len(arg) > 0 {
for i := 0; i < 63; i++ {
j := i % len(arg)
z |= (x[j] & 1) << i
x[j] >>= 1
}
}
for i := range arg {
if x[i] != 0 {
ctx.ResultError(util.ErrorString("zorder: parameter too large"))
return
}
}
ctx.ResultInt64(z)
}
func unzorder(ctx sqlite3.Context, arg ...sqlite3.Value) {
z := arg[0].Int64()
n := arg[1].Int64()
i := arg[2].Int64()
var k int
var x int64
for j := i; j < 63; j += n {
x |= ((z >> j) & 1) << k
k++
}
ctx.ResultInt64(x)
}

106
ext/zorder/zorder_test.go Normal file
View File

@@ -0,0 +1,106 @@
package zorder_test
import (
"testing"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/ext/zorder"
)
func TestRegister_zorder(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
zorder.Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
var got int64
err = db.QueryRow(`SELECT zorder(2, 3)`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if got != 14 {
t.Errorf("got %d, want 14", got)
}
err = db.QueryRow(`SELECT zorder(4, 5)`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if got != 50 {
t.Errorf("got %d, want 14", got)
}
var check bool
err = db.QueryRow(`SELECT zorder(3, 4) BETWEEN zorder(2, 3) AND zorder(4, 5)`).Scan(&check)
if err != nil {
t.Fatal(err)
}
if !check {
t.Error("want true")
}
err = db.QueryRow(`SELECT zorder(2, 2) NOT BETWEEN zorder(2, 3) AND zorder(4, 5)`).Scan(&check)
if err != nil {
t.Fatal(err)
}
if !check {
t.Error("want true")
}
}
func TestRegister_unzorder(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
zorder.Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
var got int64
err = db.QueryRow(`SELECT unzorder(zorder(3, 4), 2, 0)`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if got != 3 {
t.Errorf("got %d, want 3", got)
}
err = db.QueryRow(`SELECT unzorder(zorder(3, 4), 2, 1)`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if got != 4 {
t.Errorf("got %d, want 4", got)
}
}
func TestRegister_error(t *testing.T) {
t.Parallel()
db, err := driver.Open(":memory:", func(c *sqlite3.Conn) error {
zorder.Register(c)
return nil
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
var got int64
err = db.QueryRow(`SELECT zorder(1, 2, 3, 100000)`).Scan(&got)
if err == nil {
t.Error("want error")
}
}