|
|
@ -12,6 +12,7 @@ package deephash
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bufio"
|
|
|
|
"crypto/sha256"
|
|
|
|
"crypto/sha256"
|
|
|
|
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/hex"
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"hash"
|
|
|
|
"hash"
|
|
|
@ -21,12 +22,14 @@ import (
|
|
|
|
"sync"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const scratchSize = 128
|
|
|
|
|
|
|
|
|
|
|
|
// hasher is reusable state for hashing a value.
|
|
|
|
// hasher is reusable state for hashing a value.
|
|
|
|
// Get one via hasherPool.
|
|
|
|
// Get one via hasherPool.
|
|
|
|
type hasher struct {
|
|
|
|
type hasher struct {
|
|
|
|
h hash.Hash
|
|
|
|
h hash.Hash
|
|
|
|
bw *bufio.Writer
|
|
|
|
bw *bufio.Writer
|
|
|
|
scratch [128]byte
|
|
|
|
scratch [scratchSize]byte
|
|
|
|
visited map[uintptr]bool
|
|
|
|
visited map[uintptr]bool
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -56,8 +59,13 @@ func (h *hasher) Hash(v interface{}) (hash [sha256.Size]byte) {
|
|
|
|
h.h.Reset()
|
|
|
|
h.h.Reset()
|
|
|
|
h.print(reflect.ValueOf(v))
|
|
|
|
h.print(reflect.ValueOf(v))
|
|
|
|
h.bw.Flush()
|
|
|
|
h.bw.Flush()
|
|
|
|
h.h.Sum(hash[:0])
|
|
|
|
// Sum into scratch & copy out, as hash.Hash is an interface
|
|
|
|
return hash
|
|
|
|
// so the slice necessarily escapes, and there's no sha256
|
|
|
|
|
|
|
|
// concrete type exported and we don't want the 'hash' result
|
|
|
|
|
|
|
|
// parameter to escape to the heap:
|
|
|
|
|
|
|
|
h.h.Sum(h.scratch[:0])
|
|
|
|
|
|
|
|
copy(hash[:], h.scratch[:])
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var hasherPool = &sync.Pool{
|
|
|
|
var hasherPool = &sync.Pool{
|
|
|
@ -107,6 +115,16 @@ type appenderTo interface {
|
|
|
|
AppendTo([]byte) []byte
|
|
|
|
AppendTo([]byte) []byte
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (h *hasher) uint(i uint64) {
|
|
|
|
|
|
|
|
binary.BigEndian.PutUint64(h.scratch[:8], i)
|
|
|
|
|
|
|
|
h.bw.Write(h.scratch[:8])
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (h *hasher) int(i int) {
|
|
|
|
|
|
|
|
binary.BigEndian.PutUint64(h.scratch[:8], uint64(i))
|
|
|
|
|
|
|
|
h.bw.Write(h.scratch[:8])
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// print hashes v into w.
|
|
|
|
// print hashes v into w.
|
|
|
|
// It reports whether it was able to do so without hitting a cycle.
|
|
|
|
// It reports whether it was able to do so without hitting a cycle.
|
|
|
|
func (h *hasher) print(v reflect.Value) (acyclic bool) {
|
|
|
|
func (h *hasher) print(v reflect.Value) (acyclic bool) {
|
|
|
@ -140,31 +158,40 @@ func (h *hasher) print(v reflect.Value) (acyclic bool) {
|
|
|
|
return h.print(v.Elem())
|
|
|
|
return h.print(v.Elem())
|
|
|
|
case reflect.Struct:
|
|
|
|
case reflect.Struct:
|
|
|
|
acyclic = true
|
|
|
|
acyclic = true
|
|
|
|
w.WriteString("struct{\n")
|
|
|
|
w.WriteString("struct")
|
|
|
|
|
|
|
|
h.int(v.NumField())
|
|
|
|
for i, n := 0, v.NumField(); i < n; i++ {
|
|
|
|
for i, n := 0, v.NumField(); i < n; i++ {
|
|
|
|
fmt.Fprintf(w, " [%d]: ", i)
|
|
|
|
h.int(i)
|
|
|
|
if !h.print(v.Field(i)) {
|
|
|
|
if !h.print(v.Field(i)) {
|
|
|
|
acyclic = false
|
|
|
|
acyclic = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.WriteString("\n")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.WriteString("}\n")
|
|
|
|
|
|
|
|
return acyclic
|
|
|
|
return acyclic
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
|
|
|
|
vLen := v.Len()
|
|
|
|
|
|
|
|
if v.Kind() == reflect.Slice {
|
|
|
|
|
|
|
|
h.int(vLen)
|
|
|
|
|
|
|
|
}
|
|
|
|
if v.Type().Elem().Kind() == reflect.Uint8 && v.CanInterface() {
|
|
|
|
if v.Type().Elem().Kind() == reflect.Uint8 && v.CanInterface() {
|
|
|
|
fmt.Fprintf(w, "%q", v.Interface())
|
|
|
|
if vLen > 0 && vLen <= scratchSize {
|
|
|
|
|
|
|
|
// If it fits in scratch, avoid the Interface allocation.
|
|
|
|
|
|
|
|
// It seems tempting to do this for all sizes, doing
|
|
|
|
|
|
|
|
// scratchSize bytes at a time, but reflect.Slice seems
|
|
|
|
|
|
|
|
// to allocate, so it's not a win.
|
|
|
|
|
|
|
|
n := reflect.Copy(reflect.ValueOf(&h.scratch).Elem(), v)
|
|
|
|
|
|
|
|
w.Write(h.scratch[:n])
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Fprintf(w, "%s", v.Interface())
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "[%d]{\n", v.Len())
|
|
|
|
|
|
|
|
acyclic = true
|
|
|
|
acyclic = true
|
|
|
|
for i, ln := 0, v.Len(); i < ln; i++ {
|
|
|
|
for i := 0; i < vLen; i++ {
|
|
|
|
fmt.Fprintf(w, " [%d]: ", i)
|
|
|
|
h.int(i)
|
|
|
|
if !h.print(v.Index(i)) {
|
|
|
|
if !h.print(v.Index(i)) {
|
|
|
|
acyclic = false
|
|
|
|
acyclic = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.WriteString("\n")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.WriteString("}\n")
|
|
|
|
|
|
|
|
return acyclic
|
|
|
|
return acyclic
|
|
|
|
case reflect.Interface:
|
|
|
|
case reflect.Interface:
|
|
|
|
return h.print(v.Elem())
|
|
|
|
return h.print(v.Elem())
|
|
|
@ -196,13 +223,14 @@ func (h *hasher) print(v reflect.Value) (acyclic bool) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return h.hashMapFallback(v)
|
|
|
|
return h.hashMapFallback(v)
|
|
|
|
case reflect.String:
|
|
|
|
case reflect.String:
|
|
|
|
|
|
|
|
h.int(v.Len())
|
|
|
|
w.WriteString(v.String())
|
|
|
|
w.WriteString(v.String())
|
|
|
|
case reflect.Bool:
|
|
|
|
case reflect.Bool:
|
|
|
|
w.Write(strconv.AppendBool(h.scratch[:0], v.Bool()))
|
|
|
|
w.Write(strconv.AppendBool(h.scratch[:0], v.Bool()))
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
w.Write(strconv.AppendInt(h.scratch[:0], v.Int(), 10))
|
|
|
|
w.Write(strconv.AppendInt(h.scratch[:0], v.Int(), 10))
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
|
|
w.Write(strconv.AppendUint(h.scratch[:0], v.Uint(), 10))
|
|
|
|
h.uint(v.Uint())
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
w.Write(strconv.AppendUint(h.scratch[:0], math.Float64bits(v.Float()), 10))
|
|
|
|
w.Write(strconv.AppendUint(h.scratch[:0], math.Float64bits(v.Float()), 10))
|
|
|
|
case reflect.Complex64, reflect.Complex128:
|
|
|
|
case reflect.Complex64, reflect.Complex128:
|
|
|
|