-
Notifications
You must be signed in to change notification settings - Fork 33
/
stash.go
133 lines (118 loc) · 2.98 KB
/
stash.go
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
122
123
124
125
126
127
128
129
130
131
132
133
package stash
import (
"bazil.org/bazil/cas"
"bazil.org/bazil/cas/chunks"
"bazil.org/bazil/idpool"
"golang.org/x/net/context"
)
// New creates a new Stash.
func New(bs chunks.Store) *Stash {
s := &Stash{
chunks: bs,
local: make(map[uint64]*chunks.Chunk),
}
return s
}
// Stash is a proxy for a chunks.Store, but it keeps Private Keys
// local, only saving them to the Store when Save is called.
type Stash struct {
chunks chunks.Store
ids idpool.Pool
local map[uint64]*chunks.Chunk
}
// Get returns a chunk either from the local stash, or from the
// Store (for Private keys).
//
// For Private keys, modifying the returned chunk *will* cause the
// locally stored data to change. This is the intended usage of a
// stash.
func (s *Stash) Get(ctx context.Context, key cas.Key, typ string, level uint8) (*chunks.Chunk, error) {
priv, ok := key.Private()
if ok {
chunk, ok := s.local[priv]
if !ok {
return nil, cas.NotFoundError{
Type: typ,
Level: level,
Key: key,
}
}
return chunk, nil
}
chunk, err := s.chunks.Get(ctx, key, typ, level)
return chunk, err
}
func (s *Stash) drop(priv uint64) {
s.ids.Put(priv)
delete(s.local, priv)
}
// Drop forgets a Private chunk. The key may be reused, so caller must
// not remember the old key.
func (s *Stash) Drop(key cas.Key) {
priv, ok := key.Private()
if !ok {
return
}
s.drop(priv)
}
// Clone is like Get but clones the chunk if it's not already private.
// Chunks that are already private are returned as-is.
//
// A cloned chunk will have a buffer of size bytes. This is intended
// to use for re-inflating zero-trimmed chunks.
//
// Modifying the returned chunk *will* cause the locally stored data
// to change. This is the intended usage of a stash.
func (s *Stash) Clone(ctx context.Context, key cas.Key, typ string, level uint8, size uint32) (cas.Key, *chunks.Chunk, error) {
priv, ok := key.Private()
if ok {
chunk, ok := s.local[priv]
if !ok {
return key, nil, cas.NotFoundError{
Type: typ,
Level: level,
Key: key,
}
}
return key, chunk, nil
}
chunk, err := s.chunks.Get(ctx, key, typ, level)
if err != nil {
return key, nil, err
}
// clone the byte slice
tmp := make([]byte, size)
copy(tmp, chunk.Buf)
chunk.Buf = tmp
priv = s.ids.Get()
privkey := cas.NewKeyPrivateNum(priv)
s.local[priv] = chunk
return privkey, chunk, nil
}
// Save the local Chunk to the Store.
//
// On success, the old key becomes invalid.
func (s *Stash) Save(ctx context.Context, key cas.Key) (cas.Key, error) {
priv, ok := key.Private()
if !ok {
return key, nil
}
chunk, ok := s.local[priv]
if !ok {
return key, cas.NotFoundError{
Key: key,
}
}
newkey, err := s.chunks.Add(ctx, chunk)
if err != nil {
return key, err
}
s.drop(priv)
return newkey, nil
}
// Clear drops all the Private chunks held in this Stash. This is
// useful e.g. when the contents of a Blob are completely rewritten.
func (s *Stash) Clear() {
s.ids = idpool.Pool{}
s.local = make(map[uint64]*chunks.Chunk)
}