1
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
33
34 uint8Type = reflect.TypeOf(uint8(0))
35
36
37
38 cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
39
40
41
42
43 cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
44
45
46
47 cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
48 )
49
50
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
61
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
71
72
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
81 func (d *dumpState) dumpPtr(v reflect.Value) {
82
83
84 for k, depth := range d.pointers {
85 if depth >= d.depth {
86 delete(d.pointers, k)
87 }
88 }
89
90
91 pointerChain := make([]uintptr, 0)
92
93
94
95
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
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
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
144 d.w.Write(openParenBytes)
145 switch {
146 case nilFound:
147 d.w.Write(nilAngleBytes)
148
149 case cycleFound:
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
160
161 func (d *dumpState) dumpSlice(v reflect.Value) {
162
163
164
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
174 case cCharRE.MatchString(vts):
175 fallthrough
176 case cUnsignedCharRE.MatchString(vts):
177 fallthrough
178 case cUint8tCharRE.MatchString(vts):
179 doConvert = true
180
181
182
183 case vt.Kind() == reflect.Uint8:
184
185
186
187
188
189
190
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
199
200 iface := vs.Interface()
201 if slice, ok := iface.([]uint8); ok {
202 buf = slice
203 doHexDump = true
204 break
205 }
206 }
207
208
209
210 doConvert = true
211 }
212
213
214 if doConvert && vt.ConvertibleTo(uint8Type) {
215
216
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
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
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
248
249
250
251 func (d *dumpState) dump(v reflect.Value) {
252
253 kind := v.Kind()
254 if kind == reflect.Invalid {
255 d.w.Write(invalidAngleBytes)
256 return
257 }
258
259
260 if kind == reflect.Ptr {
261 d.indent()
262 d.dumpPtr(v)
263 return
264 }
265
266
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
277
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
303
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
315
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
363
364 if v.IsNil() {
365 d.w.Write(nilAngleBytes)
366 }
367
368 case reflect.Ptr:
369
370
371
372 case reflect.Map:
373
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
440
441
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
452
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
471
472 func Fdump(w io.Writer, a ...interface{}) {
473 fdump(&Config, w, a...)
474 }
475
476
477
478 func Sdump(a ...interface{}) string {
479 var buf bytes.Buffer
480 fdump(&Config, &buf, a...)
481 return buf.String()
482 }
483
484
507 func Dump(a ...interface{}) {
508 fdump(&Config, os.Stdout, a...)
509 }
510
View as plain text