2023-01-12 05:57:09 +00:00
|
|
|
package sqlite3
|
2023-01-11 14:58:20 +00:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2023-02-24 15:06:19 +00:00
|
|
|
"fmt"
|
2023-02-10 16:42:49 +00:00
|
|
|
"math"
|
2023-02-24 15:06:19 +00:00
|
|
|
"runtime"
|
2023-02-24 15:19:57 +00:00
|
|
|
"sync"
|
2023-01-11 14:58:20 +00:00
|
|
|
)
|
|
|
|
|
|
2023-02-09 16:40:48 +00:00
|
|
|
// Conn is a database connection handle.
|
|
|
|
|
//
|
|
|
|
|
// https://www.sqlite.org/c3ref/sqlite3.html
|
2023-01-12 05:57:09 +00:00
|
|
|
type Conn struct {
|
2023-01-18 12:44:14 +00:00
|
|
|
ctx context.Context
|
2023-01-12 05:57:09 +00:00
|
|
|
api sqliteAPI
|
2023-01-28 12:47:39 +00:00
|
|
|
mem memory
|
|
|
|
|
handle uint32
|
2023-02-14 18:21:18 +00:00
|
|
|
|
2023-02-24 14:56:49 +00:00
|
|
|
arena arena
|
2023-02-24 15:19:57 +00:00
|
|
|
mtx sync.Mutex
|
2023-02-24 14:56:49 +00:00
|
|
|
interrupt context.Context
|
|
|
|
|
waiter chan struct{}
|
|
|
|
|
pending *Stmt
|
2023-01-11 14:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-09 16:40:48 +00:00
|
|
|
// Open calls [OpenFlags] with [OPEN_READWRITE] and [OPEN_CREATE].
|
2023-01-22 15:44:39 +00:00
|
|
|
func Open(filename string) (conn *Conn, err error) {
|
|
|
|
|
return OpenFlags(filename, OPEN_READWRITE|OPEN_CREATE)
|
2023-01-16 12:54:24 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-09 16:40:48 +00:00
|
|
|
// OpenFlags opens an SQLite database file as specified by the filename argument.
|
|
|
|
|
//
|
|
|
|
|
// https://www.sqlite.org/c3ref/open.html
|
2023-01-22 15:44:39 +00:00
|
|
|
func OpenFlags(filename string, flags OpenFlag) (conn *Conn, err error) {
|
2023-01-18 12:44:14 +00:00
|
|
|
ctx := context.Background()
|
2023-01-28 12:47:39 +00:00
|
|
|
module, err := sqlite3.instantiateModule(ctx)
|
2023-01-11 14:58:20 +00:00
|
|
|
if err != nil {
|
2023-01-12 05:57:09 +00:00
|
|
|
return nil, err
|
2023-01-11 14:58:20 +00:00
|
|
|
}
|
2023-01-12 13:43:35 +00:00
|
|
|
defer func() {
|
|
|
|
|
if conn == nil {
|
2023-01-17 13:43:16 +00:00
|
|
|
module.Close(ctx)
|
2023-01-12 13:43:35 +00:00
|
|
|
}
|
|
|
|
|
}()
|
2023-01-12 05:57:09 +00:00
|
|
|
|
2023-02-10 16:42:49 +00:00
|
|
|
c, err := newConn(ctx, module)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-02-14 11:34:24 +00:00
|
|
|
c.arena = c.newArena(1024)
|
2023-02-10 16:42:49 +00:00
|
|
|
|
2023-02-14 11:34:24 +00:00
|
|
|
defer c.arena.reset()
|
|
|
|
|
connPtr := c.arena.new(ptrlen)
|
|
|
|
|
namePtr := c.arena.string(filename)
|
2023-01-12 13:43:35 +00:00
|
|
|
|
2023-01-18 12:44:14 +00:00
|
|
|
r, err := c.api.open.Call(c.ctx, uint64(namePtr), uint64(connPtr), uint64(flags), 0)
|
2023-01-11 14:58:20 +00:00
|
|
|
if err != nil {
|
2023-02-16 13:52:05 +00:00
|
|
|
panic(err)
|
2023-01-11 14:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-28 12:47:39 +00:00
|
|
|
c.handle = c.mem.readUint32(connPtr)
|
2023-01-17 13:43:16 +00:00
|
|
|
if err := c.error(r[0]); err != nil {
|
|
|
|
|
return nil, err
|
2023-01-12 05:57:09 +00:00
|
|
|
}
|
2023-01-17 13:43:16 +00:00
|
|
|
return c, nil
|
2023-01-12 05:57:09 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-10 14:14:19 +00:00
|
|
|
// Close closes the database connection.
|
|
|
|
|
//
|
2023-02-09 16:40:48 +00:00
|
|
|
// If the database connection is associated with unfinalized prepared statements,
|
|
|
|
|
// open blob handles, and/or unfinished backup objects,
|
|
|
|
|
// Close will leave the database connection open and return [BUSY].
|
|
|
|
|
//
|
2023-02-19 12:44:26 +00:00
|
|
|
// It is safe to close a nil, zero or closed connection.
|
|
|
|
|
//
|
2023-02-09 16:40:48 +00:00
|
|
|
// https://www.sqlite.org/c3ref/close.html
|
2023-01-12 13:43:35 +00:00
|
|
|
func (c *Conn) Close() error {
|
2023-02-16 13:52:05 +00:00
|
|
|
if c == nil || c.handle == 0 {
|
2023-02-10 14:14:19 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 14:56:49 +00:00
|
|
|
c.SetInterrupt(context.Background())
|
2023-02-14 18:21:18 +00:00
|
|
|
|
2023-01-18 12:44:14 +00:00
|
|
|
r, err := c.api.close.Call(c.ctx, uint64(c.handle))
|
2023-01-12 05:57:09 +00:00
|
|
|
if err != nil {
|
2023-02-16 13:52:05 +00:00
|
|
|
panic(err)
|
2023-01-11 14:58:20 +00:00
|
|
|
}
|
2023-01-12 13:43:35 +00:00
|
|
|
|
2023-01-17 13:43:16 +00:00
|
|
|
if err := c.error(r[0]); err != nil {
|
|
|
|
|
return err
|
2023-01-12 13:43:35 +00:00
|
|
|
}
|
2023-02-10 14:14:19 +00:00
|
|
|
|
|
|
|
|
c.handle = 0
|
2023-01-28 12:47:39 +00:00
|
|
|
return c.mem.mod.Close(c.ctx)
|
2023-01-11 14:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:06:19 +00:00
|
|
|
// Exec is a convenience function that allows an application to run
|
|
|
|
|
// multiple statements of SQL without having to use a lot of code.
|
|
|
|
|
//
|
|
|
|
|
// https://www.sqlite.org/c3ref/exec.html
|
|
|
|
|
func (c *Conn) Exec(sql string) error {
|
|
|
|
|
c.checkInterrupt()
|
|
|
|
|
defer c.arena.reset()
|
|
|
|
|
sqlPtr := c.arena.string(sql)
|
|
|
|
|
|
|
|
|
|
r, err := c.api.exec.Call(c.ctx, uint64(c.handle), uint64(sqlPtr), 0, 0, 0)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return c.error(r[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prepare calls [Conn.PrepareFlags] with no flags.
|
|
|
|
|
func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) {
|
|
|
|
|
return c.PrepareFlags(sql, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PrepareFlags compiles the first SQL statement in sql;
|
|
|
|
|
// tail is left pointing to what remains uncompiled.
|
|
|
|
|
// If the input text contains no SQL (if the input is an empty string or a comment),
|
|
|
|
|
// both stmt and err will be nil.
|
|
|
|
|
//
|
|
|
|
|
// https://www.sqlite.org/c3ref/prepare.html
|
|
|
|
|
func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) {
|
|
|
|
|
if emptyStatement(sql) {
|
|
|
|
|
return nil, "", nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer c.arena.reset()
|
|
|
|
|
stmtPtr := c.arena.new(ptrlen)
|
|
|
|
|
tailPtr := c.arena.new(ptrlen)
|
|
|
|
|
sqlPtr := c.arena.string(sql)
|
|
|
|
|
|
|
|
|
|
r, err := c.api.prepare.Call(c.ctx, uint64(c.handle),
|
|
|
|
|
uint64(sqlPtr), uint64(len(sql)+1), uint64(flags),
|
|
|
|
|
uint64(stmtPtr), uint64(tailPtr))
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stmt = &Stmt{c: c}
|
|
|
|
|
stmt.handle = c.mem.readUint32(stmtPtr)
|
|
|
|
|
i := c.mem.readUint32(tailPtr)
|
|
|
|
|
tail = sql[i-sqlPtr:]
|
|
|
|
|
|
|
|
|
|
if err := c.error(r[0], sql); err != nil {
|
|
|
|
|
return nil, "", err
|
|
|
|
|
}
|
|
|
|
|
if stmt.handle == 0 {
|
|
|
|
|
return nil, "", nil
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 14:31:41 +00:00
|
|
|
// GetAutocommit tests the connection for auto-commit mode.
|
|
|
|
|
//
|
|
|
|
|
// https://www.sqlite.org/c3ref/get_autocommit.html
|
|
|
|
|
func (c *Conn) GetAutocommit() bool {
|
|
|
|
|
r, err := c.api.autocommit.Call(c.ctx, uint64(c.handle))
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return r[0] != 0
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:06:19 +00:00
|
|
|
// LastInsertRowID returns the rowid of the most recent successful INSERT
|
|
|
|
|
// on the database connection.
|
|
|
|
|
//
|
|
|
|
|
// https://www.sqlite.org/c3ref/last_insert_rowid.html
|
|
|
|
|
func (c *Conn) LastInsertRowID() uint64 {
|
|
|
|
|
r, err := c.api.lastRowid.Call(c.ctx, uint64(c.handle))
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return r[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Changes returns the number of rows modified, inserted or deleted
|
|
|
|
|
// by the most recently completed INSERT, UPDATE or DELETE statement
|
|
|
|
|
// on the database connection.
|
|
|
|
|
//
|
|
|
|
|
// https://www.sqlite.org/c3ref/changes.html
|
|
|
|
|
func (c *Conn) Changes() uint64 {
|
|
|
|
|
r, err := c.api.changes.Call(c.ctx, uint64(c.handle))
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
return r[0]
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 14:56:49 +00:00
|
|
|
// SetInterrupt interrupts a long-running query when a context is done.
|
2023-02-14 18:21:18 +00:00
|
|
|
//
|
|
|
|
|
// Subsequent uses of the connection will return [INTERRUPT]
|
2023-02-24 14:56:49 +00:00
|
|
|
// until the context is reset by another call to SetInterrupt.
|
2023-02-14 18:21:18 +00:00
|
|
|
//
|
2023-02-24 14:56:49 +00:00
|
|
|
// For example, a timeout can be associated with a connection:
|
2023-02-14 18:21:18 +00:00
|
|
|
//
|
|
|
|
|
// ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
|
2023-02-24 14:56:49 +00:00
|
|
|
// conn.SetInterrupt(ctx)
|
2023-02-14 18:21:18 +00:00
|
|
|
// defer cancel()
|
|
|
|
|
//
|
2023-02-24 14:56:49 +00:00
|
|
|
// SetInterrupt returns the old context assigned to the connection.
|
|
|
|
|
//
|
2023-02-14 18:21:18 +00:00
|
|
|
// https://www.sqlite.org/c3ref/interrupt.html
|
2023-02-24 14:56:49 +00:00
|
|
|
func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
|
2023-02-14 18:21:18 +00:00
|
|
|
// Is a waiter running?
|
|
|
|
|
if c.waiter != nil {
|
|
|
|
|
c.waiter <- struct{}{} // Cancel the waiter.
|
|
|
|
|
<-c.waiter // Wait for it to finish.
|
|
|
|
|
c.waiter = nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 14:56:49 +00:00
|
|
|
old = c.interrupt
|
|
|
|
|
c.interrupt = ctx
|
|
|
|
|
if ctx == nil || ctx == context.Background() || ctx == context.TODO() || ctx.Done() == nil {
|
2023-02-24 14:31:41 +00:00
|
|
|
// Finalize the uncompleted SQL statement.
|
|
|
|
|
if c.pending != nil {
|
|
|
|
|
c.pending.Close()
|
|
|
|
|
c.pending = nil
|
|
|
|
|
}
|
2023-02-14 18:21:18 +00:00
|
|
|
return old
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-16 13:30:31 +00:00
|
|
|
// Creating an uncompleted SQL statement prevents SQLite from ignoring
|
|
|
|
|
// an interrupt that comes before any other statements are started.
|
2023-02-24 14:31:41 +00:00
|
|
|
if c.pending == nil {
|
|
|
|
|
c.pending, _, _ = c.Prepare(`SELECT 1 UNION ALL SELECT 2`)
|
|
|
|
|
c.pending.Step()
|
|
|
|
|
} else {
|
|
|
|
|
c.pending.Reset()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Don't create the goroutine if we're already interrupted.
|
|
|
|
|
// This happens frequently while restoring to a previously interrupted state.
|
|
|
|
|
if c.checkInterrupt() {
|
|
|
|
|
return old
|
|
|
|
|
}
|
2023-02-16 13:30:31 +00:00
|
|
|
|
2023-02-14 18:21:18 +00:00
|
|
|
waiter := make(chan struct{})
|
|
|
|
|
c.waiter = waiter
|
|
|
|
|
go func() {
|
|
|
|
|
select {
|
2023-02-16 13:30:31 +00:00
|
|
|
case <-waiter: // Waiter was cancelled.
|
|
|
|
|
break
|
|
|
|
|
|
2023-02-24 14:56:49 +00:00
|
|
|
case <-ctx.Done(): // Done was closed.
|
2023-02-14 18:21:18 +00:00
|
|
|
|
2023-02-24 15:19:57 +00:00
|
|
|
c.sendInterrupt()
|
2023-02-14 18:21:18 +00:00
|
|
|
// Wait for the next call to SetInterrupt.
|
2023-02-16 13:30:31 +00:00
|
|
|
<-waiter
|
2023-02-14 18:21:18 +00:00
|
|
|
}
|
2023-02-16 13:30:31 +00:00
|
|
|
|
|
|
|
|
// Signal that the waiter has finished.
|
2023-02-14 18:21:18 +00:00
|
|
|
waiter <- struct{}{}
|
|
|
|
|
}()
|
|
|
|
|
return old
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 14:31:41 +00:00
|
|
|
func (c *Conn) checkInterrupt() bool {
|
2023-02-24 14:56:49 +00:00
|
|
|
if c.interrupt == nil || c.interrupt.Err() == nil {
|
2023-02-24 14:31:41 +00:00
|
|
|
return false
|
|
|
|
|
}
|
2023-02-24 15:19:57 +00:00
|
|
|
c.sendInterrupt()
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Conn) sendInterrupt() {
|
|
|
|
|
c.mtx.Lock()
|
|
|
|
|
defer c.mtx.Unlock()
|
|
|
|
|
// This is safe to call from a goroutine
|
|
|
|
|
// because it doesn't touch the C stack.
|
2023-02-24 14:56:49 +00:00
|
|
|
_, err := c.api.interrupt.Call(c.ctx, uint64(c.handle))
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2023-02-24 14:31:41 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:06:19 +00:00
|
|
|
// Savepoint creates a named SQLite transaction using SAVEPOINT.
|
2023-02-09 16:40:48 +00:00
|
|
|
//
|
2023-02-24 15:06:19 +00:00
|
|
|
// On success Savepoint returns a release func that will call
|
|
|
|
|
// either RELEASE or ROLLBACK depending on whether the parameter *error
|
|
|
|
|
// points to a nil or non-nil error.
|
2023-02-09 16:40:48 +00:00
|
|
|
//
|
2023-02-24 15:06:19 +00:00
|
|
|
// This is meant to be deferred:
|
|
|
|
|
//
|
|
|
|
|
// func doWork(conn *sqlite3.Conn) (err error) {
|
|
|
|
|
// defer conn.Savepoint()(&err)
|
|
|
|
|
//
|
|
|
|
|
// // ... do work in the transaction
|
|
|
|
|
// }
|
|
|
|
|
func (conn *Conn) Savepoint() (release func(*error)) {
|
|
|
|
|
name := "sqlite3.Savepoint" // names can be reused
|
|
|
|
|
var pc [1]uintptr
|
|
|
|
|
if n := runtime.Callers(2, pc[:]); n > 0 {
|
|
|
|
|
frames := runtime.CallersFrames(pc[:n])
|
|
|
|
|
frame, _ := frames.Next()
|
|
|
|
|
if frame.Function != "" {
|
|
|
|
|
name = frame.Function
|
|
|
|
|
}
|
2023-02-19 12:44:26 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:06:19 +00:00
|
|
|
err := conn.Exec(fmt.Sprintf("SAVEPOINT %q;", name))
|
2023-01-15 04:35:37 +00:00
|
|
|
if err != nil {
|
2023-02-24 15:06:19 +00:00
|
|
|
return func(errp *error) {
|
|
|
|
|
if *errp == nil {
|
|
|
|
|
*errp = err
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-15 04:35:37 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:06:19 +00:00
|
|
|
return func(errp *error) {
|
|
|
|
|
recovered := recover()
|
|
|
|
|
if recovered != nil {
|
|
|
|
|
defer panic(recovered)
|
|
|
|
|
}
|
2023-01-15 04:35:37 +00:00
|
|
|
|
2023-02-24 15:06:19 +00:00
|
|
|
if conn.GetAutocommit() {
|
|
|
|
|
// There is nothing to commit/rollback.
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-01-15 04:35:37 +00:00
|
|
|
|
2023-02-24 15:06:19 +00:00
|
|
|
if *errp == nil && recovered == nil {
|
|
|
|
|
// Success path.
|
|
|
|
|
// RELEASE the savepoint successfully.
|
|
|
|
|
*errp = conn.Exec(fmt.Sprintf("RELEASE %q;", name))
|
|
|
|
|
if *errp == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// Possible interrupt, fall through to the error path.
|
|
|
|
|
}
|
2023-02-17 02:21:07 +00:00
|
|
|
|
2023-02-24 15:06:19 +00:00
|
|
|
// Error path.
|
|
|
|
|
// Always ROLLBACK even if the connection has been interrupted.
|
|
|
|
|
old := conn.SetInterrupt(context.Background())
|
|
|
|
|
defer conn.SetInterrupt(old)
|
|
|
|
|
|
|
|
|
|
err := conn.Exec(fmt.Sprintf("ROLLBACK TO %q;", name))
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
err = conn.Exec(fmt.Sprintf("RELEASE %q;", name))
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2023-02-17 02:21:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-10 16:42:49 +00:00
|
|
|
func (c *Conn) error(rc uint64, sql ...string) error {
|
2023-01-17 13:43:16 +00:00
|
|
|
if rc == _OK {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-09 16:40:48 +00:00
|
|
|
err := Error{code: rc}
|
2023-01-12 13:43:35 +00:00
|
|
|
|
2023-02-09 16:40:48 +00:00
|
|
|
if err.Code() == NOMEM || err.ExtendedCode() == IOERR_NOMEM {
|
2023-01-21 12:09:54 +00:00
|
|
|
panic(oomErr)
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:43:35 +00:00
|
|
|
var r []uint64
|
|
|
|
|
|
2023-02-16 13:30:31 +00:00
|
|
|
r, _ = c.api.errstr.Call(c.ctx, rc)
|
|
|
|
|
if r != nil {
|
2023-02-18 00:47:56 +00:00
|
|
|
err.str = c.mem.readString(uint32(r[0]), _MAX_STRING)
|
2023-02-16 13:30:31 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 12:09:54 +00:00
|
|
|
r, _ = c.api.errmsg.Call(c.ctx, uint64(c.handle))
|
2023-01-12 13:43:35 +00:00
|
|
|
if r != nil {
|
2023-02-18 00:47:56 +00:00
|
|
|
err.msg = c.mem.readString(uint32(r[0]), _MAX_STRING)
|
2023-01-12 13:43:35 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-10 16:42:49 +00:00
|
|
|
if sql != nil {
|
|
|
|
|
r, _ = c.api.erroff.Call(c.ctx, uint64(c.handle))
|
|
|
|
|
if r != nil && r[0] != math.MaxUint32 {
|
|
|
|
|
err.sql = sql[0][r[0]:]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-16 13:30:31 +00:00
|
|
|
switch err.msg {
|
|
|
|
|
case err.str, "not an error":
|
2023-01-21 12:09:54 +00:00
|
|
|
err.msg = ""
|
|
|
|
|
}
|
|
|
|
|
return &err
|
2023-01-12 13:43:35 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-12 05:57:09 +00:00
|
|
|
func (c *Conn) free(ptr uint32) {
|
2023-01-12 13:43:35 +00:00
|
|
|
if ptr == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-01-18 12:44:14 +00:00
|
|
|
_, err := c.api.free.Call(c.ctx, uint64(ptr))
|
2023-01-11 14:58:20 +00:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-14 11:34:24 +00:00
|
|
|
func (c *Conn) new(size uint32) uint32 {
|
|
|
|
|
r, err := c.api.malloc.Call(c.ctx, uint64(size))
|
2023-01-11 14:58:20 +00:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2023-01-25 14:59:02 +00:00
|
|
|
ptr := uint32(r[0])
|
2023-02-14 11:34:24 +00:00
|
|
|
if ptr == 0 && size != 0 {
|
2023-01-19 14:31:32 +00:00
|
|
|
panic(oomErr)
|
2023-01-12 11:06:17 +00:00
|
|
|
}
|
2023-01-25 14:59:02 +00:00
|
|
|
return ptr
|
2023-01-11 14:58:20 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-26 14:52:38 +00:00
|
|
|
func (c *Conn) newBytes(b []byte) uint32 {
|
|
|
|
|
if b == nil {
|
2023-01-17 18:31:46 +00:00
|
|
|
return 0
|
|
|
|
|
}
|
2023-02-14 11:34:24 +00:00
|
|
|
ptr := c.new(uint32(len(b)))
|
|
|
|
|
c.mem.writeBytes(ptr, b)
|
2023-01-17 13:43:16 +00:00
|
|
|
return ptr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Conn) newString(s string) uint32 {
|
2023-02-14 11:34:24 +00:00
|
|
|
ptr := c.new(uint32(len(s) + 1))
|
|
|
|
|
c.mem.writeString(ptr, s)
|
|
|
|
|
return ptr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *Conn) newArena(size uint32) arena {
|
|
|
|
|
return arena{
|
|
|
|
|
c: c,
|
|
|
|
|
size: size,
|
|
|
|
|
base: c.new(size),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type arena struct {
|
|
|
|
|
c *Conn
|
|
|
|
|
base uint32
|
|
|
|
|
next uint32
|
|
|
|
|
size uint32
|
|
|
|
|
ptrs []uint32
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 14:31:41 +00:00
|
|
|
func (a *arena) free() {
|
|
|
|
|
if a.c == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
a.reset()
|
|
|
|
|
a.c.free(a.base)
|
|
|
|
|
a.c = nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-14 11:34:24 +00:00
|
|
|
func (a *arena) reset() {
|
|
|
|
|
for _, ptr := range a.ptrs {
|
|
|
|
|
a.c.free(ptr)
|
|
|
|
|
}
|
|
|
|
|
a.ptrs = nil
|
|
|
|
|
a.next = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *arena) new(size uint32) uint32 {
|
|
|
|
|
if a.next+size <= a.size {
|
|
|
|
|
ptr := a.base + a.next
|
|
|
|
|
a.next += size
|
|
|
|
|
return ptr
|
|
|
|
|
}
|
|
|
|
|
ptr := a.c.new(size)
|
|
|
|
|
a.ptrs = append(a.ptrs, ptr)
|
|
|
|
|
return ptr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *arena) string(s string) uint32 {
|
|
|
|
|
ptr := a.new(uint32(len(s) + 1))
|
|
|
|
|
a.c.mem.writeString(ptr, s)
|
2023-01-11 14:58:20 +00:00
|
|
|
return ptr
|
|
|
|
|
}
|