summaryrefslogtreecommitdiff
path: root/vendor/github.com/ncruces/go-sqlite3/blob.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-05-27 15:46:15 +0000
committerLibravatar GitHub <noreply@github.com>2024-05-27 17:46:15 +0200
commit1e7b32490dfdccddd04f46d4b0416b48d749d51b (patch)
tree62a11365933a5a11e0800af64cbdf9172e5e6e7a /vendor/github.com/ncruces/go-sqlite3/blob.go
parent[chore] Small styling + link issues (#2933) (diff)
downloadgotosocial-1e7b32490dfdccddd04f46d4b0416b48d749d51b.tar.xz
[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/).
Diffstat (limited to 'vendor/github.com/ncruces/go-sqlite3/blob.go')
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/blob.go250
1 files changed, 250 insertions, 0 deletions
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
+}