wire: Add new DoubleSha256SH function.

This commit adds a new function which is similar to the DoubleSha256
function except it returns a ShaHash copy instead of a byte slice.  It
also adds a new benchmark for it.

This can be a slight optimization in certain cases where the caller
ultimately wants a ShaHash since it can avoid a heap allocation and
additional copy to convert the result to a ShaHash (the function simply
performs a type cast against the returned array which is not possible
against a []byte).

existing: DoubleSha256     500000   3081 ns/op   32 B/op   1 allocs/op
     new: DoubleSha256SH   500000   2939 ns/op    0 B/op   0 allocs/op

The hashing functions for blocks and transactions have also been updated
to make use of the new function since they directly return the ShaHash.
The transaction change in particular is quite useful since transactions
are frequently hashed and this change allows all of those hashes to avoid
an additional heap allocation.
This commit is contained in:
Dave Collins 2015-04-04 15:30:10 -05:00
parent f5cdf2d6a8
commit 6211eef7ee
5 changed files with 34 additions and 21 deletions

View file

@ -34,11 +34,8 @@ func HashMerkleBranches(left *wire.ShaHash, right *wire.ShaHash) *wire.ShaHash {
copy(sha[:wire.HashSize], left[:]) copy(sha[:wire.HashSize], left[:])
copy(sha[wire.HashSize:], right[:]) copy(sha[wire.HashSize:], right[:])
// Create a new sha hash from the double sha 256. Ignore the error newSha := wire.DoubleSha256SH(sha[:])
// here since SetBytes can't fail here due to the fact DoubleSha256 return &newSha
// always returns a []byte of the right size regardless of input.
newSha, _ := wire.NewShaHash(wire.DoubleSha256(sha[:]))
return newSha
} }
// BuildMerkleTreeStore creates a merkle tree from a slice of transactions, // BuildMerkleTreeStore creates a merkle tree from a slice of transactions,

View file

@ -394,7 +394,7 @@ func BenchmarkTxSha(b *testing.B) {
} }
// BenchmarkDoubleSha256 performs a benchmark on how long it takes to perform a // BenchmarkDoubleSha256 performs a benchmark on how long it takes to perform a
// double sha 256. // double sha 256 returning a byte slice.
func BenchmarkDoubleSha256(b *testing.B) { func BenchmarkDoubleSha256(b *testing.B) {
b.StopTimer() b.StopTimer()
var buf bytes.Buffer var buf bytes.Buffer
@ -409,3 +409,20 @@ func BenchmarkDoubleSha256(b *testing.B) {
_ = DoubleSha256(txBytes) _ = DoubleSha256(txBytes)
} }
} }
// BenchmarkDoubleSha256SH performs a benchmark on how long it takes to perform
// a double sha 256 returning a ShaHash.
func BenchmarkDoubleSha256SH(b *testing.B) {
b.StopTimer()
var buf bytes.Buffer
if err := genesisCoinbaseTx.Serialize(&buf); err != nil {
b.Errorf("Serialize: unexpected error: %v", err)
return
}
txBytes := buf.Bytes()
b.StartTimer()
for i := 0; i < b.N; i++ {
_ = DoubleSha256SH(txBytes)
}
}

View file

@ -46,21 +46,17 @@ const blockHeaderLen = 80
// BlockSha computes the block identifier hash for the given block header. // BlockSha computes the block identifier hash for the given block header.
func (h *BlockHeader) BlockSha() (ShaHash, error) { func (h *BlockHeader) BlockSha() (ShaHash, error) {
// Encode the header and run double sha256 everything prior to the // Encode the header and double sha256 everything prior to the number of
// number of transactions. Ignore the error returns since there is no // transactions. Ignore the error returns since there is no way the
// way the encode could fail except being out of memory which would // encode could fail except being out of memory which would cause a
// cause a run-time panic. Also, SetBytes can't fail here due to the // run-time panic.
// fact DoubleSha256 always returns a []byte of the right size
// regardless of input.
var buf bytes.Buffer var buf bytes.Buffer
var sha ShaHash
_ = writeBlockHeader(&buf, 0, h) _ = writeBlockHeader(&buf, 0, h)
_ = sha.SetBytes(DoubleSha256(buf.Bytes()[0:blockHeaderLen]))
// Even though this function can't currently fail, it still returns // Even though this function can't currently fail, it still returns
// a potential error to help future proof the API should a failure // a potential error to help future proof the API should a failure
// become possible. // become possible.
return sha, nil return DoubleSha256SH(buf.Bytes()), nil
} }
// Deserialize decodes a block header from r into the receiver using a format // Deserialize decodes a block header from r into the receiver using a format

View file

@ -526,3 +526,10 @@ func DoubleSha256(b []byte) []byte {
second := fastsha256.Sum256(first[:]) second := fastsha256.Sum256(first[:])
return second[:] return second[:]
} }
// DoubleSha256SH calculates sha256(sha256(b)) and returns the resulting bytes
// as a ShaHash.
func DoubleSha256SH(b []byte) ShaHash {
first := fastsha256.Sum256(b)
return ShaHash(fastsha256.Sum256(first[:]))
}

View file

@ -170,18 +170,14 @@ func (msg *MsgTx) TxSha() (ShaHash, error) {
// Encode the transaction and calculate double sha256 on the result. // Encode the transaction and calculate double sha256 on the result.
// Ignore the error returns since the only way the encode could fail // Ignore the error returns since the only way the encode could fail
// is being out of memory or due to nil pointers, both of which would // is being out of memory or due to nil pointers, both of which would
// cause a run-time panic. Also, SetBytes can't fail here due to the // cause a run-time panic.
// fact DoubleSha256 always returns a []byte of the right size
// regardless of input.
buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize())) buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize()))
_ = msg.Serialize(buf) _ = msg.Serialize(buf)
var sha ShaHash
_ = sha.SetBytes(DoubleSha256(buf.Bytes()))
// Even though this function can't currently fail, it still returns // Even though this function can't currently fail, it still returns
// a potential error to help future proof the API should a failure // a potential error to help future proof the API should a failure
// become possible. // become possible.
return sha, nil return DoubleSha256SH(buf.Bytes()), nil
} }
// Copy creates a deep copy of a transaction so that the original does not get // Copy creates a deep copy of a transaction so that the original does not get