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