From 1e7b32490dfdccddd04f46d4b0416b48d749d51b Mon Sep 17 00:00:00 2001 From: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com> Date: Mon, 27 May 2024 15:46:15 +0000 Subject: [experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863) This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/). --- vendor/github.com/ncruces/go-sqlite3/blob.go | 250 +++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 vendor/github.com/ncruces/go-sqlite3/blob.go (limited to 'vendor/github.com/ncruces/go-sqlite3/blob.go') diff --git a/vendor/github.com/ncruces/go-sqlite3/blob.go b/vendor/github.com/ncruces/go-sqlite3/blob.go new file mode 100644 index 000000000..bb10c5fa2 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/blob.go @@ -0,0 +1,250 @@ +package sqlite3 + +import ( + "io" + + "github.com/ncruces/go-sqlite3/internal/util" +) + +// ZeroBlob represents a zero-filled, length n BLOB +// that can be used as an argument to +// [database/sql.DB.Exec] and similar methods. +type ZeroBlob int64 + +// Blob is an handle to an open BLOB. +// +// It implements [io.ReadWriteSeeker] for incremental BLOB I/O. +// +// https://sqlite.org/c3ref/blob.html +type Blob struct { + c *Conn + bytes int64 + offset int64 + handle uint32 +} + +var _ io.ReadWriteSeeker = &Blob{} + +// OpenBlob opens a BLOB for incremental I/O. +// +// https://sqlite.org/c3ref/blob_open.html +func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob, error) { + c.checkInterrupt() + defer c.arena.mark()() + blobPtr := c.arena.new(ptrlen) + dbPtr := c.arena.string(db) + tablePtr := c.arena.string(table) + columnPtr := c.arena.string(column) + + var flags uint64 + if write { + flags = 1 + } + + r := c.call("sqlite3_blob_open", uint64(c.handle), + uint64(dbPtr), uint64(tablePtr), uint64(columnPtr), + uint64(row), flags, uint64(blobPtr)) + + if err := c.error(r); err != nil { + return nil, err + } + + blob := Blob{c: c} + blob.handle = util.ReadUint32(c.mod, blobPtr) + blob.bytes = int64(c.call("sqlite3_blob_bytes", uint64(blob.handle))) + return &blob, nil +} + +// Close closes a BLOB handle. +// +// It is safe to close a nil, zero or closed Blob. +// +// https://sqlite.org/c3ref/blob_close.html +func (b *Blob) Close() error { + if b == nil || b.handle == 0 { + return nil + } + + r := b.c.call("sqlite3_blob_close", uint64(b.handle)) + + b.handle = 0 + return b.c.error(r) +} + +// Size returns the size of the BLOB in bytes. +// +// https://sqlite.org/c3ref/blob_bytes.html +func (b *Blob) Size() int64 { + return b.bytes +} + +// Read implements the [io.Reader] interface. +// +// https://sqlite.org/c3ref/blob_read.html +func (b *Blob) Read(p []byte) (n int, err error) { + if b.offset >= b.bytes { + return 0, io.EOF + } + + avail := b.bytes - b.offset + want := int64(len(p)) + if want > avail { + want = avail + } + + defer b.c.arena.mark()() + ptr := b.c.arena.new(uint64(want)) + + r := b.c.call("sqlite3_blob_read", uint64(b.handle), + uint64(ptr), uint64(want), uint64(b.offset)) + err = b.c.error(r) + if err != nil { + return 0, err + } + b.offset += want + if b.offset >= b.bytes { + err = io.EOF + } + + copy(p, util.View(b.c.mod, ptr, uint64(want))) + return int(want), err +} + +// WriteTo implements the [io.WriterTo] interface. +// +// https://sqlite.org/c3ref/blob_read.html +func (b *Blob) WriteTo(w io.Writer) (n int64, err error) { + if b.offset >= b.bytes { + return 0, nil + } + + want := int64(1024 * 1024) + avail := b.bytes - b.offset + if want > avail { + want = avail + } + + defer b.c.arena.mark()() + ptr := b.c.arena.new(uint64(want)) + + for want > 0 { + r := b.c.call("sqlite3_blob_read", uint64(b.handle), + uint64(ptr), uint64(want), uint64(b.offset)) + err = b.c.error(r) + if err != nil { + return n, err + } + + mem := util.View(b.c.mod, ptr, uint64(want)) + m, err := w.Write(mem[:want]) + b.offset += int64(m) + n += int64(m) + if err != nil { + return n, err + } + if int64(m) != want { + return n, io.ErrShortWrite + } + + avail = b.bytes - b.offset + if want > avail { + want = avail + } + } + return n, nil +} + +// Write implements the [io.Writer] interface. +// +// https://sqlite.org/c3ref/blob_write.html +func (b *Blob) Write(p []byte) (n int, err error) { + defer b.c.arena.mark()() + ptr := b.c.arena.bytes(p) + + r := b.c.call("sqlite3_blob_write", uint64(b.handle), + uint64(ptr), uint64(len(p)), uint64(b.offset)) + err = b.c.error(r) + if err != nil { + return 0, err + } + b.offset += int64(len(p)) + return len(p), nil +} + +// ReadFrom implements the [io.ReaderFrom] interface. +// +// https://sqlite.org/c3ref/blob_write.html +func (b *Blob) ReadFrom(r io.Reader) (n int64, err error) { + want := int64(1024 * 1024) + avail := b.bytes - b.offset + if l, ok := r.(*io.LimitedReader); ok && want > l.N { + want = l.N + } + if want > avail { + want = avail + } + if want < 1 { + want = 1 + } + + defer b.c.arena.mark()() + ptr := b.c.arena.new(uint64(want)) + + for { + mem := util.View(b.c.mod, ptr, uint64(want)) + m, err := r.Read(mem[:want]) + if m > 0 { + r := b.c.call("sqlite3_blob_write", uint64(b.handle), + uint64(ptr), uint64(m), uint64(b.offset)) + err := b.c.error(r) + if err != nil { + return n, err + } + b.offset += int64(m) + n += int64(m) + } + if err == io.EOF { + return n, nil + } + if err != nil { + return n, err + } + + avail = b.bytes - b.offset + if want > avail { + want = avail + } + if want < 1 { + want = 1 + } + } +} + +// Seek implements the [io.Seeker] interface. +func (b *Blob) Seek(offset int64, whence int) (int64, error) { + switch whence { + default: + return 0, util.WhenceErr + case io.SeekStart: + break + case io.SeekCurrent: + offset += b.offset + case io.SeekEnd: + offset += b.bytes + } + if offset < 0 { + return 0, util.OffsetErr + } + b.offset = offset + return offset, nil +} + +// Reopen moves a BLOB handle to a new row of the same database table. +// +// https://sqlite.org/c3ref/blob_reopen.html +func (b *Blob) Reopen(row int64) error { + err := b.c.error(b.c.call("sqlite3_blob_reopen", uint64(b.handle), uint64(row))) + b.bytes = int64(b.c.call("sqlite3_blob_bytes", uint64(b.handle))) + b.offset = 0 + return err +} -- cgit v1.2.3