-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathpinned-blob.go
More file actions
121 lines (110 loc) · 2.35 KB
/
Copy pathpinned-blob.go
File metadata and controls
121 lines (110 loc) · 2.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package squirrel
import (
g "github.com/anacrolix/generics"
"io"
"time"
"github.com/go-llsqlite/adapter"
)
// Wraps a specific sqlite.Blob instance, when we don't want to dive into the cache to refetch
// blobs. Until Closed, PinnedBlob holds a transaction open on the Cache.
type PinnedBlob struct {
key string
write bool
tx *Tx
valueId rowid
}
// This is very cheap for this type.
func (pb *PinnedBlob) Length() int64 {
l, err := pb.LengthErr()
if err != nil {
return -1
}
return l
}
func (pb *PinnedBlob) closedErr() error {
if pb.tx == nil {
return ErrClosed
}
return nil
}
// This is very cheap for this type.
func (pb *PinnedBlob) LengthErr() (_ int64, err error) {
err = pb.closedErr()
if err != nil {
return
}
return pb.tx.conn.getValueLength(pb.key)
}
// Requires only that we lock the sqlite conn.
func (pb *PinnedBlob) ReadAt(b []byte, valueOff int64) (n int, err error) {
return pb.doIoAt(b, valueOff, (*sqlite.Blob).ReadAt, false)
}
// Requires only that we lock the sqlite conn.
func (pb *PinnedBlob) doIoAt(
b []byte,
valueOff int64,
blobCall func(*sqlite.Blob, []byte, int64) (int, error),
write bool,
) (n int, err error) {
err = pb.closedErr()
if err != nil {
return
}
conn := pb.tx.conn
l, err := conn.getValueLength(pb.key)
if err != nil {
return
}
if valueOff >= l {
err = io.EOF
return
}
err = conn.iterBlobs(
pb.valueId,
func(blobOff int64, blob *sqlite.Blob) (more bool, err error) {
readOff := valueOff - blobOff
if readOff < 0 {
return false, nil
}
if readOff >= blob.Size() {
return true, nil
}
b1 := b
if int64(len(b1)) > blob.Size()-readOff {
b1 = b[:blob.Size()-readOff]
}
n1, err := blobCall(blob, b1, readOff)
n += n1
b = b[n1:]
valueOff += int64(n1)
if n1 == len(b1) && err == io.EOF {
err = nil
}
if err != nil {
return
}
more = len(b) != 0
return
},
write,
valueOff,
)
if n != 0 {
g.MakeMapIfNilAndSet(&pb.tx.accessedKeys, pb.valueId, struct{}{})
}
return
}
func (pb *PinnedBlob) WriteAt(b []byte, off int64) (n int, err error) {
return pb.doIoAt(b, off, (*sqlite.Blob).WriteAt, true)
}
func (pb *PinnedBlob) Close() error {
pb.tx = nil
return nil
}
func (pb *PinnedBlob) LastUsed() (lastUsed time.Time, err error) {
err = pb.closedErr()
if err != nil {
return
}
return pb.tx.lastUsed(pb.valueId)
}