mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
util/deephash: implement SelfHasher to allow types to hash themselves
Updates: corp#16409 Signed-off-by: Tom DNetto <tom@tailscale.com>
This commit is contained in:
parent
b4b2ec7801
commit
2aeef4e610
@ -292,6 +292,14 @@ func makeTypeHasher(t reflect.Type) typeHasherFunc {
|
||||
return hashAddr
|
||||
}
|
||||
|
||||
// Types that implement their own hashing.
|
||||
if t.Kind() != reflect.Pointer && t.Kind() != reflect.Interface {
|
||||
// A method can be implemented on either the value receiver or pointer receiver.
|
||||
if t.Implements(selfHasherType) || reflect.PointerTo(t).Implements(selfHasherType) {
|
||||
return makeSelfHasherImpl(t)
|
||||
}
|
||||
}
|
||||
|
||||
// Types that can have their memory representation directly hashed.
|
||||
if typeIsMemHashable(t) {
|
||||
return makeMemHasher(t.Size())
|
||||
@ -350,6 +358,13 @@ func hashAddr(h *hasher, p pointer) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeSelfHasherImpl(t reflect.Type) typeHasherFunc {
|
||||
return func(h *hasher, p pointer) {
|
||||
e := p.asValue(t)
|
||||
e.Interface().(SelfHasher).Hash(&h.Block512)
|
||||
}
|
||||
}
|
||||
|
||||
func hashString(h *hasher, p pointer) {
|
||||
s := *p.asString()
|
||||
h.HashUint64(uint64(len(s)))
|
||||
|
@ -29,6 +29,7 @@
|
||||
"tailscale.com/types/ptr"
|
||||
"tailscale.com/util/deephash/testtype"
|
||||
"tailscale.com/util/dnsname"
|
||||
"tailscale.com/util/hashx"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/router"
|
||||
@ -41,6 +42,14 @@ func (p appendBytes) AppendTo(b []byte) []byte {
|
||||
return append(b, p...)
|
||||
}
|
||||
|
||||
type implsSelfHasherValueRecv struct {
|
||||
emit uint64
|
||||
}
|
||||
|
||||
func (s implsSelfHasherValueRecv) Hash(h *hashx.Block512) {
|
||||
h.HashUint64(s.emit)
|
||||
}
|
||||
|
||||
func TestHash(t *testing.T) {
|
||||
type tuple [2]any
|
||||
type iface struct{ X any }
|
||||
@ -169,6 +178,12 @@ type scalars struct {
|
||||
b[0] = 1
|
||||
return b
|
||||
}()))}, wantEq: false},
|
||||
{in: tuple{&implsSelfHasher{}, &implsSelfHasher{}}, wantEq: true},
|
||||
{in: tuple{(*implsSelfHasher)(nil), (*implsSelfHasher)(nil)}, wantEq: true},
|
||||
{in: tuple{(*implsSelfHasher)(nil), &implsSelfHasher{}}, wantEq: false},
|
||||
{in: tuple{&implsSelfHasher{emit: 1}, &implsSelfHasher{emit: 2}}, wantEq: false},
|
||||
{in: tuple{implsSelfHasherValueRecv{emit: 1}, implsSelfHasherValueRecv{emit: 2}}, wantEq: false},
|
||||
{in: tuple{implsSelfHasherValueRecv{emit: 2}, implsSelfHasherValueRecv{emit: 2}}, wantEq: true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
@ -7,11 +7,25 @@
|
||||
"net/netip"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"tailscale.com/util/hashx"
|
||||
)
|
||||
|
||||
// SelfHasher is the interface implemented by types that can compute their own hash
|
||||
// by writing values through the given parameter.
|
||||
//
|
||||
// Implementations of Hash MUST NOT call `Reset` or `Sum` on the provided argument.
|
||||
//
|
||||
// This interface should not be considered stable and is likely to change in the
|
||||
// future.
|
||||
type SelfHasher interface {
|
||||
Hash(*hashx.Block512)
|
||||
}
|
||||
|
||||
var (
|
||||
timeTimeType = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||||
netipAddrType = reflect.TypeOf((*netip.Addr)(nil)).Elem()
|
||||
selfHasherType = reflect.TypeOf((*SelfHasher)(nil)).Elem()
|
||||
)
|
||||
|
||||
// typeIsSpecialized reports whether this type has specialized hashing.
|
||||
@ -21,6 +35,11 @@ func typeIsSpecialized(t reflect.Type) bool {
|
||||
case timeTimeType, netipAddrType:
|
||||
return true
|
||||
default:
|
||||
if t.Kind() != reflect.Pointer && t.Kind() != reflect.Interface {
|
||||
if t.Implements(selfHasherType) || reflect.PointerTo(t).Implements(selfHasherType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,17 @@
|
||||
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/structs"
|
||||
"tailscale.com/util/hashx"
|
||||
)
|
||||
|
||||
type implsSelfHasher struct {
|
||||
emit uint64
|
||||
}
|
||||
|
||||
func (s *implsSelfHasher) Hash(h *hashx.Block512) {
|
||||
h.HashUint64(s.emit)
|
||||
}
|
||||
|
||||
func TestTypeIsMemHashable(t *testing.T) {
|
||||
tests := []struct {
|
||||
val any
|
||||
@ -67,6 +76,7 @@ func TestTypeIsMemHashable(t *testing.T) {
|
||||
false},
|
||||
{[0]chan bool{}, true},
|
||||
{struct{ f [0]func() }{}, true},
|
||||
{&implsSelfHasher{}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := typeIsMemHashable(reflect.TypeOf(tt.val))
|
||||
@ -102,6 +112,7 @@ type RecursiveStruct struct {
|
||||
{val: unsafe.Pointer(nil), want: false},
|
||||
{val: make(RecursiveChan), want: true},
|
||||
{val: make(chan int), want: false},
|
||||
{val: (*implsSelfHasher)(nil), want: false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := typeIsRecursive(reflect.TypeOf(tt.val))
|
||||
|
Loading…
Reference in New Issue
Block a user