-
Notifications
You must be signed in to change notification settings - Fork 0
/
blob.go
105 lines (80 loc) · 2.31 KB
/
blob.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
// Copyright Chrono Technologies LLC
// SPDX-License-Identifier: MIT
package arc
import "crypto/sha256"
const (
// Length of the blobID in bytes.
blobIDLen = 32
)
// blobID is a 32-byte fixed-length byte array representing the SHA-256 hash of
// a blob value. It is an array and not a slice for map key compatibility.
type blobID [blobIDLen]byte
// Slice returns the given blobID as a byte slice.
func (id blobID) Slice() []byte {
return id[:]
}
// sliceToBlobID creates a blobID from the given byte slice. It requires the
// given slice length to match the fixed blobID length (32 bytes).
func sliceToBlobID(src []byte) (blobID, error) {
var ret blobID
if len(src) != blobIDLen {
return ret, ErrCorrupted
}
copy(ret[:], src)
return ret, nil
}
// makeBlobID creates a blobID by computing the SHA-256 hash of the src value.
func makeBlobID(src []byte) blobID {
return blobID(sha256.Sum256(src))
}
// blob represents the blob value and its reference count.
type blob struct {
value []byte
refCount int
}
// blobStore maps blobIDs to their corresponding blobs. It is used to store
// values that exceed the 32-byte value length threshold.
type blobStore map[blobID]*blob
// get returns the blob that matches the blobID.
func (bs blobStore) get(id []byte) []byte {
blobID, err := sliceToBlobID(id)
if err != nil {
return nil
}
b, found := bs[blobID]
if !found {
return nil
}
// Create a copy of the value since returning a pointer to the underlying
// value can have serious implications, such as breaking data integrity.
ret := make([]byte, len(b.value))
copy(ret, b.value)
return ret
}
// put either creates a new blob and inserts it to the blobStore or increments
// the refCount of an existing blob. It returns a blobID on success.
func (bs blobStore) put(value []byte) blobID {
k := makeBlobID(value)
if b, found := bs[k]; found {
b.refCount++
} else {
bs[k] = &blob{value: value, refCount: 1}
}
return k
}
// release decrements the refCount of a blob if it exists for the given blobID.
// When the refCount reaches zero, the blob is removed from the blobStore.
func (bs blobStore) release(id []byte) {
blobID, err := sliceToBlobID(id)
if err != nil {
return
}
if b, found := bs[blobID]; found {
if b.refCount > 0 {
b.refCount--
}
if b.refCount == 0 {
delete(bs, blobID)
}
}
}