diff --git a/util/deephash/deephash.go b/util/deephash/deephash.go index 621d3e69d..941952818 100644 --- a/util/deephash/deephash.go +++ b/util/deephash/deephash.go @@ -230,69 +230,6 @@ func (ti *typeInfo) buildHashFuncOnce() { ti.hashFuncLazy = genTypeHasher(ti) } -// fieldInfo describes a struct field. -type fieldInfo struct { - index int // index of field for reflect.Value.Field(n); -1 if invalid - typeInfo *typeInfo - canMemHash bool - offset uintptr // when we can memhash the field - size uintptr // when we can memhash the field -} - -// mergeContiguousFieldsCopy returns a copy of f with contiguous memhashable fields -// merged together. Such fields get a bogus index and fu value. -func mergeContiguousFieldsCopy(in []fieldInfo) []fieldInfo { - ret := make([]fieldInfo, 0, len(in)) - var last *fieldInfo - for _, f := range in { - // Combine two fields if they're both contiguous & memhash-able. - if f.canMemHash && last != nil && last.canMemHash && last.offset+last.size == f.offset { - last.size += f.size - last.index = -1 - last.typeInfo = nil - } else { - ret = append(ret, f) - last = &ret[len(ret)-1] - } - } - return ret -} - -// genHashStructFields generates a typeHasherFunc for t, which must be of kind Struct. -func genHashStructFields(t reflect.Type) typeHasherFunc { - fields := make([]fieldInfo, 0, t.NumField()) - for i, n := 0, t.NumField(); i < n; i++ { - sf := t.Field(i) - if sf.Type.Size() == 0 { - continue - } - fields = append(fields, fieldInfo{ - index: i, - typeInfo: getTypeInfo(sf.Type), - canMemHash: typeIsMemHashable(sf.Type), - offset: sf.Offset, - size: sf.Type.Size(), - }) - } - fields = mergeContiguousFieldsCopy(fields) - return structHasher{fields}.hash -} - -type structHasher struct { - fields []fieldInfo -} - -func (sh structHasher) hash(h *hasher, p pointer) { - for _, f := range sh.fields { - pf := p.structField(f.index, f.offset, f.size) - if f.canMemHash { - h.HashBytes(pf.asMemory(f.size)) - } else { - f.typeInfo.hasher()(h, pf) - } - } -} - func genTypeHasher(ti *typeInfo) typeHasherFunc { t := ti.rtype @@ -317,7 +254,7 @@ func genTypeHasher(ti *typeInfo) typeHasherFunc { case reflect.Slice: return makeSliceHasher(t) case reflect.Struct: - return genHashStructFields(t) + return makeStructHasher(t) case reflect.Map: return func(h *hasher, p pointer) { v := p.asValue(t).Elem() // reflect.Map kind @@ -474,6 +411,53 @@ func makeSliceHasher(t reflect.Type) typeHasherFunc { } } +func makeStructHasher(t reflect.Type) typeHasherFunc { + type fieldHasher struct { + idx int // index of field for reflect.Type.Field(n); negative if memory is directly hashable + hash typeHasherFunc // only valid if idx is not negative + offset uintptr + size uintptr + } + var once sync.Once + var fields []fieldHasher + init := func() { + for i, numField := 0, t.NumField(); i < numField; i++ { + sf := t.Field(i) + f := fieldHasher{i, nil, sf.Offset, sf.Type.Size()} + if typeIsMemHashable(sf.Type) { + f.idx = -1 + } + + // Combine with previous field if both contiguous and mem-hashable. + if f.idx < 0 && len(fields) > 0 { + if last := &fields[len(fields)-1]; last.idx < 0 && last.offset+last.size == f.offset { + last.size += f.size + continue + } + } + fields = append(fields, f) + } + + for i, f := range fields { + if f.idx >= 0 { + fields[i].hash = getTypeInfo(t.Field(f.idx).Type).hasher() + } + } + } + + return func(h *hasher, p pointer) { + once.Do(init) + for _, field := range fields { + pf := p.structField(field.idx, field.offset, field.size) + if field.idx < 0 { + h.HashBytes(pf.asMemory(field.size)) + } else { + field.hash(h, pf) + } + } + } +} + func getTypeInfo(t reflect.Type) *typeInfo { if f, ok := typeInfoMap.Load(t); ok { return f.(*typeInfo)