deepprint.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package deepprint walks a Go value recursively, in a predictable
  5. // order, without looping, and prints each value out to a given
  6. // Writer, which is assumed to be a hash.Hash, as this package doesn't
  7. // format things nicely.
  8. //
  9. // This is intended as a lighter version of go-spew, etc. We don't need its
  10. // features when our writer is just a hash.
  11. package deepprint
  12. import (
  13. "crypto/sha256"
  14. "fmt"
  15. "io"
  16. "reflect"
  17. )
  18. func Hash(v ...interface{}) string {
  19. h := sha256.New()
  20. Print(h, v)
  21. return fmt.Sprintf("%x", h.Sum(nil))
  22. }
  23. // UpdateHash sets last to the hash of v and reports whether its value changed.
  24. func UpdateHash(last *string, v ...interface{}) (changed bool) {
  25. sig := Hash(v)
  26. if *last != sig {
  27. *last = sig
  28. return true
  29. }
  30. return false
  31. }
  32. func Print(w io.Writer, v ...interface{}) {
  33. print(w, reflect.ValueOf(v), make(map[uintptr]bool))
  34. }
  35. func print(w io.Writer, v reflect.Value, visited map[uintptr]bool) {
  36. if !v.IsValid() {
  37. return
  38. }
  39. switch v.Kind() {
  40. default:
  41. panic(fmt.Sprintf("unhandled kind %v for type %v", v.Kind(), v.Type()))
  42. case reflect.Ptr:
  43. ptr := v.Pointer()
  44. if visited[ptr] {
  45. return
  46. }
  47. visited[ptr] = true
  48. print(w, v.Elem(), visited)
  49. return
  50. case reflect.Struct:
  51. fmt.Fprintf(w, "struct{\n")
  52. t := v.Type()
  53. for i, n := 0, v.NumField(); i < n; i++ {
  54. sf := t.Field(i)
  55. fmt.Fprintf(w, "%s: ", sf.Name)
  56. print(w, v.Field(i), visited)
  57. fmt.Fprintf(w, "\n")
  58. }
  59. case reflect.Slice, reflect.Array:
  60. if v.Type().Elem().Kind() == reflect.Uint8 && v.CanInterface() {
  61. fmt.Fprintf(w, "%q", v.Interface())
  62. return
  63. }
  64. fmt.Fprintf(w, "[%d]{\n", v.Len())
  65. for i, ln := 0, v.Len(); i < ln; i++ {
  66. fmt.Fprintf(w, " [%d]: ", i)
  67. print(w, v.Index(i), visited)
  68. fmt.Fprintf(w, "\n")
  69. }
  70. fmt.Fprintf(w, "}\n")
  71. case reflect.Interface:
  72. print(w, v.Elem(), visited)
  73. case reflect.Map:
  74. sm := newSortedMap(v)
  75. fmt.Fprintf(w, "map[%d]{\n", len(sm.Key))
  76. for i, k := range sm.Key {
  77. print(w, k, visited)
  78. fmt.Fprintf(w, ": ")
  79. print(w, sm.Value[i], visited)
  80. fmt.Fprintf(w, "\n")
  81. }
  82. fmt.Fprintf(w, "}\n")
  83. case reflect.String:
  84. fmt.Fprintf(w, "%s", v.String())
  85. case reflect.Bool:
  86. fmt.Fprintf(w, "%v", v.Bool())
  87. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  88. fmt.Fprintf(w, "%v", v.Int())
  89. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  90. fmt.Fprintf(w, "%v", v.Uint())
  91. case reflect.Float32, reflect.Float64:
  92. fmt.Fprintf(w, "%v", v.Float())
  93. case reflect.Complex64, reflect.Complex128:
  94. fmt.Fprintf(w, "%v", v.Complex())
  95. }
  96. }