switch to sparse stack
This commit is contained in:
parent
70299b9d3b
commit
2343930773
50
blake3.go
50
blake3.go
|
@ -168,8 +168,6 @@ func (cs *chunkState) chunkCounter() uint64 {
|
||||||
return cs.n.counter
|
return cs.n.counter
|
||||||
}
|
}
|
||||||
|
|
||||||
// complete is a helper method that reports whether a full chunk has been
|
|
||||||
// processed.
|
|
||||||
func (cs *chunkState) complete() bool {
|
func (cs *chunkState) complete() bool {
|
||||||
return cs.bytesConsumed == chunkSize
|
return cs.bytesConsumed == chunkSize
|
||||||
}
|
}
|
||||||
|
@ -246,40 +244,37 @@ func parentNode(left, right [8]uint32, key [8]uint32, flags uint32) node {
|
||||||
type Hasher struct {
|
type Hasher struct {
|
||||||
cs chunkState
|
cs chunkState
|
||||||
key [8]uint32
|
key [8]uint32
|
||||||
chainStack [54][8]uint32 // space for 54 subtrees (2^54 * chunkSize = 2^64)
|
|
||||||
stackSize int // number of chainStack elements that are valid
|
|
||||||
flags uint32
|
flags uint32
|
||||||
size int // output size, for Sum
|
size int // output size, for Sum
|
||||||
|
|
||||||
|
// log(n) set of Merkle subtree roots, at most one per height.
|
||||||
|
stack [54][8]uint32 // 2^54 * chunkSize = 2^64
|
||||||
|
used uint64 // bit vector indicating which stack elems are valid; also number of chunks added
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hasher) hasSubtreeAtHeight(i uint64) bool {
|
||||||
|
return h.used&(1<<i) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// addChunkChainingValue appends a chunk to the right edge of the Merkle tree.
|
// addChunkChainingValue appends a chunk to the right edge of the Merkle tree.
|
||||||
func (h *Hasher) addChunkChainingValue(cv [8]uint32, totalChunks uint64) {
|
func (h *Hasher) addChunkChainingValue(cv [8]uint32) {
|
||||||
// This chunk might complete some subtrees. For each completed subtree, its
|
// seek to first open stack slot, merging subtrees as we go
|
||||||
// left child will be the current top entry in the CV stack, and its right
|
i := uint64(0)
|
||||||
// child will be the current value of cv. Pop each left child off the stack,
|
for ; h.hasSubtreeAtHeight(i); i++ {
|
||||||
// merge it with cv, and overwrite cv with the result. After all these
|
cv = parentNode(h.stack[i], cv, h.key, h.flags).chainingValue()
|
||||||
// merges, push the final value of cv onto the stack. The number of
|
|
||||||
// completed subtrees is given by the number of trailing 0-bits in the new
|
|
||||||
// total number of chunks.
|
|
||||||
for totalChunks&1 == 0 {
|
|
||||||
// pop and merge
|
|
||||||
h.stackSize--
|
|
||||||
cv = parentNode(h.chainStack[h.stackSize], cv, h.key, h.flags).chainingValue()
|
|
||||||
totalChunks >>= 1
|
|
||||||
}
|
}
|
||||||
h.chainStack[h.stackSize] = cv
|
h.stack[i] = cv
|
||||||
h.stackSize++
|
h.used++
|
||||||
}
|
}
|
||||||
|
|
||||||
// rootNode computes the root of the Merkle tree. It does not modify the
|
// rootNode computes the root of the Merkle tree. It does not modify the
|
||||||
// chainStack.
|
// chainStack.
|
||||||
func (h *Hasher) rootNode() node {
|
func (h *Hasher) rootNode() node {
|
||||||
// Starting with the node from the current chunk, compute all the
|
|
||||||
// parent chaining values along the right edge of the tree, until we
|
|
||||||
// have the root node.
|
|
||||||
n := h.cs.node()
|
n := h.cs.node()
|
||||||
for i := h.stackSize - 1; i >= 0; i-- {
|
for i := uint64(bits.TrailingZeros64(h.used)); i < 64; i++ {
|
||||||
n = parentNode(h.chainStack[i], n.chainingValue(), h.key, h.flags)
|
if h.hasSubtreeAtHeight(i) {
|
||||||
|
n = parentNode(h.stack[i], n.chainingValue(), h.key, h.flags)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
n.flags |= flagRoot
|
n.flags |= flagRoot
|
||||||
return n
|
return n
|
||||||
|
@ -288,7 +283,7 @@ func (h *Hasher) rootNode() node {
|
||||||
// Reset implements hash.Hash.
|
// Reset implements hash.Hash.
|
||||||
func (h *Hasher) Reset() {
|
func (h *Hasher) Reset() {
|
||||||
h.cs = newChunkState(h.key, 0, h.flags)
|
h.cs = newChunkState(h.key, 0, h.flags)
|
||||||
h.stackSize = 0
|
h.used = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockSize implements hash.Hash.
|
// BlockSize implements hash.Hash.
|
||||||
|
@ -306,9 +301,8 @@ func (h *Hasher) Write(p []byte) (int, error) {
|
||||||
// chunks).
|
// chunks).
|
||||||
if h.cs.complete() {
|
if h.cs.complete() {
|
||||||
cv := h.cs.node().chainingValue()
|
cv := h.cs.node().chainingValue()
|
||||||
totalChunks := h.cs.chunkCounter() + 1
|
h.addChunkChainingValue(cv)
|
||||||
h.addChunkChainingValue(cv, totalChunks)
|
h.cs = newChunkState(h.key, h.cs.chunkCounter()+1, h.flags)
|
||||||
h.cs = newChunkState(h.key, totalChunks, h.flags)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress input bytes into the current chunk state.
|
// Compress input bytes into the current chunk state.
|
||||||
|
|
Reference in New Issue