[chore] update dependencies (#4801)
- github.com/ncruces/go-sqlite3: v0.33.3 -> v0.34.0 - github.com/oklog/ulid: v1.3.1 -> v2.1.1 Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4801
This commit is contained in:
@@ -55,8 +55,8 @@ require (
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/minio/minio-go/v7 v7.0.100
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/ncruces/go-sqlite3 v0.33.3
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/ncruces/go-sqlite3 v0.34.0
|
||||
github.com/oklog/ulid/v2 v2.1.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/rivo/uniseg v0.4.7
|
||||
@@ -192,10 +192,9 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/go-sqlite3-wasm v1.1.1-0.20260409221933-87e4b35a38d0 // indirect
|
||||
github.com/ncruces/go-sqlite3-wasm/v2 v2.1.35300 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/oklog/ulid/v2 v2.1.1 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
|
||||
@@ -368,16 +368,14 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
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/ncruces/go-sqlite3 v0.33.3 h1:6jCR3KuGvJSEwhaQrkHDGeIe2qCQ6nOUDNsPz7ZIotw=
|
||||
github.com/ncruces/go-sqlite3 v0.33.3/go.mod h1:t2Osfw0wcKzJTgv2EvrkTtVLqlbKTA5Yvwb2ypAlBcY=
|
||||
github.com/ncruces/go-sqlite3-wasm v1.1.1-0.20260409221933-87e4b35a38d0 h1:ymE9H30x1AyW5VfMNkJC9teuI2W1jjMsQS7kc6zl6Tg=
|
||||
github.com/ncruces/go-sqlite3-wasm v1.1.1-0.20260409221933-87e4b35a38d0/go.mod h1:/H3+JykPsfSlvKbOxNSx9kKwm3ecqQGzyCs1e9KkNsU=
|
||||
github.com/ncruces/go-sqlite3 v0.34.0 h1:q2I6wHTLWIoz6ehYkKdG5dGQc66eJv7ZGnekhvuMfK8=
|
||||
github.com/ncruces/go-sqlite3 v0.34.0/go.mod h1:qpBxsSdGPnO9K5OExuv5GEsrGQ7Rk6JsJFH6wn2DwwU=
|
||||
github.com/ncruces/go-sqlite3-wasm/v2 v2.1.35300 h1:cRdxCt3BDfMu0vfSdoqaAPD+dzIXPkGREjqyZMLN2Ak=
|
||||
github.com/ncruces/go-sqlite3-wasm/v2 v2.1.35300/go.mod h1:R2kJLPoSA/GBX/b8x7zwOq/KLAw6rLMY1l3Hi76SQIo=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=
|
||||
github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"github.com/oklog/ulid"
|
||||
"github.com/oklog/ulid/v2"
|
||||
)
|
||||
|
||||
type testSuite interface {
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import (
|
||||
"code.superseriousbusiness.org/gopkg/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
"github.com/oklog/ulid"
|
||||
"github.com/oklog/ulid/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
libc/
|
||||
tools/
|
||||
Generated
Vendored
Generated
Vendored
+197
@@ -0,0 +1,197 @@
|
||||
// Code generated by libc-gen. DO NOT EDIT.
|
||||
|
||||
package sqlite3_wasm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func (m *Module) _acos(x float64) float64 { return math.Acos(x) }
|
||||
func (m *Module) _acosh(x float64) float64 { return math.Acosh(x) }
|
||||
func (m *Module) _asin(x float64) float64 { return math.Asin(x) }
|
||||
func (m *Module) _asinh(x float64) float64 { return math.Asinh(x) }
|
||||
func (m *Module) _atan(x float64) float64 { return math.Atan(x) }
|
||||
func (m *Module) _atan2(y, x float64) float64 { return math.Atan2(y, x) }
|
||||
func (m *Module) _atanh(x float64) float64 { return math.Atanh(x) }
|
||||
|
||||
func (m *Module) _cos(x float64) float64 { return math.Cos(x) }
|
||||
func (m *Module) _cosh(x float64) float64 { return math.Cosh(x) }
|
||||
|
||||
func (m *Module) _exp(x float64) float64 { return math.Exp(x) }
|
||||
|
||||
func (m *Module) _fmod(x, y float64) float64 { return math.Mod(x, y) }
|
||||
func (m *Module) _localtime_r(timer, buf int32) int32 {
|
||||
const size = 32 / 8
|
||||
t := load64((*m.memory)[uint32(timer):])
|
||||
m._storetime_r((*m.memory)[uint32(buf):], time.Unix(int64(t), 0))
|
||||
return buf
|
||||
}
|
||||
|
||||
func (m *Module) _log(x float64) float64 { return math.Log(x) }
|
||||
func (m *Module) _log10(x float64) float64 { return math.Log10(x) }
|
||||
|
||||
func (m *Module) _log2(x float64) float64 { return math.Log2(x) }
|
||||
func (m *Module) _memchr(s, c, n int32) int32 {
|
||||
b := (*m.memory)[uint32(s):]
|
||||
if uint(len(b)) > uint(uint32(n)) {
|
||||
b = b[:uint32(n)]
|
||||
}
|
||||
if i := bytes.IndexByte(b, byte(c)); i >= 0 {
|
||||
return s + int32(i)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Module) _memcmp(s1, s2, n int32) int32 {
|
||||
e1, e2 := s1+n, s2+n
|
||||
b1 := (*m.memory)[uint32(s1):uint32(e1)]
|
||||
b2 := (*m.memory)[uint32(s2):uint32(e2)]
|
||||
return int32(bytes.Compare(b1, b2))
|
||||
}
|
||||
|
||||
func (m *Module) _pow(x, y float64) float64 { return math.Pow(x, y) }
|
||||
|
||||
func (m *Module) _sin(x float64) float64 { return math.Sin(x) }
|
||||
func (m *Module) _sinh(x float64) float64 { return math.Sinh(x) }
|
||||
func (m *Module) _strchr(s, c int32) int32 {
|
||||
s = m._strchrnul(s, c)
|
||||
if (*m.memory)[uint32(s)] == byte(c) {
|
||||
return s
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Module) _strchrnul(s, c int32) int32 {
|
||||
b := (*m.memory)[uint32(s):]
|
||||
b = b[:bytes.IndexByte(b, 0)]
|
||||
sz := len(b)
|
||||
if c := byte(c); c != 0 {
|
||||
if i := bytes.IndexByte(b, c); i >= 0 {
|
||||
sz = i
|
||||
}
|
||||
}
|
||||
return s + int32(sz)
|
||||
}
|
||||
|
||||
func (m *Module) _strcmp(s1, s2 int32) int32 {
|
||||
b1 := (*m.memory)[uint32(s1):]
|
||||
b2 := (*m.memory)[uint32(s2):]
|
||||
sz := min(len(b1), len(b2))
|
||||
if i := bytes.IndexByte(b1[:sz], 0); i >= 0 {
|
||||
sz = i + 1
|
||||
}
|
||||
return int32(bytes.Compare(b1[:sz], b2[:sz]))
|
||||
}
|
||||
|
||||
func (m *Module) _strcpy(d, s int32) int32 {
|
||||
b := (*m.memory)[uint32(s):]
|
||||
b = b[:bytes.IndexByte(b, 0)+1]
|
||||
copy((*m.memory)[uint32(d):], b)
|
||||
return d
|
||||
}
|
||||
func (m *Module) _strcspn(s, reject int32) int32 {
|
||||
b := (*m.memory)[uint32(s):]
|
||||
r := (*m.memory)[uint32(reject):]
|
||||
r = r[:bytes.IndexByte(r, 0)+1]
|
||||
|
||||
for i, b := range b {
|
||||
if bytes.IndexByte(r, b) >= 0 {
|
||||
return int32(i)
|
||||
}
|
||||
}
|
||||
return int32(len(b))
|
||||
}
|
||||
func (m *Module) _strlen(s int32) int32 {
|
||||
return int32(bytes.IndexByte((*m.memory)[uint32(s):], 0))
|
||||
}
|
||||
|
||||
func (m *Module) _strncmp(s1, s2, n int32) int32 {
|
||||
b1 := (*m.memory)[uint32(s1):]
|
||||
b2 := (*m.memory)[uint32(s2):]
|
||||
sz := int(min(uint(len(b1)), uint(len(b2)), uint(uint32(n))))
|
||||
if i := bytes.IndexByte(b1[:sz], 0); i >= 0 {
|
||||
sz = i + 1
|
||||
}
|
||||
return int32(bytes.Compare(b1[:sz], b2[:sz]))
|
||||
}
|
||||
func (m *Module) _strrchr(s, c int32) int32 {
|
||||
b := (*m.memory)[uint32(s):]
|
||||
b = b[:bytes.IndexByte(b, 0)+1]
|
||||
if i := bytes.LastIndexByte(b, byte(c)); i >= 0 {
|
||||
return s + int32(i)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Module) _strspn(s, accept int32) int32 {
|
||||
b := (*m.memory)[uint32(s):]
|
||||
a := (*m.memory)[uint32(accept):]
|
||||
a = a[:bytes.IndexByte(a, 0)]
|
||||
|
||||
for i, b := range b {
|
||||
if bytes.IndexByte(a, b) < 0 {
|
||||
return int32(i)
|
||||
}
|
||||
}
|
||||
return int32(len(b))
|
||||
}
|
||||
|
||||
func (m *Module) _strstr(haystack, needle int32) int32 {
|
||||
h := (*m.memory)[uint32(haystack):]
|
||||
n := (*m.memory)[uint32(needle):]
|
||||
h = h[:bytes.IndexByte(h, 0)]
|
||||
n = n[:bytes.IndexByte(n, 0)]
|
||||
i := bytes.Index(h, n)
|
||||
if i < 0 {
|
||||
return 0
|
||||
}
|
||||
return haystack + int32(i)
|
||||
}
|
||||
func (m *Module) _strtol(s, endptr int32, base int32) int32 {
|
||||
m0 := (*m.memory)[uint32(s):]
|
||||
m1 := bytes.TrimLeft(m0, " \t\n\v\f\r")
|
||||
m2 := bytes.TrimLeft(m1, "+-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
spaces := len(m0) - len(m1)
|
||||
digits := len(m1) - len(m2)
|
||||
|
||||
var val int64
|
||||
for ; digits > 0; digits-- {
|
||||
var err error
|
||||
str := unsafe.String(&m1[0], digits)
|
||||
val, err = strconv.ParseInt(str, int(base), 32)
|
||||
if e, ok := err.(*strconv.NumError); !ok || e.Err == strconv.ErrRange {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if endptr != 0 {
|
||||
if digits > 0 {
|
||||
s += int32(spaces + digits)
|
||||
}
|
||||
store32((*m.memory)[uint32(endptr):], uint32(s))
|
||||
}
|
||||
return int32(val)
|
||||
}
|
||||
func (m *Module) _tan(x float64) float64 { return math.Tan(x) }
|
||||
func (m *Module) _tanh(x float64) float64 { return math.Tanh(x) }
|
||||
func (m *Module) _storetime_r(buf []byte, t time.Time) {
|
||||
const size = 32 / 8
|
||||
var isdst uint32
|
||||
if t.IsDST() {
|
||||
isdst = 1
|
||||
}
|
||||
|
||||
store32(buf[0*size:], uint32(t.Second()))
|
||||
store32(buf[1*size:], uint32(t.Minute()))
|
||||
store32(buf[2*size:], uint32(t.Hour()))
|
||||
store32(buf[3*size:], uint32(t.Day()))
|
||||
store32(buf[4*size:], uint32(t.Month()-time.January))
|
||||
store32(buf[5*size:], uint32(t.Year()-1900))
|
||||
store32(buf[6*size:], uint32(t.Weekday()-time.Sunday))
|
||||
store32(buf[7*size:], uint32(t.YearDay()-1))
|
||||
store32(buf[8*size:], isdst)
|
||||
}
|
||||
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
+4593
-8133
File diff suppressed because one or more lines are too long
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
|
||||
WASI_SDK="x86_64-windows"
|
||||
BINARYEN="x86_64-windows"
|
||||
elif [[ "$OSTYPE" == "linux"* ]]; then
|
||||
if [[ "$(uname -m)" == "x86_64" ]]; then
|
||||
WASI_SDK="x86_64-linux"
|
||||
BINARYEN="x86_64-linux"
|
||||
else
|
||||
WASI_SDK="arm64-linux"
|
||||
BINARYEN="aarch64-linux"
|
||||
fi
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
if [[ "$(uname -m)" == "x86_64" ]]; then
|
||||
WASI_SDK="x86_64-macos"
|
||||
BINARYEN="x86_64-macos"
|
||||
else
|
||||
WASI_SDK="arm64-macos"
|
||||
BINARYEN="arm64-macos"
|
||||
fi
|
||||
fi
|
||||
|
||||
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_129/binaryen-version_129-$BINARYEN.tar.gz"
|
||||
|
||||
# Download tools
|
||||
rm -rf "tools/"
|
||||
mkdir -p "tools/"
|
||||
curl -#L "$WASI_SDK" | tar xzC "tools/" &
|
||||
curl -#L "$BINARYEN" | tar xzC "tools/" &
|
||||
wait
|
||||
|
||||
mv "tools/wasi-sdk"* "tools/wasi-sdk"
|
||||
mv "tools/binaryen"* "tools/binaryen"
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"sync"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/internal/errutil"
|
||||
)
|
||||
|
||||
var (
|
||||
// +checklocks:extRegistryMtx
|
||||
extRegistry []func(*Conn) error
|
||||
extRegistryMtx sync.RWMutex
|
||||
)
|
||||
|
||||
// AutoExtension causes the entryPoint function to be invoked
|
||||
// for each new database connection that is created.
|
||||
//
|
||||
// https://sqlite.org/c3ref/auto_extension.html
|
||||
func AutoExtension(entryPoint func(*Conn) error) {
|
||||
extRegistryMtx.Lock()
|
||||
extRegistry = append(extRegistry, entryPoint)
|
||||
extRegistryMtx.Unlock()
|
||||
}
|
||||
|
||||
func initExtensions(c *Conn) error {
|
||||
c.base64()
|
||||
extRegistryMtx.RLock()
|
||||
defer extRegistryMtx.RUnlock()
|
||||
for _, f := range extRegistry {
|
||||
if err := f(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) base64() error {
|
||||
return c.CreateFunction("base64", 1, DETERMINISTIC, func(ctx Context, arg ...Value) {
|
||||
switch a := arg[0]; a.Type() {
|
||||
case NULL:
|
||||
|
||||
case BLOB:
|
||||
data := a.RawBlob()
|
||||
code := base64.StdEncoding
|
||||
size := int64(code.EncodedLen(len(data)))
|
||||
if size > _MAX_LENGTH {
|
||||
ctx.ResultError(TOOBIG)
|
||||
return
|
||||
}
|
||||
ptr := c.wrp.New(size)
|
||||
if size > 0 {
|
||||
code.Encode(c.wrp.Bytes(ptr, size), data)
|
||||
}
|
||||
ctx.c.wrp.Xsqlite3_result_text_go(int32(ctx.handle), int32(ptr), size)
|
||||
|
||||
case TEXT:
|
||||
data := a.RawText()
|
||||
data = bytes.Trim(data, " \t\n\v\f\r")
|
||||
data = bytes.TrimRight(data, "=")
|
||||
code := base64.RawStdEncoding
|
||||
size := int64(code.DecodedLen(len(data)))
|
||||
if size > _MAX_LENGTH {
|
||||
ctx.ResultError(TOOBIG)
|
||||
return
|
||||
}
|
||||
ptr := c.wrp.New(size)
|
||||
if size > 0 {
|
||||
n, _ := code.Decode(c.wrp.Bytes(ptr, size), data)
|
||||
size = int64(n)
|
||||
}
|
||||
ctx.c.wrp.Xsqlite3_result_blob_go(int32(ctx.handle), int32(ptr), size)
|
||||
|
||||
default:
|
||||
ctx.ResultError(errutil.ErrorString("base64: accepts only blob or text"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionLibrary represents a dynamically linked SQLite extension.
|
||||
type ExtensionLibrary interface {
|
||||
Xsqlite3_extension_init(db, _, _ int32) int32
|
||||
}
|
||||
|
||||
// ExtensionInfo returns values needed to load a dynamically linked SQLite extension.
|
||||
type ExtensionInfo func() (memorySize, memoryAlignment, tableSize, tableAlignment int64)
|
||||
|
||||
type extEnv struct {
|
||||
*env
|
||||
memoryBase int32
|
||||
tableBase int32
|
||||
}
|
||||
|
||||
func (e *extEnv) X__memory_base() *int32 { return &e.memoryBase }
|
||||
func (e *extEnv) X__table_base() *int32 { return &e.tableBase }
|
||||
|
||||
// ExtensionInit loads an SQLite extension library.
|
||||
//
|
||||
// https://sqlite.org/loadext.html
|
||||
func ExtensionInit[Env any, Mod ExtensionLibrary](db *Conn, init func(env Env) Mod, info ExtensionInfo) error {
|
||||
memSize, memAlign, tableSize, tableAlign := info()
|
||||
|
||||
var memBase int32
|
||||
if memSize > 0 {
|
||||
memBase = db.wrp.Xaligned_alloc(int32(memAlign), int32(memSize))
|
||||
if memBase == 0 {
|
||||
panic(errutil.OOMErr)
|
||||
}
|
||||
}
|
||||
|
||||
var tableBase int
|
||||
if tableSize > 0 {
|
||||
// Round up to the alignment.
|
||||
rnd := int(tableAlign) - 1
|
||||
tab := db.wrp.X__indirect_function_table()
|
||||
tableBase = (len(*tab) + rnd) &^ rnd
|
||||
if add := tableBase + int(tableSize) - len(*tab); add > 0 {
|
||||
*tab = append(*tab, make([]any, add)...)
|
||||
}
|
||||
}
|
||||
|
||||
e := &extEnv{
|
||||
env: &env{db.wrp},
|
||||
memoryBase: memBase,
|
||||
tableBase: int32(tableBase),
|
||||
}
|
||||
|
||||
mod := init(any(e).(Env))
|
||||
if opt, ok := any(mod).(interface{ X__wasm_apply_data_relocs() }); ok {
|
||||
opt.X__wasm_apply_data_relocs()
|
||||
}
|
||||
if opt, ok := any(mod).(interface{ X__wasm_call_ctors() }); ok {
|
||||
opt.X__wasm_call_ctors()
|
||||
}
|
||||
rc := mod.Xsqlite3_extension_init(int32(db.handle), 0, 0)
|
||||
return db.error(res_t(rc))
|
||||
}
|
||||
+5
-7
@@ -1,6 +1,7 @@
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"iter"
|
||||
"sync"
|
||||
@@ -30,13 +31,10 @@ func (c *Conn) CollationNeeded(cb func(db *Conn, name string)) error {
|
||||
//
|
||||
// This can be used to load schemas that contain
|
||||
// one or more unknown collating sequences.
|
||||
func (c Conn) AnyCollationNeeded() error {
|
||||
rc := res_t(c.wrp.Xsqlite3_anycollseq_init(int32(c.handle), 0, 0))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
c.collation = nil
|
||||
return nil
|
||||
func (c *Conn) AnyCollationNeeded() error {
|
||||
return c.CollationNeeded(func(db *Conn, name string) {
|
||||
db.CreateCollation(name, bytes.Compare)
|
||||
})
|
||||
}
|
||||
|
||||
// CreateCollation defines a new collating sequence.
|
||||
|
||||
+5
-4
@@ -56,11 +56,12 @@ type MappedRegion struct {
|
||||
}
|
||||
|
||||
func (r *MappedRegion) Unmap() error {
|
||||
// We can't munmap the region, otherwise it could be remaped.
|
||||
// Instead, convert it to a protected, private, anonymous mapping.
|
||||
// If successful, it can be reused for a subsequent mmap.
|
||||
// We can't munmap the region, otherwise it could be remaped by the runtime.
|
||||
// We shouldn't create a hole, because unaligned reads might fail.
|
||||
// Instead remap it readonly, and if successful,
|
||||
// it can be reused for a subsequent mmap.
|
||||
_, err := unix.MmapPtr(-1, 0, r.addr, uintptr(r.size),
|
||||
unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_FIXED|unix.MAP_ANON)
|
||||
unix.PROT_READ, unix.MAP_PRIVATE|unix.MAP_FIXED|unix.MAP_ANON)
|
||||
r.used = err != nil
|
||||
return err
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package sqlite3_wrap
|
||||
import (
|
||||
"io"
|
||||
|
||||
sqlite3_wasm "github.com/ncruces/go-sqlite3-wasm"
|
||||
sqlite3_wasm "github.com/ncruces/go-sqlite3-wasm/v2"
|
||||
"github.com/ncruces/go-sqlite3/internal/errutil"
|
||||
)
|
||||
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package sqlite3
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
// +checklocks:extRegistryMtx
|
||||
extRegistry []func(*Conn) error
|
||||
extRegistryMtx sync.RWMutex
|
||||
)
|
||||
|
||||
// AutoExtension causes the entryPoint function to be invoked
|
||||
// for each new database connection that is created.
|
||||
//
|
||||
// https://sqlite.org/c3ref/auto_extension.html
|
||||
func AutoExtension(entryPoint func(*Conn) error) {
|
||||
extRegistryMtx.Lock()
|
||||
extRegistry = append(extRegistry, entryPoint)
|
||||
extRegistryMtx.Unlock()
|
||||
}
|
||||
|
||||
func initExtensions(c *Conn) error {
|
||||
extRegistryMtx.RLock()
|
||||
defer extRegistryMtx.RUnlock()
|
||||
for _, f := range extRegistry {
|
||||
if err := f(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+1
-168
@@ -2,15 +2,13 @@
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"math"
|
||||
"math/bits"
|
||||
"time"
|
||||
_ "unsafe"
|
||||
|
||||
sqlite3_wasm "github.com/ncruces/go-sqlite3-wasm"
|
||||
sqlite3_wasm "github.com/ncruces/go-sqlite3-wasm/v2"
|
||||
"github.com/ncruces/go-sqlite3/internal/errutil"
|
||||
"github.com/ncruces/go-sqlite3/internal/sqlite3_wrap"
|
||||
"github.com/ncruces/go-sqlite3/vfs"
|
||||
@@ -49,154 +47,6 @@ func createWrapper(ctx context.Context) (*sqlite3_wrap.Wrapper, error) {
|
||||
|
||||
func (e *env) Xmemory() sqlite3_wasm.Memory { return e.Memory }
|
||||
|
||||
// Math functions.
|
||||
|
||||
func (*env) Xacos(x float64) float64 { return math.Acos(x) }
|
||||
func (*env) Xacosh(x float64) float64 { return math.Acosh(x) }
|
||||
func (*env) Xasin(x float64) float64 { return math.Asin(x) }
|
||||
func (*env) Xasinh(x float64) float64 { return math.Asinh(x) }
|
||||
func (*env) Xatan(x float64) float64 { return math.Atan(x) }
|
||||
func (*env) Xatan2(y, x float64) float64 { return math.Atan2(y, x) }
|
||||
func (*env) Xatanh(x float64) float64 { return math.Atanh(x) }
|
||||
func (*env) Xcos(x float64) float64 { return math.Cos(x) }
|
||||
func (*env) Xcosh(x float64) float64 { return math.Cosh(x) }
|
||||
func (*env) Xexp(x float64) float64 { return math.Exp(x) }
|
||||
func (*env) Xfmod(x, y float64) float64 { return math.Mod(x, y) }
|
||||
func (*env) Xlog(x float64) float64 { return math.Log(x) }
|
||||
func (*env) Xlog10(x float64) float64 { return math.Log10(x) }
|
||||
func (*env) Xlog2(x float64) float64 { return math.Log2(x) }
|
||||
func (*env) Xpow(x, y float64) float64 { return math.Pow(x, y) }
|
||||
func (*env) Xsin(x float64) float64 { return math.Sin(x) }
|
||||
func (*env) Xsinh(x float64) float64 { return math.Sinh(x) }
|
||||
func (*env) Xtan(x float64) float64 { return math.Tan(x) }
|
||||
func (*env) Xtanh(x float64) float64 { return math.Tanh(x) }
|
||||
|
||||
// String functions.
|
||||
|
||||
func (e *env) Xmemchr(s, c, n int32) int32 {
|
||||
m := e.Buf[s:]
|
||||
if len(m) > int(n) {
|
||||
m = m[:n]
|
||||
}
|
||||
if i := bytes.IndexByte(m, byte(c)); i >= 0 {
|
||||
return s + int32(i)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *env) Xmemcmp(s1, s2, n int32) int32 {
|
||||
e1, e2 := s1+n, s2+n
|
||||
m1 := e.Buf[s1:e1]
|
||||
m2 := e.Buf[s2:e2]
|
||||
return int32(bytes.Compare(m1, m2))
|
||||
}
|
||||
|
||||
func (e *env) Xstrlen(s int32) int32 {
|
||||
return int32(bytes.IndexByte(e.Buf[s:], 0))
|
||||
}
|
||||
|
||||
func (e *env) Xstrchr(s, c int32) int32 {
|
||||
s = e.Xstrchrnul(s, c)
|
||||
if e.Buf[s] == byte(c) {
|
||||
return s
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *env) Xstrchrnul(s, c int32) int32 {
|
||||
m := e.Buf[s:]
|
||||
m = m[:bytes.IndexByte(m, 0)]
|
||||
b := byte(c)
|
||||
l := len(m)
|
||||
if c != 0 {
|
||||
i := bytes.IndexByte(m, b)
|
||||
if i >= 0 {
|
||||
l = i
|
||||
}
|
||||
}
|
||||
return s + int32(l)
|
||||
}
|
||||
|
||||
func (e *env) Xstrrchr(s, c int32) int32 {
|
||||
m := e.Buf[s:]
|
||||
m = m[:bytes.IndexByte(m, 0)+1]
|
||||
if i := bytes.LastIndexByte(m, byte(c)); i >= 0 {
|
||||
return s + int32(i)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *env) Xstrcmp(s1, s2 int32) int32 {
|
||||
m1 := e.Buf[s1:]
|
||||
m2 := e.Buf[s2:]
|
||||
m1 = m1[:bytes.IndexByte(m1, 0)]
|
||||
m2 = m2[:bytes.IndexByte(m2, 0)]
|
||||
return int32(bytes.Compare(m1, m2))
|
||||
}
|
||||
|
||||
func (e *env) Xstrncmp(s1, s2, n int32) int32 {
|
||||
m1 := e.Buf[s1:]
|
||||
m2 := e.Buf[s2:]
|
||||
m1 = m1[:bytes.IndexByte(m1, 0)]
|
||||
m2 = m2[:bytes.IndexByte(m2, 0)]
|
||||
if len(m1) > int(n) {
|
||||
m1 = m1[:n]
|
||||
}
|
||||
if len(m2) > int(n) {
|
||||
m2 = m2[:n]
|
||||
}
|
||||
return int32(bytes.Compare(m1, m2))
|
||||
}
|
||||
|
||||
func (e *env) Xstrspn(s, accept int32) int32 {
|
||||
m := e.Buf[s:]
|
||||
a := e.Buf[accept:]
|
||||
a = a[:bytes.IndexByte(a, 0)]
|
||||
|
||||
i := int32(0)
|
||||
for _, b := range m {
|
||||
if bytes.IndexByte(a, b) == -1 {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (e *env) Xstrcspn(s, reject int32) int32 {
|
||||
m := e.Buf[s:]
|
||||
r := e.Buf[reject:]
|
||||
r = r[:bytes.IndexByte(r, 0)]
|
||||
|
||||
i := int32(0)
|
||||
for _, b := range m {
|
||||
if b == 0 || bytes.IndexByte(r, b) != -1 {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (e *env) Xstrstr(haystack, needle int32) int32 {
|
||||
h := e.Buf[haystack:]
|
||||
n := e.Buf[needle:]
|
||||
h = h[:bytes.IndexByte(h, 0)]
|
||||
n = n[:bytes.IndexByte(n, 0)]
|
||||
i := bytes.Index(h, n)
|
||||
if i < 0 {
|
||||
return 0
|
||||
}
|
||||
return haystack + int32(i)
|
||||
}
|
||||
|
||||
func (e *env) Xstrcpy(d, s int32) int32 {
|
||||
m := e.Buf[s:]
|
||||
m = m[:bytes.IndexByte(m, 0)+1]
|
||||
copy(e.Buf[d:], m)
|
||||
return d
|
||||
}
|
||||
|
||||
// VFS functions.
|
||||
|
||||
func (e *env) Xgo_randomness(pVfs, nByte, zByte int32) int32 {
|
||||
@@ -217,23 +67,6 @@ func (e *env) Xgo_current_time_64(pVfs, nMicro int32) int32 {
|
||||
return int32(_OK)
|
||||
}
|
||||
|
||||
func (e *env) Xgo_localtime(pTm int32, t int64) int32 {
|
||||
const size = 32 / 8
|
||||
mem := e.Memory
|
||||
tm := time.Unix(t, 0)
|
||||
// https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html
|
||||
mem.Write32(ptr_t(pTm+0*size), uint32(tm.Second()))
|
||||
mem.Write32(ptr_t(pTm+1*size), uint32(tm.Minute()))
|
||||
mem.Write32(ptr_t(pTm+2*size), uint32(tm.Hour()))
|
||||
mem.Write32(ptr_t(pTm+3*size), uint32(tm.Day()))
|
||||
mem.Write32(ptr_t(pTm+4*size), uint32(tm.Month()-time.January))
|
||||
mem.Write32(ptr_t(pTm+5*size), uint32(tm.Year()-1900))
|
||||
mem.Write32(ptr_t(pTm+6*size), uint32(tm.Weekday()-time.Sunday))
|
||||
mem.Write32(ptr_t(pTm+7*size), uint32(tm.YearDay()-1))
|
||||
mem.WriteBool(ptr_t(pTm+8*size), tm.IsDST())
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (e *env) Xgo_vfs_find(zVfsName int32) int32 {
|
||||
if vfs.Find(e.ReadString(ptr_t(zVfsName), _MAX_NAME)) != nil {
|
||||
return 1
|
||||
|
||||
Generated
Vendored
Generated
-29
@@ -1,29 +0,0 @@
|
||||
#### joe made this: http://goel.io/joe
|
||||
|
||||
#####=== Go ===#####
|
||||
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.10.x
|
||||
install:
|
||||
- go get -v github.com/golang/lint/golint
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get -d -t -v ./...
|
||||
- go build -v ./...
|
||||
script:
|
||||
- go vet ./...
|
||||
- $HOME/gopath/bin/golint .
|
||||
- go test -v -race ./...
|
||||
- go test -v -covermode=count -coverprofile=cov.out
|
||||
- $HOME/gopath/bin/goveralls -coverprofile=cov.out -service=travis-ci -repotoken "$COVERALLS_TOKEN" || true
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
- Peter Bourgon (@peterbourgon)
|
||||
- Tomás Senart (@tsenart)
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
## 1.3.1 / 2018-10-02
|
||||
|
||||
* Use underlying entropy source for random increments in Monotonic (#32)
|
||||
|
||||
## 1.3.0 / 2018-09-29
|
||||
|
||||
* Monotonic entropy support (#31)
|
||||
|
||||
## 1.2.0 / 2018-09-09
|
||||
|
||||
* Add a function to convert Unix time in milliseconds back to time.Time (#30)
|
||||
|
||||
## 1.1.0 / 2018-08-15
|
||||
|
||||
* Ensure random part is always read from the entropy reader in full (#28)
|
||||
|
||||
## 1.0.0 / 2018-07-29
|
||||
|
||||
* Add ParseStrict and MustParseStrict functions (#26)
|
||||
* Enforce overflow checking when parsing (#20)
|
||||
|
||||
## 0.3.0 / 2017-01-03
|
||||
|
||||
* Implement ULID.Compare method
|
||||
|
||||
## 0.2.0 / 2016-12-13
|
||||
|
||||
* Remove year 2262 Timestamp bug. (#1)
|
||||
* Gracefully handle invalid encodings when parsing.
|
||||
|
||||
## 0.1.0 / 2016-12-06
|
||||
|
||||
* First ULID release
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
We use GitHub to manage reviews of pull requests.
|
||||
|
||||
* If you have a trivial fix or improvement, go ahead and create a pull
|
||||
request, addressing (with `@...`) one or more of the maintainers
|
||||
(see [AUTHORS.md](AUTHORS.md)) in the description of the pull request.
|
||||
|
||||
* If you plan to do something more involved, first propose your ideas
|
||||
in a Github issue. This will avoid unnecessary work and surely give
|
||||
you and us a good deal of inspiration.
|
||||
|
||||
* Relevant coding style guidelines are the [Go Code Review
|
||||
Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments)
|
||||
and the _Formatting and style_ section of Peter Bourgon's [Go: Best
|
||||
Practices for Production
|
||||
Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style).
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/pborman/getopt"
|
||||
packages = ["v2"]
|
||||
revision = "7148bc3a4c3008adfcab60cbebfd0576018f330b"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "6779b05abd5cd429c5393641d2453005a3cb74a400d161b2b5c5d0ca2e10e116"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/pborman/getopt"
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
# Universally Unique Lexicographically Sortable Identifier
|
||||
|
||||

|
||||
[](http://travis-ci.org/oklog/ulid)
|
||||
[](https://goreportcard.com/report/oklog/ulid)
|
||||
[](https://coveralls.io/github/oklog/ulid?branch=master)
|
||||
[](https://godoc.org/github.com/oklog/ulid)
|
||||
[](https://raw.githubusercontent.com/oklog/ulid/master/LICENSE)
|
||||
|
||||
A Go port of [alizain/ulid](https://github.com/alizain/ulid) with binary format implemented.
|
||||
|
||||
## Background
|
||||
|
||||
A GUID/UUID can be suboptimal for many use-cases because:
|
||||
|
||||
- It isn't the most character efficient way of encoding 128 bits
|
||||
- UUID v1/v2 is impractical in many environments, as it requires access to a unique, stable MAC address
|
||||
- UUID v3/v5 requires a unique seed and produces randomly distributed IDs, which can cause fragmentation in many data structures
|
||||
- UUID v4 provides no other information than randomness which can cause fragmentation in many data structures
|
||||
|
||||
A ULID however:
|
||||
|
||||
- Is compatible with UUID/GUID's
|
||||
- 1.21e+24 unique ULIDs per millisecond (1,208,925,819,614,629,174,706,176 to be exact)
|
||||
- Lexicographically sortable
|
||||
- Canonically encoded as a 26 character string, as opposed to the 36 character UUID
|
||||
- Uses Crockford's base32 for better efficiency and readability (5 bits per character)
|
||||
- Case insensitive
|
||||
- No special characters (URL safe)
|
||||
- Monotonic sort order (correctly detects and handles the same millisecond)
|
||||
|
||||
## Install
|
||||
|
||||
```shell
|
||||
go get github.com/oklog/ulid
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
An ULID is constructed with a `time.Time` and an `io.Reader` entropy source.
|
||||
This design allows for greater flexibility in choosing your trade-offs.
|
||||
|
||||
Please note that `rand.Rand` from the `math` package is *not* safe for concurrent use.
|
||||
Instantiate one per long living go-routine or use a `sync.Pool` if you want to avoid the potential contention of a locked `rand.Source` as its been frequently observed in the package level functions.
|
||||
|
||||
|
||||
```go
|
||||
func ExampleULID() {
|
||||
t := time.Unix(1000000, 0)
|
||||
entropy := ulid.Monotonic(rand.New(rand.NewSource(t.UnixNano())), 0)
|
||||
fmt.Println(ulid.MustNew(ulid.Timestamp(t), entropy))
|
||||
// Output: 0000XSNJG0MQJHBF4QX1EFD6Y3
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Specification
|
||||
|
||||
Below is the current specification of ULID as implemented in this repository.
|
||||
|
||||
### Components
|
||||
|
||||
**Timestamp**
|
||||
- 48 bits
|
||||
- UNIX-time in milliseconds
|
||||
- Won't run out of space till the year 10895 AD
|
||||
|
||||
**Entropy**
|
||||
- 80 bits
|
||||
- User defined entropy source.
|
||||
- Monotonicity within the same millisecond with [`ulid.Monotonic`](https://godoc.org/github.com/oklog/ulid#Monotonic)
|
||||
|
||||
### Encoding
|
||||
|
||||
[Crockford's Base32](http://www.crockford.com/wrmg/base32.html) is used as shown.
|
||||
This alphabet excludes the letters I, L, O, and U to avoid confusion and abuse.
|
||||
|
||||
```
|
||||
0123456789ABCDEFGHJKMNPQRSTVWXYZ
|
||||
```
|
||||
|
||||
### Binary Layout and Byte Order
|
||||
|
||||
The components are encoded as 16 octets. Each component is encoded with the Most Significant Byte first (network byte order).
|
||||
|
||||
```
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_time_high |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 16_bit_uint_time_low | 16_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
```
|
||||
|
||||
### String Representation
|
||||
|
||||
```
|
||||
01AN4Z07BY 79KA1307SR9X4MV3
|
||||
|----------| |----------------|
|
||||
Timestamp Entropy
|
||||
10 chars 16 chars
|
||||
48bits 80bits
|
||||
base32 base32
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```shell
|
||||
go test ./...
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
On a Intel Core i7 Ivy Bridge 2.7 GHz, MacOS 10.12.1 and Go 1.8.0beta1
|
||||
|
||||
```
|
||||
BenchmarkNew/WithCryptoEntropy-8 2000000 771 ns/op 20.73 MB/s 16 B/op 1 allocs/op
|
||||
BenchmarkNew/WithEntropy-8 20000000 65.8 ns/op 243.01 MB/s 16 B/op 1 allocs/op
|
||||
BenchmarkNew/WithoutEntropy-8 50000000 30.0 ns/op 534.06 MB/s 16 B/op 1 allocs/op
|
||||
BenchmarkMustNew/WithCryptoEntropy-8 2000000 781 ns/op 20.48 MB/s 16 B/op 1 allocs/op
|
||||
BenchmarkMustNew/WithEntropy-8 20000000 70.0 ns/op 228.51 MB/s 16 B/op 1 allocs/op
|
||||
BenchmarkMustNew/WithoutEntropy-8 50000000 34.6 ns/op 462.98 MB/s 16 B/op 1 allocs/op
|
||||
BenchmarkParse-8 50000000 30.0 ns/op 866.16 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMustParse-8 50000000 35.2 ns/op 738.94 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString-8 20000000 64.9 ns/op 246.40 MB/s 32 B/op 1 allocs/op
|
||||
BenchmarkMarshal/Text-8 20000000 55.8 ns/op 286.84 MB/s 32 B/op 1 allocs/op
|
||||
BenchmarkMarshal/TextTo-8 100000000 22.4 ns/op 714.91 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMarshal/Binary-8 300000000 4.02 ns/op 3981.77 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkMarshal/BinaryTo-8 2000000000 1.18 ns/op 13551.75 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkUnmarshal/Text-8 100000000 20.5 ns/op 1265.27 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkUnmarshal/Binary-8 300000000 4.94 ns/op 3240.01 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkNow-8 100000000 15.1 ns/op 528.09 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkTimestamp-8 2000000000 0.29 ns/op 27271.59 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkTime-8 2000000000 0.58 ns/op 13717.80 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkSetTime-8 2000000000 0.89 ns/op 9023.95 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkEntropy-8 200000000 7.62 ns/op 1311.66 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkSetEntropy-8 2000000000 0.88 ns/op 11376.54 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkCompare-8 200000000 7.34 ns/op 4359.23 MB/s 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
## Prior Art
|
||||
|
||||
- [alizain/ulid](https://github.com/alizain/ulid)
|
||||
- [RobThree/NUlid](https://github.com/RobThree/NUlid)
|
||||
- [imdario/go-ulid](https://github.com/imdario/go-ulid)
|
||||
-614
@@ -1,614 +0,0 @@
|
||||
// Copyright 2016 The Oklog Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ulid
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
An ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier
|
||||
|
||||
The components are encoded as 16 octets.
|
||||
Each component is encoded with the MSB first (network byte order).
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_time_high |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 16_bit_uint_time_low | 16_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 32_bit_uint_random |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
type ULID [16]byte
|
||||
|
||||
var (
|
||||
// ErrDataSize is returned when parsing or unmarshaling ULIDs with the wrong
|
||||
// data size.
|
||||
ErrDataSize = errors.New("ulid: bad data size when unmarshaling")
|
||||
|
||||
// ErrInvalidCharacters is returned when parsing or unmarshaling ULIDs with
|
||||
// invalid Base32 encodings.
|
||||
ErrInvalidCharacters = errors.New("ulid: bad data characters when unmarshaling")
|
||||
|
||||
// ErrBufferSize is returned when marshalling ULIDs to a buffer of insufficient
|
||||
// size.
|
||||
ErrBufferSize = errors.New("ulid: bad buffer size when marshaling")
|
||||
|
||||
// ErrBigTime is returned when constructing an ULID with a time that is larger
|
||||
// than MaxTime.
|
||||
ErrBigTime = errors.New("ulid: time too big")
|
||||
|
||||
// ErrOverflow is returned when unmarshaling a ULID whose first character is
|
||||
// larger than 7, thereby exceeding the valid bit depth of 128.
|
||||
ErrOverflow = errors.New("ulid: overflow when unmarshaling")
|
||||
|
||||
// ErrMonotonicOverflow is returned by a Monotonic entropy source when
|
||||
// incrementing the previous ULID's entropy bytes would result in overflow.
|
||||
ErrMonotonicOverflow = errors.New("ulid: monotonic entropy overflow")
|
||||
|
||||
// ErrScanValue is returned when the value passed to scan cannot be unmarshaled
|
||||
// into the ULID.
|
||||
ErrScanValue = errors.New("ulid: source value must be a string or byte slice")
|
||||
)
|
||||
|
||||
// New returns an ULID with the given Unix milliseconds timestamp and an
|
||||
// optional entropy source. Use the Timestamp function to convert
|
||||
// a time.Time to Unix milliseconds.
|
||||
//
|
||||
// ErrBigTime is returned when passing a timestamp bigger than MaxTime.
|
||||
// Reading from the entropy source may also return an error.
|
||||
func New(ms uint64, entropy io.Reader) (id ULID, err error) {
|
||||
if err = id.SetTime(ms); err != nil {
|
||||
return id, err
|
||||
}
|
||||
|
||||
switch e := entropy.(type) {
|
||||
case nil:
|
||||
return id, err
|
||||
case *monotonic:
|
||||
err = e.MonotonicRead(ms, id[6:])
|
||||
default:
|
||||
_, err = io.ReadFull(e, id[6:])
|
||||
}
|
||||
|
||||
return id, err
|
||||
}
|
||||
|
||||
// MustNew is a convenience function equivalent to New that panics on failure
|
||||
// instead of returning an error.
|
||||
func MustNew(ms uint64, entropy io.Reader) ULID {
|
||||
id, err := New(ms, entropy)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// Parse parses an encoded ULID, returning an error in case of failure.
|
||||
//
|
||||
// ErrDataSize is returned if the len(ulid) is different from an encoded
|
||||
// ULID's length. Invalid encodings produce undefined ULIDs. For a version that
|
||||
// returns an error instead, see ParseStrict.
|
||||
func Parse(ulid string) (id ULID, err error) {
|
||||
return id, parse([]byte(ulid), false, &id)
|
||||
}
|
||||
|
||||
// ParseStrict parses an encoded ULID, returning an error in case of failure.
|
||||
//
|
||||
// It is like Parse, but additionally validates that the parsed ULID consists
|
||||
// only of valid base32 characters. It is slightly slower than Parse.
|
||||
//
|
||||
// ErrDataSize is returned if the len(ulid) is different from an encoded
|
||||
// ULID's length. Invalid encodings return ErrInvalidCharacters.
|
||||
func ParseStrict(ulid string) (id ULID, err error) {
|
||||
return id, parse([]byte(ulid), true, &id)
|
||||
}
|
||||
|
||||
func parse(v []byte, strict bool, id *ULID) error {
|
||||
// Check if a base32 encoded ULID is the right length.
|
||||
if len(v) != EncodedSize {
|
||||
return ErrDataSize
|
||||
}
|
||||
|
||||
// Check if all the characters in a base32 encoded ULID are part of the
|
||||
// expected base32 character set.
|
||||
if strict &&
|
||||
(dec[v[0]] == 0xFF ||
|
||||
dec[v[1]] == 0xFF ||
|
||||
dec[v[2]] == 0xFF ||
|
||||
dec[v[3]] == 0xFF ||
|
||||
dec[v[4]] == 0xFF ||
|
||||
dec[v[5]] == 0xFF ||
|
||||
dec[v[6]] == 0xFF ||
|
||||
dec[v[7]] == 0xFF ||
|
||||
dec[v[8]] == 0xFF ||
|
||||
dec[v[9]] == 0xFF ||
|
||||
dec[v[10]] == 0xFF ||
|
||||
dec[v[11]] == 0xFF ||
|
||||
dec[v[12]] == 0xFF ||
|
||||
dec[v[13]] == 0xFF ||
|
||||
dec[v[14]] == 0xFF ||
|
||||
dec[v[15]] == 0xFF ||
|
||||
dec[v[16]] == 0xFF ||
|
||||
dec[v[17]] == 0xFF ||
|
||||
dec[v[18]] == 0xFF ||
|
||||
dec[v[19]] == 0xFF ||
|
||||
dec[v[20]] == 0xFF ||
|
||||
dec[v[21]] == 0xFF ||
|
||||
dec[v[22]] == 0xFF ||
|
||||
dec[v[23]] == 0xFF ||
|
||||
dec[v[24]] == 0xFF ||
|
||||
dec[v[25]] == 0xFF) {
|
||||
return ErrInvalidCharacters
|
||||
}
|
||||
|
||||
// Check if the first character in a base32 encoded ULID will overflow. This
|
||||
// happens because the base32 representation encodes 130 bits, while the
|
||||
// ULID is only 128 bits.
|
||||
//
|
||||
// See https://github.com/oklog/ulid/issues/9 for details.
|
||||
if v[0] > '7' {
|
||||
return ErrOverflow
|
||||
}
|
||||
|
||||
// Use an optimized unrolled loop (from https://github.com/RobThree/NUlid)
|
||||
// to decode a base32 ULID.
|
||||
|
||||
// 6 bytes timestamp (48 bits)
|
||||
(*id)[0] = ((dec[v[0]] << 5) | dec[v[1]])
|
||||
(*id)[1] = ((dec[v[2]] << 3) | (dec[v[3]] >> 2))
|
||||
(*id)[2] = ((dec[v[3]] << 6) | (dec[v[4]] << 1) | (dec[v[5]] >> 4))
|
||||
(*id)[3] = ((dec[v[5]] << 4) | (dec[v[6]] >> 1))
|
||||
(*id)[4] = ((dec[v[6]] << 7) | (dec[v[7]] << 2) | (dec[v[8]] >> 3))
|
||||
(*id)[5] = ((dec[v[8]] << 5) | dec[v[9]])
|
||||
|
||||
// 10 bytes of entropy (80 bits)
|
||||
(*id)[6] = ((dec[v[10]] << 3) | (dec[v[11]] >> 2))
|
||||
(*id)[7] = ((dec[v[11]] << 6) | (dec[v[12]] << 1) | (dec[v[13]] >> 4))
|
||||
(*id)[8] = ((dec[v[13]] << 4) | (dec[v[14]] >> 1))
|
||||
(*id)[9] = ((dec[v[14]] << 7) | (dec[v[15]] << 2) | (dec[v[16]] >> 3))
|
||||
(*id)[10] = ((dec[v[16]] << 5) | dec[v[17]])
|
||||
(*id)[11] = ((dec[v[18]] << 3) | dec[v[19]]>>2)
|
||||
(*id)[12] = ((dec[v[19]] << 6) | (dec[v[20]] << 1) | (dec[v[21]] >> 4))
|
||||
(*id)[13] = ((dec[v[21]] << 4) | (dec[v[22]] >> 1))
|
||||
(*id)[14] = ((dec[v[22]] << 7) | (dec[v[23]] << 2) | (dec[v[24]] >> 3))
|
||||
(*id)[15] = ((dec[v[24]] << 5) | dec[v[25]])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustParse is a convenience function equivalent to Parse that panics on failure
|
||||
// instead of returning an error.
|
||||
func MustParse(ulid string) ULID {
|
||||
id, err := Parse(ulid)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// MustParseStrict is a convenience function equivalent to ParseStrict that
|
||||
// panics on failure instead of returning an error.
|
||||
func MustParseStrict(ulid string) ULID {
|
||||
id, err := ParseStrict(ulid)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// String returns a lexicographically sortable string encoded ULID
|
||||
// (26 characters, non-standard base 32) e.g. 01AN4Z07BY79KA1307SR9X4MV3
|
||||
// Format: tttttttttteeeeeeeeeeeeeeee where t is time and e is entropy
|
||||
func (id ULID) String() string {
|
||||
ulid := make([]byte, EncodedSize)
|
||||
_ = id.MarshalTextTo(ulid)
|
||||
return string(ulid)
|
||||
}
|
||||
|
||||
// MarshalBinary implements the encoding.BinaryMarshaler interface by
|
||||
// returning the ULID as a byte slice.
|
||||
func (id ULID) MarshalBinary() ([]byte, error) {
|
||||
ulid := make([]byte, len(id))
|
||||
return ulid, id.MarshalBinaryTo(ulid)
|
||||
}
|
||||
|
||||
// MarshalBinaryTo writes the binary encoding of the ULID to the given buffer.
|
||||
// ErrBufferSize is returned when the len(dst) != 16.
|
||||
func (id ULID) MarshalBinaryTo(dst []byte) error {
|
||||
if len(dst) != len(id) {
|
||||
return ErrBufferSize
|
||||
}
|
||||
|
||||
copy(dst, id[:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface by
|
||||
// copying the passed data and converting it to an ULID. ErrDataSize is
|
||||
// returned if the data length is different from ULID length.
|
||||
func (id *ULID) UnmarshalBinary(data []byte) error {
|
||||
if len(data) != len(*id) {
|
||||
return ErrDataSize
|
||||
}
|
||||
|
||||
copy((*id)[:], data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encoding is the base 32 encoding alphabet used in ULID strings.
|
||||
const Encoding = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
||||
|
||||
// MarshalText implements the encoding.TextMarshaler interface by
|
||||
// returning the string encoded ULID.
|
||||
func (id ULID) MarshalText() ([]byte, error) {
|
||||
ulid := make([]byte, EncodedSize)
|
||||
return ulid, id.MarshalTextTo(ulid)
|
||||
}
|
||||
|
||||
// MarshalTextTo writes the ULID as a string to the given buffer.
|
||||
// ErrBufferSize is returned when the len(dst) != 26.
|
||||
func (id ULID) MarshalTextTo(dst []byte) error {
|
||||
// Optimized unrolled loop ahead.
|
||||
// From https://github.com/RobThree/NUlid
|
||||
|
||||
if len(dst) != EncodedSize {
|
||||
return ErrBufferSize
|
||||
}
|
||||
|
||||
// 10 byte timestamp
|
||||
dst[0] = Encoding[(id[0]&224)>>5]
|
||||
dst[1] = Encoding[id[0]&31]
|
||||
dst[2] = Encoding[(id[1]&248)>>3]
|
||||
dst[3] = Encoding[((id[1]&7)<<2)|((id[2]&192)>>6)]
|
||||
dst[4] = Encoding[(id[2]&62)>>1]
|
||||
dst[5] = Encoding[((id[2]&1)<<4)|((id[3]&240)>>4)]
|
||||
dst[6] = Encoding[((id[3]&15)<<1)|((id[4]&128)>>7)]
|
||||
dst[7] = Encoding[(id[4]&124)>>2]
|
||||
dst[8] = Encoding[((id[4]&3)<<3)|((id[5]&224)>>5)]
|
||||
dst[9] = Encoding[id[5]&31]
|
||||
|
||||
// 16 bytes of entropy
|
||||
dst[10] = Encoding[(id[6]&248)>>3]
|
||||
dst[11] = Encoding[((id[6]&7)<<2)|((id[7]&192)>>6)]
|
||||
dst[12] = Encoding[(id[7]&62)>>1]
|
||||
dst[13] = Encoding[((id[7]&1)<<4)|((id[8]&240)>>4)]
|
||||
dst[14] = Encoding[((id[8]&15)<<1)|((id[9]&128)>>7)]
|
||||
dst[15] = Encoding[(id[9]&124)>>2]
|
||||
dst[16] = Encoding[((id[9]&3)<<3)|((id[10]&224)>>5)]
|
||||
dst[17] = Encoding[id[10]&31]
|
||||
dst[18] = Encoding[(id[11]&248)>>3]
|
||||
dst[19] = Encoding[((id[11]&7)<<2)|((id[12]&192)>>6)]
|
||||
dst[20] = Encoding[(id[12]&62)>>1]
|
||||
dst[21] = Encoding[((id[12]&1)<<4)|((id[13]&240)>>4)]
|
||||
dst[22] = Encoding[((id[13]&15)<<1)|((id[14]&128)>>7)]
|
||||
dst[23] = Encoding[(id[14]&124)>>2]
|
||||
dst[24] = Encoding[((id[14]&3)<<3)|((id[15]&224)>>5)]
|
||||
dst[25] = Encoding[id[15]&31]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Byte to index table for O(1) lookups when unmarshaling.
|
||||
// We use 0xFF as sentinel value for invalid indexes.
|
||||
var dec = [...]byte{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,
|
||||
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF,
|
||||
0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E,
|
||||
0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C,
|
||||
0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14,
|
||||
0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C,
|
||||
0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
}
|
||||
|
||||
// EncodedSize is the length of a text encoded ULID.
|
||||
const EncodedSize = 26
|
||||
|
||||
// UnmarshalText implements the encoding.TextUnmarshaler interface by
|
||||
// parsing the data as string encoded ULID.
|
||||
//
|
||||
// ErrDataSize is returned if the len(v) is different from an encoded
|
||||
// ULID's length. Invalid encodings produce undefined ULIDs.
|
||||
func (id *ULID) UnmarshalText(v []byte) error {
|
||||
return parse(v, false, id)
|
||||
}
|
||||
|
||||
// Time returns the Unix time in milliseconds encoded in the ULID.
|
||||
// Use the top level Time function to convert the returned value to
|
||||
// a time.Time.
|
||||
func (id ULID) Time() uint64 {
|
||||
return uint64(id[5]) | uint64(id[4])<<8 |
|
||||
uint64(id[3])<<16 | uint64(id[2])<<24 |
|
||||
uint64(id[1])<<32 | uint64(id[0])<<40
|
||||
}
|
||||
|
||||
// maxTime is the maximum Unix time in milliseconds that can be
|
||||
// represented in an ULID.
|
||||
var maxTime = ULID{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}.Time()
|
||||
|
||||
// MaxTime returns the maximum Unix time in milliseconds that
|
||||
// can be encoded in an ULID.
|
||||
func MaxTime() uint64 { return maxTime }
|
||||
|
||||
// Now is a convenience function that returns the current
|
||||
// UTC time in Unix milliseconds. Equivalent to:
|
||||
// Timestamp(time.Now().UTC())
|
||||
func Now() uint64 { return Timestamp(time.Now().UTC()) }
|
||||
|
||||
// Timestamp converts a time.Time to Unix milliseconds.
|
||||
//
|
||||
// Because of the way ULID stores time, times from the year
|
||||
// 10889 produces undefined results.
|
||||
func Timestamp(t time.Time) uint64 {
|
||||
return uint64(t.Unix())*1000 +
|
||||
uint64(t.Nanosecond()/int(time.Millisecond))
|
||||
}
|
||||
|
||||
// Time converts Unix milliseconds in the format
|
||||
// returned by the Timestamp function to a time.Time.
|
||||
func Time(ms uint64) time.Time {
|
||||
s := int64(ms / 1e3)
|
||||
ns := int64((ms % 1e3) * 1e6)
|
||||
return time.Unix(s, ns)
|
||||
}
|
||||
|
||||
// SetTime sets the time component of the ULID to the given Unix time
|
||||
// in milliseconds.
|
||||
func (id *ULID) SetTime(ms uint64) error {
|
||||
if ms > maxTime {
|
||||
return ErrBigTime
|
||||
}
|
||||
|
||||
(*id)[0] = byte(ms >> 40)
|
||||
(*id)[1] = byte(ms >> 32)
|
||||
(*id)[2] = byte(ms >> 24)
|
||||
(*id)[3] = byte(ms >> 16)
|
||||
(*id)[4] = byte(ms >> 8)
|
||||
(*id)[5] = byte(ms)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Entropy returns the entropy from the ULID.
|
||||
func (id ULID) Entropy() []byte {
|
||||
e := make([]byte, 10)
|
||||
copy(e, id[6:])
|
||||
return e
|
||||
}
|
||||
|
||||
// SetEntropy sets the ULID entropy to the passed byte slice.
|
||||
// ErrDataSize is returned if len(e) != 10.
|
||||
func (id *ULID) SetEntropy(e []byte) error {
|
||||
if len(e) != 10 {
|
||||
return ErrDataSize
|
||||
}
|
||||
|
||||
copy((*id)[6:], e)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compare returns an integer comparing id and other lexicographically.
|
||||
// The result will be 0 if id==other, -1 if id < other, and +1 if id > other.
|
||||
func (id ULID) Compare(other ULID) int {
|
||||
return bytes.Compare(id[:], other[:])
|
||||
}
|
||||
|
||||
// Scan implements the sql.Scanner interface. It supports scanning
|
||||
// a string or byte slice.
|
||||
func (id *ULID) Scan(src interface{}) error {
|
||||
switch x := src.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case string:
|
||||
return id.UnmarshalText([]byte(x))
|
||||
case []byte:
|
||||
return id.UnmarshalBinary(x)
|
||||
}
|
||||
|
||||
return ErrScanValue
|
||||
}
|
||||
|
||||
// Value implements the sql/driver.Valuer interface. This returns the value
|
||||
// represented as a byte slice. If instead a string is desirable, a wrapper
|
||||
// type can be created that calls String().
|
||||
//
|
||||
// // stringValuer wraps a ULID as a string-based driver.Valuer.
|
||||
// type stringValuer ULID
|
||||
//
|
||||
// func (id stringValuer) Value() (driver.Value, error) {
|
||||
// return ULID(id).String(), nil
|
||||
// }
|
||||
//
|
||||
// // Example usage.
|
||||
// db.Exec("...", stringValuer(id))
|
||||
func (id ULID) Value() (driver.Value, error) {
|
||||
return id.MarshalBinary()
|
||||
}
|
||||
|
||||
// Monotonic returns an entropy source that is guaranteed to yield
|
||||
// strictly increasing entropy bytes for the same ULID timestamp.
|
||||
// On conflicts, the previous ULID entropy is incremented with a
|
||||
// random number between 1 and `inc` (inclusive).
|
||||
//
|
||||
// The provided entropy source must actually yield random bytes or else
|
||||
// monotonic reads are not guaranteed to terminate, since there isn't
|
||||
// enough randomness to compute an increment number.
|
||||
//
|
||||
// When `inc == 0`, it'll be set to a secure default of `math.MaxUint32`.
|
||||
// The lower the value of `inc`, the easier the next ULID within the
|
||||
// same millisecond is to guess. If your code depends on ULIDs having
|
||||
// secure entropy bytes, then don't go under this default unless you know
|
||||
// what you're doing.
|
||||
//
|
||||
// The returned io.Reader isn't safe for concurrent use.
|
||||
func Monotonic(entropy io.Reader, inc uint64) io.Reader {
|
||||
m := monotonic{
|
||||
Reader: bufio.NewReader(entropy),
|
||||
inc: inc,
|
||||
}
|
||||
|
||||
if m.inc == 0 {
|
||||
m.inc = math.MaxUint32
|
||||
}
|
||||
|
||||
if rng, ok := entropy.(*rand.Rand); ok {
|
||||
m.rng = rng
|
||||
}
|
||||
|
||||
return &m
|
||||
}
|
||||
|
||||
type monotonic struct {
|
||||
io.Reader
|
||||
ms uint64
|
||||
inc uint64
|
||||
entropy uint80
|
||||
rand [8]byte
|
||||
rng *rand.Rand
|
||||
}
|
||||
|
||||
func (m *monotonic) MonotonicRead(ms uint64, entropy []byte) (err error) {
|
||||
if !m.entropy.IsZero() && m.ms == ms {
|
||||
err = m.increment()
|
||||
m.entropy.AppendTo(entropy)
|
||||
} else if _, err = io.ReadFull(m.Reader, entropy); err == nil {
|
||||
m.ms = ms
|
||||
m.entropy.SetBytes(entropy)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// increment the previous entropy number with a random number
|
||||
// of up to m.inc (inclusive).
|
||||
func (m *monotonic) increment() error {
|
||||
if inc, err := m.random(); err != nil {
|
||||
return err
|
||||
} else if m.entropy.Add(inc) {
|
||||
return ErrMonotonicOverflow
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// random returns a uniform random value in [1, m.inc), reading entropy
|
||||
// from m.Reader. When m.inc == 0 || m.inc == 1, it returns 1.
|
||||
// Adapted from: https://golang.org/pkg/crypto/rand/#Int
|
||||
func (m *monotonic) random() (inc uint64, err error) {
|
||||
if m.inc <= 1 {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
// Fast path for using a underlying rand.Rand directly.
|
||||
if m.rng != nil {
|
||||
// Range: [1, m.inc)
|
||||
return 1 + uint64(m.rng.Int63n(int64(m.inc))), nil
|
||||
}
|
||||
|
||||
// bitLen is the maximum bit length needed to encode a value < m.inc.
|
||||
bitLen := bits.Len64(m.inc)
|
||||
|
||||
// byteLen is the maximum byte length needed to encode a value < m.inc.
|
||||
byteLen := uint(bitLen+7) / 8
|
||||
|
||||
// msbitLen is the number of bits in the most significant byte of m.inc-1.
|
||||
msbitLen := uint(bitLen % 8)
|
||||
if msbitLen == 0 {
|
||||
msbitLen = 8
|
||||
}
|
||||
|
||||
for inc == 0 || inc >= m.inc {
|
||||
if _, err = io.ReadFull(m.Reader, m.rand[:byteLen]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Clear bits in the first byte to increase the probability
|
||||
// that the candidate is < m.inc.
|
||||
m.rand[0] &= uint8(int(1<<msbitLen) - 1)
|
||||
|
||||
// Convert the read bytes into an uint64 with byteLen
|
||||
// Optimized unrolled loop.
|
||||
switch byteLen {
|
||||
case 1:
|
||||
inc = uint64(m.rand[0])
|
||||
case 2:
|
||||
inc = uint64(binary.LittleEndian.Uint16(m.rand[:2]))
|
||||
case 3, 4:
|
||||
inc = uint64(binary.LittleEndian.Uint32(m.rand[:4]))
|
||||
case 5, 6, 7, 8:
|
||||
inc = uint64(binary.LittleEndian.Uint64(m.rand[:8]))
|
||||
}
|
||||
}
|
||||
|
||||
// Range: [1, m.inc)
|
||||
return 1 + inc, nil
|
||||
}
|
||||
|
||||
type uint80 struct {
|
||||
Hi uint16
|
||||
Lo uint64
|
||||
}
|
||||
|
||||
func (u *uint80) SetBytes(bs []byte) {
|
||||
u.Hi = binary.BigEndian.Uint16(bs[:2])
|
||||
u.Lo = binary.BigEndian.Uint64(bs[2:])
|
||||
}
|
||||
|
||||
func (u *uint80) AppendTo(bs []byte) {
|
||||
binary.BigEndian.PutUint16(bs[:2], u.Hi)
|
||||
binary.BigEndian.PutUint64(bs[2:], u.Lo)
|
||||
}
|
||||
|
||||
func (u *uint80) Add(n uint64) (overflow bool) {
|
||||
lo, hi := u.Lo, u.Hi
|
||||
if u.Lo += n; u.Lo < lo {
|
||||
u.Hi++
|
||||
}
|
||||
return u.Hi < hi
|
||||
}
|
||||
|
||||
func (u uint80) IsZero() bool {
|
||||
return u.Hi == 0 && u.Lo == 0
|
||||
}
|
||||
Vendored
+3
-6
@@ -783,7 +783,7 @@ github.com/modern-go/reflect2
|
||||
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
|
||||
## explicit
|
||||
github.com/munnerz/goautoneg
|
||||
# github.com/ncruces/go-sqlite3 v0.33.3
|
||||
# github.com/ncruces/go-sqlite3 v0.34.0
|
||||
## explicit; go 1.25.0
|
||||
github.com/ncruces/go-sqlite3
|
||||
github.com/ncruces/go-sqlite3/driver
|
||||
@@ -795,18 +795,15 @@ github.com/ncruces/go-sqlite3/internal/util
|
||||
github.com/ncruces/go-sqlite3/util/vfsutil
|
||||
github.com/ncruces/go-sqlite3/vfs
|
||||
github.com/ncruces/go-sqlite3/vfs/memdb
|
||||
# github.com/ncruces/go-sqlite3-wasm v1.1.1-0.20260409221933-87e4b35a38d0
|
||||
# github.com/ncruces/go-sqlite3-wasm/v2 v2.1.35300
|
||||
## explicit; go 1.25.0
|
||||
github.com/ncruces/go-sqlite3-wasm
|
||||
github.com/ncruces/go-sqlite3-wasm/v2
|
||||
# github.com/ncruces/go-strftime v1.0.0
|
||||
## explicit; go 1.17
|
||||
github.com/ncruces/go-strftime
|
||||
# github.com/ncruces/julianday v1.0.0
|
||||
## explicit; go 1.17
|
||||
github.com/ncruces/julianday
|
||||
# github.com/oklog/ulid v1.3.1
|
||||
## explicit
|
||||
github.com/oklog/ulid
|
||||
# github.com/oklog/ulid/v2 v2.1.1
|
||||
## explicit; go 1.15
|
||||
github.com/oklog/ulid/v2
|
||||
|
||||
Reference in New Issue
Block a user