mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-04 07:25:39 +00:00
WIP
Change-Id: Ibc7de639b31ed85c1270f5f9a889b4e6b4228be7 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
ab60f28227
commit
4acfd07815
@ -424,11 +424,56 @@ func genHashPtrToMemoryRange(eleType reflect.Type) typeHasherFunc {
|
||||
}
|
||||
}
|
||||
|
||||
const debug = false
|
||||
const debug = true
|
||||
|
||||
// canMaybeFastPath reports whether t is a type that we might be able to
|
||||
// hash quickly at runtime with a typeHasherFunc.
|
||||
//
|
||||
// If it returns false, the slow path should be used directly.
|
||||
func canMaybeFastPath(t reflect.Type) (ret bool) {
|
||||
defer func() {
|
||||
if !ret {
|
||||
log.Printf("Can't on %v", t)
|
||||
}
|
||||
}()
|
||||
ti := getTypeInfo(t)
|
||||
if ti.isRecursive {
|
||||
return false
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Map, reflect.Interface, reflect.Func, reflect.UnsafePointer, reflect.Chan:
|
||||
return false
|
||||
case reflect.Array:
|
||||
return t.Size() == 0 || canMaybeFastPath(t.Elem())
|
||||
case reflect.Slice, reflect.Pointer:
|
||||
return canMaybeFastPath(t.Elem())
|
||||
case reflect.Struct:
|
||||
if t == timeTimeType || t.Implements(appenderToType) {
|
||||
return true
|
||||
}
|
||||
for i, n := 0, t.NumField(); i < n; i++ {
|
||||
sf := t.Field(i)
|
||||
if sf.Type.Size() == 0 {
|
||||
continue
|
||||
}
|
||||
if !canMaybeFastPath(sf.Type) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func genTypeHasher(t reflect.Type) typeHasherFunc {
|
||||
fastable := canMaybeFastPath(t)
|
||||
if debug {
|
||||
log.Printf("generating func for %v", t)
|
||||
log.Printf("generating func for %v; fastable=%v", t, fastable)
|
||||
}
|
||||
if !fastable {
|
||||
return func(h *hasher, v reflect.Value) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
switch t.Kind() {
|
||||
|
@ -317,6 +317,45 @@ type RecursiveStruct struct {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanMaybeFastPath(t *testing.T) {
|
||||
type RecursiveStruct struct {
|
||||
v *RecursiveStruct
|
||||
}
|
||||
type RecursiveChan chan *RecursiveChan
|
||||
type FastStruct struct{ _, _ string }
|
||||
|
||||
tests := []struct {
|
||||
val any
|
||||
want bool
|
||||
}{
|
||||
{val: 42, want: true},
|
||||
{val: "string", want: true},
|
||||
{val: 1 + 2i, want: true},
|
||||
{val: struct{}{}, want: true},
|
||||
{val: (*RecursiveStruct)(nil), want: false},
|
||||
{val: RecursiveStruct{}, want: false},
|
||||
{val: FastStruct{}, want: true},
|
||||
{val: time.Unix(0, 0), want: true},
|
||||
{val: structs.Incomparable{}, want: true}, // ignore its [0]func()
|
||||
{val: tailcfg.NetPortRange{}, want: true}, // uses structs.Incomparable
|
||||
{val: (*tailcfg.Node)(nil), want: true},
|
||||
{val: map[string]bool{}, want: false},
|
||||
{val: func() {}, want: false},
|
||||
{val: make(chan int), want: false},
|
||||
{val: unsafe.Pointer(nil), want: false},
|
||||
{val: make(RecursiveChan), want: false},
|
||||
{val: make(chan int), want: false},
|
||||
{val: tailcfg.SSHRule{}, want: false}, // contains a map
|
||||
{val: (*tailcfg.SSHRule)(nil), want: false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := canMaybeFastPath(reflect.TypeOf(tt.val))
|
||||
if got != tt.want {
|
||||
t.Errorf("for type %T: got %v, want %v", tt.val, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type IntThenByte struct {
|
||||
i int
|
||||
b byte
|
||||
@ -611,6 +650,17 @@ func TestGetTypeHasher(t *testing.T) {
|
||||
val: &tailcfg.Node{},
|
||||
out: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x140001-01-01T00:00:00Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x140001-01-01T00:00:00Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "no_hashing_for_mixed_fast_slow_type",
|
||||
val: struct {
|
||||
A, B string
|
||||
M map[string]string
|
||||
}{
|
||||
"foo", "bar", map[string]string{"alice": "bob"},
|
||||
},
|
||||
want: false,
|
||||
out: "", // no "foo" or "bar" included
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
Loading…
Reference in New Issue
Block a user