|
|
|
@ -180,6 +180,7 @@ var uint8Type = reflect.TypeOf(byte(0))
|
|
|
|
|
// typeInfo describes properties of a type.
|
|
|
|
|
type typeInfo struct {
|
|
|
|
|
rtype reflect.Type
|
|
|
|
|
canMemHash bool
|
|
|
|
|
isRecursive bool
|
|
|
|
|
|
|
|
|
|
// elemTypeInfo is the element type's typeInfo.
|
|
|
|
@ -218,6 +219,7 @@ func getTypeInfoLocked(t reflect.Type, incomplete map[reflect.Type]*typeInfo) *t
|
|
|
|
|
ti := &typeInfo{
|
|
|
|
|
rtype: t,
|
|
|
|
|
isRecursive: typeIsRecursive(t),
|
|
|
|
|
canMemHash: canMemHash(t),
|
|
|
|
|
}
|
|
|
|
|
incomplete[t] = ti
|
|
|
|
|
|
|
|
|
@ -311,6 +313,34 @@ func typeIsRecursive(t reflect.Type) bool {
|
|
|
|
|
return visitType(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// canMemHash reports whether a slice of t can be hashed by looking at its
|
|
|
|
|
// contiguous bytes in memory alone. (e.g. structs with gaps aren't memhashable)
|
|
|
|
|
func canMemHash(t reflect.Type) bool {
|
|
|
|
|
switch t.Kind() {
|
|
|
|
|
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
|
|
|
reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
|
|
|
reflect.Float64, reflect.Float32, reflect.Complex128, reflect.Complex64:
|
|
|
|
|
return true
|
|
|
|
|
case reflect.Array:
|
|
|
|
|
return canMemHash(t.Elem())
|
|
|
|
|
case reflect.Struct:
|
|
|
|
|
var sumFieldSize uintptr
|
|
|
|
|
for i, numField := 0, t.NumField(); i < numField; i++ {
|
|
|
|
|
sf := t.Field(i)
|
|
|
|
|
if !canMemHash(sf.Type) {
|
|
|
|
|
// Special case for 0-width fields that aren't at the end.
|
|
|
|
|
if sf.Type.Size() == 0 && i < numField-1 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
sumFieldSize += sf.Type.Size()
|
|
|
|
|
}
|
|
|
|
|
return sumFieldSize == t.Size() // else there are gaps
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *hasher) hashValue(v reflect.Value, forceCycleChecking bool) {
|
|
|
|
|
if !v.IsValid() {
|
|
|
|
|
return
|
|
|
|
|