@ -38,21 +38,15 @@ type hasher struct {
visitStack visitStack
visitStack visitStack
}
}
// newHasher initializes a new hasher, for use by hasherPool.
func ( h * hasher ) reset ( ) {
func newHasher ( ) * hasher {
if h . h == nil {
h := & hasher { h : sha256 . New ( ) }
h . h = sha256 . New ( )
}
if h . bw == nil {
h . bw = bufio . NewWriterSize ( h . h , h . h . BlockSize ( ) )
h . bw = bufio . NewWriterSize ( h . h , h . h . BlockSize ( ) )
return h
}
}
h . bw . Flush ( )
// setBufioWriter switches the bufio writer to w after flushing
h . h . Reset ( )
// any output to the old one. It then also returns the old one, so
// the caller can switch back to it.
func ( h * hasher ) setBufioWriter ( w * bufio . Writer ) ( old * bufio . Writer ) {
old = h . bw
old . Flush ( )
h . bw = w
return old
}
}
// Sum is an opaque checksum type that is comparable.
// Sum is an opaque checksum type that is comparable.
@ -60,6 +54,12 @@ type Sum struct {
sum [ sha256 . Size ] byte
sum [ sha256 . Size ] byte
}
}
func ( s1 * Sum ) xor ( s2 Sum ) {
for i := 0 ; i < sha256 . Size ; i ++ {
s1 . sum [ i ] ^ = s2 . sum [ i ]
}
}
func ( s Sum ) String ( ) string {
func ( s Sum ) String ( ) string {
return hex . EncodeToString ( s . sum [ : ] )
return hex . EncodeToString ( s . sum [ : ] )
}
}
@ -69,34 +69,31 @@ var (
seed uint64
seed uint64
)
)
// Hash returns the hash of v.
func ( h * hasher ) sum ( ) ( s Sum ) {
func ( h * hasher ) Hash ( v interface { } ) ( hash Sum ) {
h . bw . Flush ( )
h . h . Reset ( )
once . Do ( func ( ) {
seed = uint64 ( time . Now ( ) . UnixNano ( ) )
} )
h . uint ( seed )
h . print ( reflect . ValueOf ( v ) )
h . bw . Flush ( )
h . bw . Flush ( )
// Sum into scratch & copy out, as hash.Hash is an interface
// Sum into scratch & copy out, as hash.Hash is an interface
// so the slice necessarily escapes, and there's no sha256
// so the slice necessarily escapes, and there's no sha256
// concrete type exported and we don't want the 'hash' result
// concrete type exported and we don't want the 'hash' result
// parameter to escape to the heap:
// parameter to escape to the heap:
h . h . Sum ( h . scratch [ : 0 ] )
copy ( s . sum [ : ] , h . h . Sum ( h . scratch [ : 0 ] ) )
copy ( hash . sum [ : ] , h . scratch [ : ] )
return s
return
}
}
var hasherPool = & sync . Pool {
var hasherPool = & sync . Pool {
New : func ( ) interface { } { return newHasher ( ) } ,
New : func ( ) interface { } { return new ( hasher ) } ,
}
}
// Hash returns the hash of v.
// Hash returns the hash of v.
func Hash ( v interface { } ) Sum {
func Hash ( v interface { } ) ( s Sum ) {
h := hasherPool . Get ( ) . ( * hasher )
h := hasherPool . Get ( ) . ( * hasher )
defer hasherPool . Put ( h )
defer hasherPool . Put ( h )
return h . Hash ( v )
h . reset ( )
once . Do ( func ( ) {
seed = uint64 ( time . Now ( ) . UnixNano ( ) )
} )
h . uint ( seed )
h . print ( reflect . ValueOf ( v ) )
return h . sum ( )
}
}
// Update sets last to the hash of v and reports whether its value changed.
// Update sets last to the hash of v and reports whether its value changed.
@ -243,53 +240,25 @@ func (h *hasher) print(v reflect.Value) {
}
}
type mapHasher struct {
type mapHasher struct {
xbuf [ sha256 . Size ] byte // XOR'ed accumulated buffer
h hasher
ebuf [ sha256 . Size ] byte // scratch buffer
s256 hash . Hash // sha256 hash.Hash
bw * bufio . Writer // to hasher into ebuf
val valueCache // re-usable values for map iteration
val valueCache // re-usable values for map iteration
iter * reflect . MapIter // re-usable map iterator
iter reflect . MapIter // re-usable map iterator
}
func ( mh * mapHasher ) Reset ( ) {
for i := range mh . xbuf {
mh . xbuf [ i ] = 0
}
}
func ( mh * mapHasher ) startEntry ( ) {
for i := range mh . ebuf {
mh . ebuf [ i ] = 0
}
mh . bw . Flush ( )
mh . s256 . Reset ( )
}
func ( mh * mapHasher ) endEntry ( ) {
mh . bw . Flush ( )
for i , b := range mh . s256 . Sum ( mh . ebuf [ : 0 ] ) {
mh . xbuf [ i ] ^ = b
}
}
}
var mapHasherPool = & sync . Pool {
var mapHasherPool = & sync . Pool {
New : func ( ) interface { } {
New : func ( ) interface { } { return new ( mapHasher ) } ,
mh := new ( mapHasher )
mh . s256 = sha256 . New ( )
mh . bw = bufio . NewWriter ( mh . s256 )
mh . val = make ( valueCache )
mh . iter = new ( reflect . MapIter )
return mh
} ,
}
}
type valueCache map [ reflect . Type ] reflect . Value
type valueCache map [ reflect . Type ] reflect . Value
func ( c valueCache ) get ( t reflect . Type ) reflect . Value {
func ( c * valueCache ) get ( t reflect . Type ) reflect . Value {
v , ok := c [ t ]
v , ok := ( * c ) [ t ]
if ! ok {
if ! ok {
v = reflect . New ( t ) . Elem ( )
v = reflect . New ( t ) . Elem ( )
c [ t ] = v
if * c == nil {
* c = make ( valueCache )
}
( * c ) [ t ] = v
}
}
return v
return v
}
}
@ -301,25 +270,22 @@ func (c valueCache) get(t reflect.Type) reflect.Value {
func ( h * hasher ) hashMap ( v reflect . Value ) {
func ( h * hasher ) hashMap ( v reflect . Value ) {
mh := mapHasherPool . Get ( ) . ( * mapHasher )
mh := mapHasherPool . Get ( ) . ( * mapHasher )
defer mapHasherPool . Put ( mh )
defer mapHasherPool . Put ( mh )
mh . Reset ( )
iter := mapIter ( & mh . iter , v )
iter := mapIter ( mh . iter , v )
defer mapIter ( & mh . iter , reflect . Value { } ) // avoid pinning v from mh.iter when we return
defer mapIter ( mh . iter , reflect . Value { } ) // avoid pinning v from mh.iter when we return
// Temporarily switch to the map hasher's bufio.Writer.
oldw := h . setBufioWriter ( mh . bw )
defer h . setBufioWriter ( oldw )
var sum Sum
k := mh . val . get ( v . Type ( ) . Key ( ) )
k := mh . val . get ( v . Type ( ) . Key ( ) )
e := mh . val . get ( v . Type ( ) . Elem ( ) )
e := mh . val . get ( v . Type ( ) . Elem ( ) )
mh . h . visitStack = h . visitStack // always use the parent's visit stack to avoid cycles
for iter . Next ( ) {
for iter . Next ( ) {
key := iterKey ( iter , k )
key := iterKey ( iter , k )
val := iterVal ( iter , e )
val := iterVal ( iter , e )
mh . startEntry ( )
mh . h . re se t( )
h. print ( key )
mh. h. print ( key )
h. print ( val )
mh. h. print ( val )
mh. endEntry ( )
sum. xor ( mh . h . sum ( ) )
}
}
oldw. Write ( mh . xbuf [ : ] )
h. bw . Write ( append ( h . scratch [ : 0 ] , sum . sum [ : ] ... ) ) // append into scratch to avoid heap allocation
}
}
// visitStack is a stack of pointers visited.
// visitStack is a stack of pointers visited.