lbcd/wire/shahash.go
Dave Collins f5cdf2d6a8 Minor hashing-related optimizations.
This commit contains three classes of optimizations:
 - Reducing the number of unnecessary hash copies
 - Improve the performance of the DoubleSha256 function
 - A couple of minor optimizations of the ShaHash functions

The first class is a result of the Bytes function on a ShaHash making a
copy of the bytes before returning them.  It really should have been named
CloneBytes, but that would break the API now.

To address this, a comment has been added to the function which explicitly
calls out the copy behavior.  In addition, all call sites of .Bytes on a
ShaHash in the code base have been updated to simply slice the array when
a copy is not needed.  This saves a significant amount of data copying.

The second optimization modifies the DoubleSha256 function to directly use
fastsha256.Sum256 instead of the hasher interface.  This reduces the
number of allocations needed.  A benchmark for the function has been added
as well.

old: BenchmarkDoubleSha256  500000   3691 ns/op   192 B/op   3 allocs/op
new: BenchmarkDoubleSha256  500000   3081 ns/op    32 B/op   1 allocs/op

The final optimizations are for the ShaHash IsEqual and SetBytes functions
which have been modified to make use of the fact the type is an array and
remove an unneeded subslice.
2015-04-06 11:33:58 -05:00

111 lines
3.2 KiB
Go

// Copyright (c) 2013-2015 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"encoding/hex"
"fmt"
)
// Size of array used to store sha hashes. See ShaHash.
const HashSize = 32
// MaxHashStringSize is the maximum length of a ShaHash hash string.
const MaxHashStringSize = HashSize * 2
// ErrHashStrSize describes an error that indicates the caller specified a hash
// string that has too many characters.
var ErrHashStrSize = fmt.Errorf("max hash string length is %v bytes", MaxHashStringSize)
// ShaHash is used in several of the bitcoin messages and common structures. It
// typically represents the double sha256 of data.
type ShaHash [HashSize]byte
// String returns the ShaHash as the hexadecimal string of the byte-reversed
// hash.
func (hash ShaHash) String() string {
for i := 0; i < HashSize/2; i++ {
hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i]
}
return hex.EncodeToString(hash[:])
}
// Bytes returns the bytes which represent the hash as a byte slice.
//
// NOTE: This makes a copy of the bytes and should have probably been named
// CloneBytes. It is generally cheaper to just slice the hash directly thereby
// reusing the same bytes rather than calling this method.
func (hash *ShaHash) Bytes() []byte {
newHash := make([]byte, HashSize)
copy(newHash, hash[:])
return newHash
}
// SetBytes sets the bytes which represent the hash. An error is returned if
// the number of bytes passed in is not HashSize.
func (hash *ShaHash) SetBytes(newHash []byte) error {
nhlen := len(newHash)
if nhlen != HashSize {
return fmt.Errorf("invalid sha length of %v, want %v", nhlen,
HashSize)
}
copy(hash[:], newHash)
return nil
}
// IsEqual returns true if target is the same as hash.
func (hash *ShaHash) IsEqual(target *ShaHash) bool {
return *hash == *target
}
// NewShaHash returns a new ShaHash from a byte slice. An error is returned if
// the number of bytes passed in is not HashSize.
func NewShaHash(newHash []byte) (*ShaHash, error) {
var sh ShaHash
err := sh.SetBytes(newHash)
if err != nil {
return nil, err
}
return &sh, err
}
// NewShaHashFromStr creates a ShaHash from a hash string. The string should be
// the hexadecimal string of a byte-reversed hash, but any missing characters
// result in zero padding at the end of the ShaHash.
func NewShaHashFromStr(hash string) (*ShaHash, error) {
// Return error if hash string is too long.
if len(hash) > MaxHashStringSize {
return nil, ErrHashStrSize
}
// Hex decoder expects the hash to be a multiple of two.
if len(hash)%2 != 0 {
hash = "0" + hash
}
// Convert string hash to bytes.
buf, err := hex.DecodeString(hash)
if err != nil {
return nil, err
}
// Un-reverse the decoded bytes, copying into in leading bytes of a
// ShaHash. There is no need to explicitly pad the result as any
// missing (when len(buf) < HashSize) bytes from the decoded hex string
// will remain zeros at the end of the ShaHash.
var ret ShaHash
blen := len(buf)
mid := blen / 2
if blen%2 != 0 {
mid++
}
blen--
for i, b := range buf[:mid] {
ret[i], ret[blen-i] = buf[blen-i], b
}
return &ret, nil
}