|
Packit |
63bb0d |
/*
|
|
Packit |
63bb0d |
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
|
Packit |
63bb0d |
*
|
|
Packit |
63bb0d |
* Permission to use, copy, modify, and distribute this software for any
|
|
Packit |
63bb0d |
* purpose with or without fee is hereby granted, provided that the above
|
|
Packit |
63bb0d |
* copyright notice and this permission notice appear in all copies.
|
|
Packit |
63bb0d |
*
|
|
Packit |
63bb0d |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
Packit |
63bb0d |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
Packit |
63bb0d |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
Packit |
63bb0d |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
Packit |
63bb0d |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
Packit |
63bb0d |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
Packit |
63bb0d |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
Packit |
63bb0d |
*/
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
package spew
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit |
63bb0d |
"bytes"
|
|
Packit |
63bb0d |
"fmt"
|
|
Packit |
63bb0d |
"io"
|
|
Packit |
63bb0d |
"reflect"
|
|
Packit |
63bb0d |
"sort"
|
|
Packit |
63bb0d |
"strconv"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
|
Packit |
63bb0d |
// the technique used in the fmt package.
|
|
Packit |
63bb0d |
var (
|
|
Packit |
63bb0d |
panicBytes = []byte("(PANIC=")
|
|
Packit |
63bb0d |
plusBytes = []byte("+")
|
|
Packit |
63bb0d |
iBytes = []byte("i")
|
|
Packit |
63bb0d |
trueBytes = []byte("true")
|
|
Packit |
63bb0d |
falseBytes = []byte("false")
|
|
Packit |
63bb0d |
interfaceBytes = []byte("(interface {})")
|
|
Packit |
63bb0d |
commaNewlineBytes = []byte(",\n")
|
|
Packit |
63bb0d |
newlineBytes = []byte("\n")
|
|
Packit |
63bb0d |
openBraceBytes = []byte("{")
|
|
Packit |
63bb0d |
openBraceNewlineBytes = []byte("{\n")
|
|
Packit |
63bb0d |
closeBraceBytes = []byte("}")
|
|
Packit |
63bb0d |
asteriskBytes = []byte("*")
|
|
Packit |
63bb0d |
colonBytes = []byte(":")
|
|
Packit |
63bb0d |
colonSpaceBytes = []byte(": ")
|
|
Packit |
63bb0d |
openParenBytes = []byte("(")
|
|
Packit |
63bb0d |
closeParenBytes = []byte(")")
|
|
Packit |
63bb0d |
spaceBytes = []byte(" ")
|
|
Packit |
63bb0d |
pointerChainBytes = []byte("->")
|
|
Packit |
63bb0d |
nilAngleBytes = []byte("<nil>")
|
|
Packit |
63bb0d |
maxNewlineBytes = []byte("<max depth reached>\n")
|
|
Packit |
63bb0d |
maxShortBytes = []byte("<max>")
|
|
Packit |
63bb0d |
circularBytes = []byte("<already shown>")
|
|
Packit |
63bb0d |
circularShortBytes = []byte("<shown>")
|
|
Packit |
63bb0d |
invalidAngleBytes = []byte("<invalid>")
|
|
Packit |
63bb0d |
openBracketBytes = []byte("[")
|
|
Packit |
63bb0d |
closeBracketBytes = []byte("]")
|
|
Packit |
63bb0d |
percentBytes = []byte("%")
|
|
Packit |
63bb0d |
precisionBytes = []byte(".")
|
|
Packit |
63bb0d |
openAngleBytes = []byte("<")
|
|
Packit |
63bb0d |
closeAngleBytes = []byte(">")
|
|
Packit |
63bb0d |
openMapBytes = []byte("map[")
|
|
Packit |
63bb0d |
closeMapBytes = []byte("]")
|
|
Packit |
63bb0d |
lenEqualsBytes = []byte("len=")
|
|
Packit |
63bb0d |
capEqualsBytes = []byte("cap=")
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// hexDigits is used to map a decimal value to a hex digit.
|
|
Packit |
63bb0d |
var hexDigits = "0123456789abcdef"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// catchPanic handles any panics that might occur during the handleMethods
|
|
Packit |
63bb0d |
// calls.
|
|
Packit |
63bb0d |
func catchPanic(w io.Writer, v reflect.Value) {
|
|
Packit |
63bb0d |
if err := recover(); err != nil {
|
|
Packit |
63bb0d |
w.Write(panicBytes)
|
|
Packit |
63bb0d |
fmt.Fprintf(w, "%v", err)
|
|
Packit |
63bb0d |
w.Write(closeParenBytes)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// handleMethods attempts to call the Error and String methods on the underlying
|
|
Packit |
63bb0d |
// type the passed reflect.Value represents and outputes the result to Writer w.
|
|
Packit |
63bb0d |
//
|
|
Packit |
63bb0d |
// It handles panics in any called methods by catching and displaying the error
|
|
Packit |
63bb0d |
// as the formatted value.
|
|
Packit |
63bb0d |
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
|
Packit |
63bb0d |
// We need an interface to check if the type implements the error or
|
|
Packit |
63bb0d |
// Stringer interface. However, the reflect package won't give us an
|
|
Packit |
63bb0d |
// interface on certain things like unexported struct fields in order
|
|
Packit |
63bb0d |
// to enforce visibility rules. We use unsafe, when it's available,
|
|
Packit |
63bb0d |
// to bypass these restrictions since this package does not mutate the
|
|
Packit |
63bb0d |
// values.
|
|
Packit |
63bb0d |
if !v.CanInterface() {
|
|
Packit |
63bb0d |
if UnsafeDisabled {
|
|
Packit |
63bb0d |
return false
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
v = unsafeReflectValue(v)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Choose whether or not to do error and Stringer interface lookups against
|
|
Packit |
63bb0d |
// the base type or a pointer to the base type depending on settings.
|
|
Packit |
63bb0d |
// Technically calling one of these methods with a pointer receiver can
|
|
Packit |
63bb0d |
// mutate the value, however, types which choose to satisify an error or
|
|
Packit |
63bb0d |
// Stringer interface with a pointer receiver should not be mutating their
|
|
Packit |
63bb0d |
// state inside these interface methods.
|
|
Packit |
63bb0d |
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
|
Packit |
63bb0d |
v = unsafeReflectValue(v)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
if v.CanAddr() {
|
|
Packit |
63bb0d |
v = v.Addr()
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Is it an error or Stringer?
|
|
Packit |
63bb0d |
switch iface := v.Interface().(type) {
|
|
Packit |
63bb0d |
case error:
|
|
Packit |
63bb0d |
defer catchPanic(w, v)
|
|
Packit |
63bb0d |
if cs.ContinueOnMethod {
|
|
Packit |
63bb0d |
w.Write(openParenBytes)
|
|
Packit |
63bb0d |
w.Write([]byte(iface.Error()))
|
|
Packit |
63bb0d |
w.Write(closeParenBytes)
|
|
Packit |
63bb0d |
w.Write(spaceBytes)
|
|
Packit |
63bb0d |
return false
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
w.Write([]byte(iface.Error()))
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
case fmt.Stringer:
|
|
Packit |
63bb0d |
defer catchPanic(w, v)
|
|
Packit |
63bb0d |
if cs.ContinueOnMethod {
|
|
Packit |
63bb0d |
w.Write(openParenBytes)
|
|
Packit |
63bb0d |
w.Write([]byte(iface.String()))
|
|
Packit |
63bb0d |
w.Write(closeParenBytes)
|
|
Packit |
63bb0d |
w.Write(spaceBytes)
|
|
Packit |
63bb0d |
return false
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
w.Write([]byte(iface.String()))
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return false
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// printBool outputs a boolean value as true or false to Writer w.
|
|
Packit |
63bb0d |
func printBool(w io.Writer, val bool) {
|
|
Packit |
63bb0d |
if val {
|
|
Packit |
63bb0d |
w.Write(trueBytes)
|
|
Packit |
63bb0d |
} else {
|
|
Packit |
63bb0d |
w.Write(falseBytes)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// printInt outputs a signed integer value to Writer w.
|
|
Packit |
63bb0d |
func printInt(w io.Writer, val int64, base int) {
|
|
Packit |
63bb0d |
w.Write([]byte(strconv.FormatInt(val, base)))
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// printUint outputs an unsigned integer value to Writer w.
|
|
Packit |
63bb0d |
func printUint(w io.Writer, val uint64, base int) {
|
|
Packit |
63bb0d |
w.Write([]byte(strconv.FormatUint(val, base)))
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// printFloat outputs a floating point value using the specified precision,
|
|
Packit |
63bb0d |
// which is expected to be 32 or 64bit, to Writer w.
|
|
Packit |
63bb0d |
func printFloat(w io.Writer, val float64, precision int) {
|
|
Packit |
63bb0d |
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// printComplex outputs a complex value using the specified float precision
|
|
Packit |
63bb0d |
// for the real and imaginary parts to Writer w.
|
|
Packit |
63bb0d |
func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
|
Packit |
63bb0d |
r := real(c)
|
|
Packit |
63bb0d |
w.Write(openParenBytes)
|
|
Packit |
63bb0d |
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
|
|
Packit |
63bb0d |
i := imag(c)
|
|
Packit |
63bb0d |
if i >= 0 {
|
|
Packit |
63bb0d |
w.Write(plusBytes)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
|
|
Packit |
63bb0d |
w.Write(iBytes)
|
|
Packit |
63bb0d |
w.Write(closeParenBytes)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
|
Packit |
63bb0d |
// prefix to Writer w.
|
|
Packit |
63bb0d |
func printHexPtr(w io.Writer, p uintptr) {
|
|
Packit |
63bb0d |
// Null pointer.
|
|
Packit |
63bb0d |
num := uint64(p)
|
|
Packit |
63bb0d |
if num == 0 {
|
|
Packit |
63bb0d |
w.Write(nilAngleBytes)
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
|
Packit |
63bb0d |
buf := make([]byte, 18)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// It's simpler to construct the hex string right to left.
|
|
Packit |
63bb0d |
base := uint64(16)
|
|
Packit |
63bb0d |
i := len(buf) - 1
|
|
Packit |
63bb0d |
for num >= base {
|
|
Packit |
63bb0d |
buf[i] = hexDigits[num%base]
|
|
Packit |
63bb0d |
num /= base
|
|
Packit |
63bb0d |
i--
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
buf[i] = hexDigits[num]
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Add '0x' prefix.
|
|
Packit |
63bb0d |
i--
|
|
Packit |
63bb0d |
buf[i] = 'x'
|
|
Packit |
63bb0d |
i--
|
|
Packit |
63bb0d |
buf[i] = '0'
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Strip unused leading bytes.
|
|
Packit |
63bb0d |
buf = buf[i:]
|
|
Packit |
63bb0d |
w.Write(buf)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
|
Packit |
63bb0d |
// elements to be sorted.
|
|
Packit |
63bb0d |
type valuesSorter struct {
|
|
Packit |
63bb0d |
values []reflect.Value
|
|
Packit |
63bb0d |
strings []string // either nil or same len and values
|
|
Packit |
63bb0d |
cs *ConfigState
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
|
Packit |
63bb0d |
// surrogate keys on which the data should be sorted. It uses flags in
|
|
Packit |
63bb0d |
// ConfigState to decide if and how to populate those surrogate keys.
|
|
Packit |
63bb0d |
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
|
Packit |
63bb0d |
vs := &valuesSorter{values: values, cs: cs}
|
|
Packit |
63bb0d |
if canSortSimply(vs.values[0].Kind()) {
|
|
Packit |
63bb0d |
return vs
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
if !cs.DisableMethods {
|
|
Packit |
63bb0d |
vs.strings = make([]string, len(values))
|
|
Packit |
63bb0d |
for i := range vs.values {
|
|
Packit |
63bb0d |
b := bytes.Buffer{}
|
|
Packit |
63bb0d |
if !handleMethods(cs, &b, vs.values[i]) {
|
|
Packit |
63bb0d |
vs.strings = nil
|
|
Packit |
63bb0d |
break
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
vs.strings[i] = b.String()
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
if vs.strings == nil && cs.SpewKeys {
|
|
Packit |
63bb0d |
vs.strings = make([]string, len(values))
|
|
Packit |
63bb0d |
for i := range vs.values {
|
|
Packit |
63bb0d |
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return vs
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
|
Packit |
63bb0d |
// directly, or whether it should be considered for sorting by surrogate keys
|
|
Packit |
63bb0d |
// (if the ConfigState allows it).
|
|
Packit |
63bb0d |
func canSortSimply(kind reflect.Kind) bool {
|
|
Packit |
63bb0d |
// This switch parallels valueSortLess, except for the default case.
|
|
Packit |
63bb0d |
switch kind {
|
|
Packit |
63bb0d |
case reflect.Bool:
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
case reflect.Float32, reflect.Float64:
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
case reflect.String:
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
case reflect.Uintptr:
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
case reflect.Array:
|
|
Packit |
63bb0d |
return true
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return false
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Len returns the number of values in the slice. It is part of the
|
|
Packit |
63bb0d |
// sort.Interface implementation.
|
|
Packit |
63bb0d |
func (s *valuesSorter) Len() int {
|
|
Packit |
63bb0d |
return len(s.values)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Swap swaps the values at the passed indices. It is part of the
|
|
Packit |
63bb0d |
// sort.Interface implementation.
|
|
Packit |
63bb0d |
func (s *valuesSorter) Swap(i, j int) {
|
|
Packit |
63bb0d |
s.values[i], s.values[j] = s.values[j], s.values[i]
|
|
Packit |
63bb0d |
if s.strings != nil {
|
|
Packit |
63bb0d |
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// valueSortLess returns whether the first value should sort before the second
|
|
Packit |
63bb0d |
// value. It is used by valueSorter.Less as part of the sort.Interface
|
|
Packit |
63bb0d |
// implementation.
|
|
Packit |
63bb0d |
func valueSortLess(a, b reflect.Value) bool {
|
|
Packit |
63bb0d |
switch a.Kind() {
|
|
Packit |
63bb0d |
case reflect.Bool:
|
|
Packit |
63bb0d |
return !a.Bool() && b.Bool()
|
|
Packit |
63bb0d |
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
Packit |
63bb0d |
return a.Int() < b.Int()
|
|
Packit |
63bb0d |
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
Packit |
63bb0d |
return a.Uint() < b.Uint()
|
|
Packit |
63bb0d |
case reflect.Float32, reflect.Float64:
|
|
Packit |
63bb0d |
return a.Float() < b.Float()
|
|
Packit |
63bb0d |
case reflect.String:
|
|
Packit |
63bb0d |
return a.String() < b.String()
|
|
Packit |
63bb0d |
case reflect.Uintptr:
|
|
Packit |
63bb0d |
return a.Uint() < b.Uint()
|
|
Packit |
63bb0d |
case reflect.Array:
|
|
Packit |
63bb0d |
// Compare the contents of both arrays.
|
|
Packit |
63bb0d |
l := a.Len()
|
|
Packit |
63bb0d |
for i := 0; i < l; i++ {
|
|
Packit |
63bb0d |
av := a.Index(i)
|
|
Packit |
63bb0d |
bv := b.Index(i)
|
|
Packit |
63bb0d |
if av.Interface() == bv.Interface() {
|
|
Packit |
63bb0d |
continue
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return valueSortLess(av, bv)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return a.String() < b.String()
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Less returns whether the value at index i should sort before the
|
|
Packit |
63bb0d |
// value at index j. It is part of the sort.Interface implementation.
|
|
Packit |
63bb0d |
func (s *valuesSorter) Less(i, j int) bool {
|
|
Packit |
63bb0d |
if s.strings == nil {
|
|
Packit |
63bb0d |
return valueSortLess(s.values[i], s.values[j])
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
return s.strings[i] < s.strings[j]
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// sortValues is a sort function that handles both native types and any type that
|
|
Packit |
63bb0d |
// can be converted to error or Stringer. Other inputs are sorted according to
|
|
Packit |
63bb0d |
// their Value.String() value to ensure display stability.
|
|
Packit |
63bb0d |
func sortValues(values []reflect.Value, cs *ConfigState) {
|
|
Packit |
63bb0d |
if len(values) == 0 {
|
|
Packit |
63bb0d |
return
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
sort.Sort(newValuesSorter(values, cs))
|
|
Packit |
63bb0d |
}
|