-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdchash.go
109 lines (91 loc) · 2.16 KB
/
dchash.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
// Package dchash implements a configurable variation of Dropbox's Content Hash.
package dchash
import (
"crypto"
_ "crypto/sha256" // DefaultHash
"hash"
)
const (
// DefaultHash denotes the default Hash.
DefaultHash = crypto.SHA256
// DefaultBlockSize denotes the default block size.
DefaultBlockSize = 1 << 22
)
// New returns a Hash which yields checksums according to Dropbox's Content Hash
// algorithm, more details on which may be found here:
//
// https://www.dropbox.com/developers/reference/content-hash
//
// Should newFunc be nil, DefaultHash.New will be used in its place.
//
// Should blockSize be less than 1, DefaultBlockSize will be used instead.
//
// Hashes returned by New are NOT safe for concurrent use by multiple
// goroutines.
func New(newFunc func() hash.Hash, blockSize int) hash.Hash {
if newFunc == nil {
newFunc = DefaultHash.New
}
if blockSize < 1 {
blockSize = DefaultBlockSize
}
h := newFunc()
return &wrapper{
blk: h,
sum: newFunc(),
buf: make([]byte, h.Size()),
blockSize: blockSize,
rem: blockSize,
}
}
type wrapper struct {
buf []byte
blk hash.Hash // sums blocks
sum hash.Hash // sums blocks' sums
blockSize int // denotes the size of individual blocks
rem int // room in current block
}
func (w *wrapper) BlockSize() int {
return w.blockSize
}
func (w *wrapper) Size() int {
return w.blk.Size()
}
func (w *wrapper) Sum(b []byte) []byte {
if w.rem != w.BlockSize() {
w.sumBlock()
}
return w.sum.Sum(b)
}
func (w *wrapper) Reset() {
w.blk.Reset()
w.sum.Reset()
w.rem = w.BlockSize()
}
func (w *wrapper) Write(b []byte) (n int, err error) {
for err == nil && len(b) > 0 {
// read as many bytes as are missing from the current block
// (or until the end of b)
var nn int
nn, err = w.blk.Write(b[:min(len(b), w.rem)])
n += nn
b = b[nn:]
if w.rem -= nn; w.rem == 0 {
w.sumBlock()
w.rem = w.BlockSize()
}
}
return
}
// sumBlock sums w's blk hash into w's buf.
func (w *wrapper) sumBlock() {
defer w.blk.Reset()
_ = w.blk.Sum(w.buf[:0])
_, _ = w.sum.Write(w.buf)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}