diff options
Diffstat (limited to 'vendor/github.com/davecgh/go-spew/spew/dump.go')
-rw-r--r-- | vendor/github.com/davecgh/go-spew/spew/dump.go | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go new file mode 100644 index 0000000..df1d582 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/dump.go | |||
@@ -0,0 +1,509 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013-2016 Dave Collins <dave@davec.name> | ||
3 | * | ||
4 | * Permission to use, copy, modify, and distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | package spew | ||
18 | |||
19 | import ( | ||
20 | "bytes" | ||
21 | "encoding/hex" | ||
22 | "fmt" | ||
23 | "io" | ||
24 | "os" | ||
25 | "reflect" | ||
26 | "regexp" | ||
27 | "strconv" | ||
28 | "strings" | ||
29 | ) | ||
30 | |||
31 | var ( | ||
32 | // uint8Type is a reflect.Type representing a uint8. It is used to | ||
33 | // convert cgo types to uint8 slices for hexdumping. | ||
34 | uint8Type = reflect.TypeOf(uint8(0)) | ||
35 | |||
36 | // cCharRE is a regular expression that matches a cgo char. | ||
37 | // It is used to detect character arrays to hexdump them. | ||
38 | cCharRE = regexp.MustCompile("^.*\\._Ctype_char$") | ||
39 | |||
40 | // cUnsignedCharRE is a regular expression that matches a cgo unsigned | ||
41 | // char. It is used to detect unsigned character arrays to hexdump | ||
42 | // them. | ||
43 | cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$") | ||
44 | |||
45 | // cUint8tCharRE is a regular expression that matches a cgo uint8_t. | ||
46 | // It is used to detect uint8_t arrays to hexdump them. | ||
47 | cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$") | ||
48 | ) | ||
49 | |||
50 | // dumpState contains information about the state of a dump operation. | ||
51 | type dumpState struct { | ||
52 | w io.Writer | ||
53 | depth int | ||
54 | pointers map[uintptr]int | ||
55 | ignoreNextType bool | ||
56 | ignoreNextIndent bool | ||
57 | cs *ConfigState | ||
58 | } | ||
59 | |||
60 | // indent performs indentation according to the depth level and cs.Indent | ||
61 | // option. | ||
62 | func (d *dumpState) indent() { | ||
63 | if d.ignoreNextIndent { | ||
64 | d.ignoreNextIndent = false | ||
65 | return | ||
66 | } | ||
67 | d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) | ||
68 | } | ||
69 | |||
70 | // unpackValue returns values inside of non-nil interfaces when possible. | ||
71 | // This is useful for data types like structs, arrays, slices, and maps which | ||
72 | // can contain varying types packed inside an interface. | ||
73 | func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { | ||
74 | if v.Kind() == reflect.Interface && !v.IsNil() { | ||
75 | v = v.Elem() | ||
76 | } | ||
77 | return v | ||
78 | } | ||
79 | |||
80 | // dumpPtr handles formatting of pointers by indirecting them as necessary. | ||
81 | func (d *dumpState) dumpPtr(v reflect.Value) { | ||
82 | // Remove pointers at or below the current depth from map used to detect | ||
83 | // circular refs. | ||
84 | for k, depth := range d.pointers { | ||
85 | if depth >= d.depth { | ||
86 | delete(d.pointers, k) | ||
87 | } | ||
88 | } | ||
89 | |||
90 | // Keep list of all dereferenced pointers to show later. | ||
91 | pointerChain := make([]uintptr, 0) | ||
92 | |||
93 | // Figure out how many levels of indirection there are by dereferencing | ||
94 | // pointers and unpacking interfaces down the chain while detecting circular | ||
95 | // references. | ||
96 | nilFound := false | ||
97 | cycleFound := false | ||
98 | indirects := 0 | ||
99 | ve := v | ||
100 | for ve.Kind() == reflect.Ptr { | ||
101 | if ve.IsNil() { | ||
102 | nilFound = true | ||
103 | break | ||
104 | } | ||
105 | indirects++ | ||
106 | addr := ve.Pointer() | ||
107 | pointerChain = append(pointerChain, addr) | ||
108 | if pd, ok := d.pointers[addr]; ok && pd < d.depth { | ||
109 | cycleFound = true | ||
110 | indirects-- | ||
111 | break | ||
112 | } | ||
113 | d.pointers[addr] = d.depth | ||
114 | |||
115 | ve = ve.Elem() | ||
116 | if ve.Kind() == reflect.Interface { | ||
117 | if ve.IsNil() { | ||
118 | nilFound = true | ||
119 | break | ||
120 | } | ||
121 | ve = ve.Elem() | ||
122 | } | ||
123 | } | ||
124 | |||
125 | // Display type information. | ||
126 | d.w.Write(openParenBytes) | ||
127 | d.w.Write(bytes.Repeat(asteriskBytes, indirects)) | ||
128 | d.w.Write([]byte(ve.Type().String())) | ||
129 | d.w.Write(closeParenBytes) | ||
130 | |||
131 | // Display pointer information. | ||
132 | if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { | ||
133 | d.w.Write(openParenBytes) | ||
134 | for i, addr := range pointerChain { | ||
135 | if i > 0 { | ||
136 | d.w.Write(pointerChainBytes) | ||
137 | } | ||
138 | printHexPtr(d.w, addr) | ||
139 | } | ||
140 | d.w.Write(closeParenBytes) | ||
141 | } | ||
142 | |||
143 | // Display dereferenced value. | ||
144 | d.w.Write(openParenBytes) | ||
145 | switch { | ||
146 | case nilFound == true: | ||
147 | d.w.Write(nilAngleBytes) | ||
148 | |||
149 | case cycleFound == true: | ||
150 | d.w.Write(circularBytes) | ||
151 | |||
152 | default: | ||
153 | d.ignoreNextType = true | ||
154 | d.dump(ve) | ||
155 | } | ||
156 | d.w.Write(closeParenBytes) | ||
157 | } | ||
158 | |||
159 | // dumpSlice handles formatting of arrays and slices. Byte (uint8 under | ||
160 | // reflection) arrays and slices are dumped in hexdump -C fashion. | ||
161 | func (d *dumpState) dumpSlice(v reflect.Value) { | ||
162 | // Determine whether this type should be hex dumped or not. Also, | ||
163 | // for types which should be hexdumped, try to use the underlying data | ||
164 | // first, then fall back to trying to convert them to a uint8 slice. | ||
165 | var buf []uint8 | ||
166 | doConvert := false | ||
167 | doHexDump := false | ||
168 | numEntries := v.Len() | ||
169 | if numEntries > 0 { | ||
170 | vt := v.Index(0).Type() | ||
171 | vts := vt.String() | ||
172 | switch { | ||
173 | // C types that need to be converted. | ||
174 | case cCharRE.MatchString(vts): | ||
175 | fallthrough | ||
176 | case cUnsignedCharRE.MatchString(vts): | ||
177 | fallthrough | ||
178 | case cUint8tCharRE.MatchString(vts): | ||
179 | doConvert = true | ||
180 | |||
181 | // Try to use existing uint8 slices and fall back to converting | ||
182 | // and copying if that fails. | ||
183 | case vt.Kind() == reflect.Uint8: | ||
184 | // We need an addressable interface to convert the type | ||
185 | // to a byte slice. However, the reflect package won't | ||
186 | // give us an interface on certain things like | ||
187 | // unexported struct fields in order to enforce | ||
188 | // visibility rules. We use unsafe, when available, to | ||
189 | // bypass these restrictions since this package does not | ||
190 | // mutate the values. | ||
191 | vs := v | ||
192 | if !vs.CanInterface() || !vs.CanAddr() { | ||
193 | vs = unsafeReflectValue(vs) | ||
194 | } | ||
195 | if !UnsafeDisabled { | ||
196 | vs = vs.Slice(0, numEntries) | ||
197 | |||
198 | // Use the existing uint8 slice if it can be | ||
199 | // type asserted. | ||
200 | iface := vs.Interface() | ||
201 | if slice, ok := iface.([]uint8); ok { | ||
202 | buf = slice | ||
203 | doHexDump = true | ||
204 | break | ||
205 | } | ||
206 | } | ||
207 | |||
208 | // The underlying data needs to be converted if it can't | ||
209 | // be type asserted to a uint8 slice. | ||
210 | doConvert = true | ||
211 | } | ||
212 | |||
213 | // Copy and convert the underlying type if needed. | ||
214 | if doConvert && vt.ConvertibleTo(uint8Type) { | ||
215 | // Convert and copy each element into a uint8 byte | ||
216 | // slice. | ||
217 | buf = make([]uint8, numEntries) | ||
218 | for i := 0; i < numEntries; i++ { | ||
219 | vv := v.Index(i) | ||
220 | buf[i] = uint8(vv.Convert(uint8Type).Uint()) | ||
221 | } | ||
222 | doHexDump = true | ||
223 | } | ||
224 | } | ||
225 | |||
226 | // Hexdump the entire slice as needed. | ||
227 | if doHexDump { | ||
228 | indent := strings.Repeat(d.cs.Indent, d.depth) | ||
229 | str := indent + hex.Dump(buf) | ||
230 | str = strings.Replace(str, "\n", "\n"+indent, -1) | ||
231 | str = strings.TrimRight(str, d.cs.Indent) | ||
232 | d.w.Write([]byte(str)) | ||
233 | return | ||
234 | } | ||
235 | |||
236 | // Recursively call dump for each item. | ||
237 | for i := 0; i < numEntries; i++ { | ||
238 | d.dump(d.unpackValue(v.Index(i))) | ||
239 | if i < (numEntries - 1) { | ||
240 | d.w.Write(commaNewlineBytes) | ||
241 | } else { | ||
242 | d.w.Write(newlineBytes) | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | // dump is the main workhorse for dumping a value. It uses the passed reflect | ||
248 | // value to figure out what kind of object we are dealing with and formats it | ||
249 | // appropriately. It is a recursive function, however circular data structures | ||
250 | // are detected and handled properly. | ||
251 | func (d *dumpState) dump(v reflect.Value) { | ||
252 | // Handle invalid reflect values immediately. | ||
253 | kind := v.Kind() | ||
254 | if kind == reflect.Invalid { | ||
255 | d.w.Write(invalidAngleBytes) | ||
256 | return | ||
257 | } | ||
258 | |||
259 | // Handle pointers specially. | ||
260 | if kind == reflect.Ptr { | ||
261 | d.indent() | ||
262 | d.dumpPtr(v) | ||
263 | return | ||
264 | } | ||
265 | |||
266 | // Print type information unless already handled elsewhere. | ||
267 | if !d.ignoreNextType { | ||
268 | d.indent() | ||
269 | d.w.Write(openParenBytes) | ||
270 | d.w.Write([]byte(v.Type().String())) | ||
271 | d.w.Write(closeParenBytes) | ||
272 | d.w.Write(spaceBytes) | ||
273 | } | ||
274 | d.ignoreNextType = false | ||
275 | |||
276 | // Display length and capacity if the built-in len and cap functions | ||
277 | // work with the value's kind and the len/cap itself is non-zero. | ||
278 | valueLen, valueCap := 0, 0 | ||
279 | switch v.Kind() { | ||
280 | case reflect.Array, reflect.Slice, reflect.Chan: | ||
281 | valueLen, valueCap = v.Len(), v.Cap() | ||
282 | case reflect.Map, reflect.String: | ||
283 | valueLen = v.Len() | ||
284 | } | ||
285 | if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { | ||
286 | d.w.Write(openParenBytes) | ||
287 | if valueLen != 0 { | ||
288 | d.w.Write(lenEqualsBytes) | ||
289 | printInt(d.w, int64(valueLen), 10) | ||
290 | } | ||
291 | if !d.cs.DisableCapacities && valueCap != 0 { | ||
292 | if valueLen != 0 { | ||
293 | d.w.Write(spaceBytes) | ||
294 | } | ||
295 | d.w.Write(capEqualsBytes) | ||
296 | printInt(d.w, int64(valueCap), 10) | ||
297 | } | ||
298 | d.w.Write(closeParenBytes) | ||
299 | d.w.Write(spaceBytes) | ||
300 | } | ||
301 | |||
302 | // Call Stringer/error interfaces if they exist and the handle methods flag | ||
303 | // is enabled | ||
304 | if !d.cs.DisableMethods { | ||
305 | if (kind != reflect.Invalid) && (kind != reflect.Interface) { | ||
306 | if handled := handleMethods(d.cs, d.w, v); handled { | ||
307 | return | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | |||
312 | switch kind { | ||
313 | case reflect.Invalid: | ||
314 | // Do nothing. We should never get here since invalid has already | ||
315 | // been handled above. | ||
316 | |||
317 | case reflect.Bool: | ||
318 | printBool(d.w, v.Bool()) | ||
319 | |||
320 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: | ||
321 | printInt(d.w, v.Int(), 10) | ||
322 | |||
323 | case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: | ||
324 | printUint(d.w, v.Uint(), 10) | ||
325 | |||
326 | case reflect.Float32: | ||
327 | printFloat(d.w, v.Float(), 32) | ||
328 | |||
329 | case reflect.Float64: | ||
330 | printFloat(d.w, v.Float(), 64) | ||
331 | |||
332 | case reflect.Complex64: | ||
333 | printComplex(d.w, v.Complex(), 32) | ||
334 | |||
335 | case reflect.Complex128: | ||
336 | printComplex(d.w, v.Complex(), 64) | ||
337 | |||
338 | case reflect.Slice: | ||
339 | if v.IsNil() { | ||
340 | d.w.Write(nilAngleBytes) | ||
341 | break | ||
342 | } | ||
343 | fallthrough | ||
344 | |||
345 | case reflect.Array: | ||
346 | d.w.Write(openBraceNewlineBytes) | ||
347 | d.depth++ | ||
348 | if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { | ||
349 | d.indent() | ||
350 | d.w.Write(maxNewlineBytes) | ||
351 | } else { | ||
352 | d.dumpSlice(v) | ||
353 | } | ||
354 | d.depth-- | ||
355 | d.indent() | ||
356 | d.w.Write(closeBraceBytes) | ||
357 | |||
358 | case reflect.String: | ||
359 | d.w.Write([]byte(strconv.Quote(v.String()))) | ||
360 | |||
361 | case reflect.Interface: | ||
362 | // The only time we should get here is for nil interfaces due to | ||
363 | // unpackValue calls. | ||
364 | if v.IsNil() { | ||
365 | d.w.Write(nilAngleBytes) | ||
366 | } | ||
367 | |||
368 | case reflect.Ptr: | ||
369 | // Do nothing. We should never get here since pointers have already | ||
370 | // been handled above. | ||
371 | |||
372 | case reflect.Map: | ||
373 | // nil maps should be indicated as different than empty maps | ||
374 | if v.IsNil() { | ||
375 | d.w.Write(nilAngleBytes) | ||
376 | break | ||
377 | } | ||
378 | |||
379 | d.w.Write(openBraceNewlineBytes) | ||
380 | d.depth++ | ||
381 | if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { | ||
382 | d.indent() | ||
383 | d.w.Write(maxNewlineBytes) | ||
384 | } else { | ||
385 | numEntries := v.Len() | ||
386 | keys := v.MapKeys() | ||
387 | if d.cs.SortKeys { | ||
388 | sortValues(keys, d.cs) | ||
389 | } | ||
390 | for i, key := range keys { | ||
391 | d.dump(d.unpackValue(key)) | ||
392 | d.w.Write(colonSpaceBytes) | ||
393 | d.ignoreNextIndent = true | ||
394 | d.dump(d.unpackValue(v.MapIndex(key))) | ||
395 | if i < (numEntries - 1) { | ||
396 | d.w.Write(commaNewlineBytes) | ||
397 | } else { | ||
398 | d.w.Write(newlineBytes) | ||
399 | } | ||
400 | } | ||
401 | } | ||
402 | d.depth-- | ||
403 | d.indent() | ||
404 | d.w.Write(closeBraceBytes) | ||
405 | |||
406 | case reflect.Struct: | ||
407 | d.w.Write(openBraceNewlineBytes) | ||
408 | d.depth++ | ||
409 | if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { | ||
410 | d.indent() | ||
411 | d.w.Write(maxNewlineBytes) | ||
412 | } else { | ||
413 | vt := v.Type() | ||
414 | numFields := v.NumField() | ||
415 | for i := 0; i < numFields; i++ { | ||
416 | d.indent() | ||
417 | vtf := vt.Field(i) | ||
418 | d.w.Write([]byte(vtf.Name)) | ||
419 | d.w.Write(colonSpaceBytes) | ||
420 | d.ignoreNextIndent = true | ||
421 | d.dump(d.unpackValue(v.Field(i))) | ||
422 | if i < (numFields - 1) { | ||
423 | d.w.Write(commaNewlineBytes) | ||
424 | } else { | ||
425 | d.w.Write(newlineBytes) | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | d.depth-- | ||
430 | d.indent() | ||
431 | d.w.Write(closeBraceBytes) | ||
432 | |||
433 | case reflect.Uintptr: | ||
434 | printHexPtr(d.w, uintptr(v.Uint())) | ||
435 | |||
436 | case reflect.UnsafePointer, reflect.Chan, reflect.Func: | ||
437 | printHexPtr(d.w, v.Pointer()) | ||
438 | |||
439 | // There were not any other types at the time this code was written, but | ||
440 | // fall back to letting the default fmt package handle it in case any new | ||
441 | // types are added. | ||
442 | default: | ||
443 | if v.CanInterface() { | ||
444 | fmt.Fprintf(d.w, "%v", v.Interface()) | ||
445 | } else { | ||
446 | fmt.Fprintf(d.w, "%v", v.String()) | ||
447 | } | ||
448 | } | ||
449 | } | ||
450 | |||
451 | // fdump is a helper function to consolidate the logic from the various public | ||
452 | // methods which take varying writers and config states. | ||
453 | func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { | ||
454 | for _, arg := range a { | ||
455 | if arg == nil { | ||
456 | w.Write(interfaceBytes) | ||
457 | w.Write(spaceBytes) | ||
458 | w.Write(nilAngleBytes) | ||
459 | w.Write(newlineBytes) | ||
460 | continue | ||
461 | } | ||
462 | |||
463 | d := dumpState{w: w, cs: cs} | ||
464 | d.pointers = make(map[uintptr]int) | ||
465 | d.dump(reflect.ValueOf(arg)) | ||
466 | d.w.Write(newlineBytes) | ||
467 | } | ||
468 | } | ||
469 | |||
470 | // Fdump formats and displays the passed arguments to io.Writer w. It formats | ||
471 | // exactly the same as Dump. | ||
472 | func Fdump(w io.Writer, a ...interface{}) { | ||
473 | fdump(&Config, w, a...) | ||
474 | } | ||
475 | |||
476 | // Sdump returns a string with the passed arguments formatted exactly the same | ||
477 | // as Dump. | ||
478 | func Sdump(a ...interface{}) string { | ||
479 | var buf bytes.Buffer | ||
480 | fdump(&Config, &buf, a...) | ||
481 | return buf.String() | ||
482 | } | ||
483 | |||
484 | /* | ||
485 | Dump displays the passed parameters to standard out with newlines, customizable | ||
486 | indentation, and additional debug information such as complete types and all | ||
487 | pointer addresses used to indirect to the final value. It provides the | ||
488 | following features over the built-in printing facilities provided by the fmt | ||
489 | package: | ||
490 | |||
491 | * Pointers are dereferenced and followed | ||
492 | * Circular data structures are detected and handled properly | ||
493 | * Custom Stringer/error interfaces are optionally invoked, including | ||
494 | on unexported types | ||
495 | * Custom types which only implement the Stringer/error interfaces via | ||
496 | a pointer receiver are optionally invoked when passing non-pointer | ||
497 | variables | ||
498 | * Byte arrays and slices are dumped like the hexdump -C command which | ||
499 | includes offsets, byte values in hex, and ASCII output | ||
500 | |||
501 | The configuration options are controlled by an exported package global, | ||
502 | spew.Config. See ConfigState for options documentation. | ||
503 | |||
504 | See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to | ||
505 | get the formatted result as a string. | ||
506 | */ | ||
507 | func Dump(a ...interface{}) { | ||
508 | fdump(&Config, os.Stdout, a...) | ||
509 | } | ||