]> Sergey Matveev's repositories - btrtrc.git/blob - merkle/hash.go
cmd/btrtrc client
[btrtrc.git] / merkle / hash.go
1 package merkle
2
3 import (
4         "crypto/sha256"
5         "hash"
6         "unsafe"
7 )
8
9 func NewHash() *Hash {
10         h := &Hash{
11                 nextBlock: sha256.New(),
12         }
13         return h
14 }
15
16 type Hash struct {
17         blocks    [][32]byte
18         nextBlock hash.Hash
19         // How many bytes have been written to nextBlock so far.
20         nextBlockWritten int
21 }
22
23 func (h *Hash) remaining() int {
24         return BlockSize - h.nextBlockWritten
25 }
26
27 func (h *Hash) Write(p []byte) (n int, err error) {
28         for len(p) > 0 {
29                 var n1 int
30                 n1, err = h.nextBlock.Write(p[:min(len(p), h.remaining())])
31                 n += n1
32                 h.nextBlockWritten += n1
33                 p = p[n1:]
34                 if h.remaining() == 0 {
35                         h.blocks = append(h.blocks, h.nextBlockSum())
36                         h.nextBlock.Reset()
37                         h.nextBlockWritten = 0
38                 }
39                 if err != nil {
40                         break
41                 }
42         }
43         return
44 }
45
46 func (h *Hash) nextBlockSum() (sum [32]byte) {
47         if unsafe.SliceData(h.nextBlock.Sum(sum[:0])) != unsafe.SliceData(sum[:]) {
48                 panic("go sux")
49         }
50         return
51 }
52
53 func (h *Hash) curBlocks() [][32]byte {
54         blocks := h.blocks
55         if h.nextBlockWritten != 0 {
56                 blocks = append(blocks, h.nextBlockSum())
57         }
58         return blocks
59 }
60
61 func (h *Hash) Sum(b []byte) []byte {
62         sum := RootWithPadHash(h.curBlocks(), [32]byte{})
63         return append(b, sum[:]...)
64 }
65
66 // Sums by extending with zero hashes for blocks missing to meet the given length. Necessary for
67 // piece layers hashes for file tail blocks that don't pad to the piece length.
68 func (h *Hash) SumMinLength(b []byte, length int) []byte {
69         blocks := h.curBlocks()
70         minBlocks := (length + BlockSize - 1) / BlockSize
71         blocks = append(blocks, make([][32]byte, minBlocks-len(blocks))...)
72         sum := RootWithPadHash(blocks, [32]byte{})
73         return append(b, sum[:]...)
74 }
75
76 func (h *Hash) Reset() {
77         h.blocks = h.blocks[:0]
78         h.nextBlock.Reset()
79         h.nextBlockWritten = 0
80 }
81
82 func (h *Hash) Size() int {
83         return 32
84 }
85
86 func (h *Hash) BlockSize() int {
87         return h.nextBlock.BlockSize()
88 }
89
90 var _ hash.Hash = (*Hash)(nil)