2020-06-30 02:36:45 +00:00
|
|
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2020-06-28 17:58:21 +00:00
|
|
|
// Package deepprint walks a Go value recursively, in a predictable
|
|
|
|
// order, without looping, and prints each value out to a given
|
|
|
|
// Writer, which is assumed to be a hash.Hash, as this package doesn't
|
|
|
|
// format things nicely.
|
|
|
|
//
|
|
|
|
// This is intended as a lighter version of go-spew, etc. We don't need its
|
|
|
|
// features when our writer is just a hash.
|
|
|
|
package deepprint
|
|
|
|
|
|
|
|
import (
|
2021-05-10 20:29:56 +00:00
|
|
|
"bufio"
|
2020-06-28 17:58:21 +00:00
|
|
|
"crypto/sha256"
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
2021-05-10 21:15:31 +00:00
|
|
|
|
|
|
|
"inet.af/netaddr"
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
"tailscale.com/types/wgkey"
|
2020-06-28 17:58:21 +00:00
|
|
|
)
|
|
|
|
|
2020-07-29 01:47:23 +00:00
|
|
|
func Hash(v ...interface{}) string {
|
2020-06-28 17:58:21 +00:00
|
|
|
h := sha256.New()
|
2021-05-10 20:29:56 +00:00
|
|
|
// 64 matches the chunk size in crypto/sha256/sha256.go
|
|
|
|
b := bufio.NewWriterSize(h, 64)
|
|
|
|
Print(b, v)
|
|
|
|
b.Flush()
|
2020-06-28 17:58:21 +00:00
|
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
|
|
}
|
|
|
|
|
2020-07-29 01:47:23 +00:00
|
|
|
// UpdateHash sets last to the hash of v and reports whether its value changed.
|
|
|
|
func UpdateHash(last *string, v ...interface{}) (changed bool) {
|
|
|
|
sig := Hash(v)
|
|
|
|
if *last != sig {
|
|
|
|
*last = sig
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-05-10 20:29:56 +00:00
|
|
|
func Print(w *bufio.Writer, v ...interface{}) {
|
2020-06-28 17:58:21 +00:00
|
|
|
print(w, reflect.ValueOf(v), make(map[uintptr]bool))
|
|
|
|
}
|
|
|
|
|
2021-05-10 21:15:31 +00:00
|
|
|
var (
|
|
|
|
netaddrIPType = reflect.TypeOf(netaddr.IP{})
|
|
|
|
netaddrIPPrefix = reflect.TypeOf(netaddr.IPPrefix{})
|
|
|
|
wgkeyKeyType = reflect.TypeOf(wgkey.Key{})
|
|
|
|
wgkeyPrivateType = reflect.TypeOf(wgkey.Private{})
|
|
|
|
tailcfgDiscoKeyType = reflect.TypeOf(tailcfg.DiscoKey{})
|
|
|
|
)
|
|
|
|
|
2021-05-10 20:29:56 +00:00
|
|
|
func print(w *bufio.Writer, v reflect.Value, visited map[uintptr]bool) {
|
2020-06-28 17:58:21 +00:00
|
|
|
if !v.IsValid() {
|
|
|
|
return
|
|
|
|
}
|
2021-05-10 21:15:31 +00:00
|
|
|
|
|
|
|
// Special case some common types.
|
|
|
|
if v.CanInterface() {
|
|
|
|
switch v.Type() {
|
|
|
|
case netaddrIPType:
|
|
|
|
var b []byte
|
|
|
|
var err error
|
|
|
|
if v.CanAddr() {
|
|
|
|
x := v.Addr().Interface().(*netaddr.IP)
|
|
|
|
b, err = x.MarshalText()
|
|
|
|
} else {
|
|
|
|
x := v.Interface().(netaddr.IP)
|
|
|
|
b, err = x.MarshalText()
|
|
|
|
}
|
|
|
|
if err == nil {
|
|
|
|
w.Write(b)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case netaddrIPPrefix:
|
|
|
|
var b []byte
|
|
|
|
var err error
|
|
|
|
if v.CanAddr() {
|
|
|
|
x := v.Addr().Interface().(*netaddr.IPPrefix)
|
|
|
|
b, err = x.MarshalText()
|
|
|
|
} else {
|
|
|
|
x := v.Interface().(netaddr.IPPrefix)
|
|
|
|
b, err = x.MarshalText()
|
|
|
|
}
|
|
|
|
if err == nil {
|
|
|
|
w.Write(b)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
case wgkeyKeyType:
|
|
|
|
if v.CanAddr() {
|
|
|
|
x := v.Addr().Interface().(*wgkey.Key)
|
|
|
|
w.Write(x[:])
|
|
|
|
} else {
|
|
|
|
x := v.Interface().(wgkey.Key)
|
|
|
|
w.Write(x[:])
|
|
|
|
}
|
|
|
|
return
|
|
|
|
case wgkeyPrivateType:
|
|
|
|
if v.CanAddr() {
|
|
|
|
x := v.Addr().Interface().(*wgkey.Private)
|
|
|
|
w.Write(x[:])
|
|
|
|
} else {
|
|
|
|
x := v.Interface().(wgkey.Private)
|
|
|
|
w.Write(x[:])
|
|
|
|
}
|
|
|
|
return
|
|
|
|
case tailcfgDiscoKeyType:
|
|
|
|
if v.CanAddr() {
|
|
|
|
x := v.Addr().Interface().(*tailcfg.DiscoKey)
|
|
|
|
w.Write(x[:])
|
|
|
|
} else {
|
|
|
|
x := v.Interface().(tailcfg.DiscoKey)
|
|
|
|
w.Write(x[:])
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generic handling.
|
2020-06-28 17:58:21 +00:00
|
|
|
switch v.Kind() {
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unhandled kind %v for type %v", v.Kind(), v.Type()))
|
|
|
|
case reflect.Ptr:
|
|
|
|
ptr := v.Pointer()
|
|
|
|
if visited[ptr] {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
visited[ptr] = true
|
|
|
|
print(w, v.Elem(), visited)
|
|
|
|
return
|
|
|
|
case reflect.Struct:
|
|
|
|
fmt.Fprintf(w, "struct{\n")
|
|
|
|
t := v.Type()
|
|
|
|
for i, n := 0, v.NumField(); i < n; i++ {
|
|
|
|
sf := t.Field(i)
|
2021-05-10 20:29:56 +00:00
|
|
|
w.WriteString(sf.Name)
|
|
|
|
w.WriteString(": ")
|
2020-06-28 17:58:21 +00:00
|
|
|
print(w, v.Field(i), visited)
|
|
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
}
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
if v.Type().Elem().Kind() == reflect.Uint8 && v.CanInterface() {
|
|
|
|
fmt.Fprintf(w, "%q", v.Interface())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "[%d]{\n", v.Len())
|
|
|
|
for i, ln := 0, v.Len(); i < ln; i++ {
|
|
|
|
fmt.Fprintf(w, " [%d]: ", i)
|
|
|
|
print(w, v.Index(i), visited)
|
|
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
case reflect.Interface:
|
|
|
|
print(w, v.Elem(), visited)
|
|
|
|
case reflect.Map:
|
|
|
|
sm := newSortedMap(v)
|
|
|
|
fmt.Fprintf(w, "map[%d]{\n", len(sm.Key))
|
|
|
|
for i, k := range sm.Key {
|
|
|
|
print(w, k, visited)
|
|
|
|
fmt.Fprintf(w, ": ")
|
|
|
|
print(w, sm.Value[i], visited)
|
|
|
|
fmt.Fprintf(w, "\n")
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "}\n")
|
|
|
|
|
|
|
|
case reflect.String:
|
2021-05-10 20:29:56 +00:00
|
|
|
w.WriteString(v.String())
|
2020-06-28 17:58:21 +00:00
|
|
|
case reflect.Bool:
|
|
|
|
fmt.Fprintf(w, "%v", v.Bool())
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
|
|
fmt.Fprintf(w, "%v", v.Int())
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
|
|
fmt.Fprintf(w, "%v", v.Uint())
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
fmt.Fprintf(w, "%v", v.Float())
|
|
|
|
case reflect.Complex64, reflect.Complex128:
|
|
|
|
fmt.Fprintf(w, "%v", v.Complex())
|
|
|
|
}
|
|
|
|
}
|