-
Notifications
You must be signed in to change notification settings - Fork 0
/
dump.go
146 lines (133 loc) · 3.74 KB
/
dump.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package serial
import (
"bytes"
"fmt"
"reflect"
"slices"
"strconv"
)
type PtrSeen map[uintptr]struct{}
func (ps PtrSeen) Add(rv reflect.Value) bool {
ptr := rv.Pointer()
if _, ok := ps[ptr]; ok {
// e := fmt.Sprintf("encountered a cycle via %s", rv.Type())
// panic(e)
return false
}
ps[ptr] = struct{}{}
return true
}
// Dump any value to string(include private field)
func String(val any, hashPtrAddr bool) string {
refV := reflect.ValueOf(val)
ps := PtrSeen{}
return string(dump(refV, hashPtrAddr, ps))
}
// Dump any value to bytes(include private field)
func Bytes(val any, hashPtrAddr bool) []byte {
refV := reflect.ValueOf(val)
ps := PtrSeen{}
return dump(refV, hashPtrAddr, ps)
}
func dump(refV reflect.Value, hashPtrAddr bool, ps PtrSeen) []byte {
var buf bytes.Buffer
switch refV.Kind() {
case reflect.Invalid:
buf.WriteString("<invalid>")
case reflect.String:
buf.WriteString(`"`)
buf.WriteString(refV.String())
buf.WriteString(`"`)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
buf.WriteString(fmt.Sprintf("%d", refV.Int()))
// refV.CanInt()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
buf.WriteString(fmt.Sprintf("%d", refV.Uint()))
case reflect.Float32, reflect.Float64:
buf.WriteString(fmt.Sprintf("%f", refV.Float()))
case reflect.Complex64, reflect.Complex128:
buf.WriteString(fmt.Sprintf("%f", refV.Complex()))
case reflect.Ptr, reflect.Interface:
dumpPtrInterface(refV, hashPtrAddr, &buf, ps)
case reflect.Slice:
if !hashPtrAddr && !ps.Add(refV) {
buf.WriteString("<cycle slice>")
break
}
fallthrough
case reflect.Array:
dumpSliceArray(refV, hashPtrAddr, &buf, ps)
case reflect.Map:
if !hashPtrAddr && !ps.Add(refV) {
buf.WriteString("<cycle map>")
break
}
dumpMap(refV, hashPtrAddr, &buf, ps)
case reflect.Struct:
dumpStruct(refV, hashPtrAddr, &buf, ps)
case reflect.Func:
buf.WriteString("<func>")
case reflect.Chan:
buf.WriteString("<chan>")
case reflect.Bool:
buf.WriteString(strconv.FormatBool(refV.Bool()))
default:
msg := fmt.Sprintf("unsupported kind %s", refV.Kind())
panic(msg)
}
return buf.Bytes()
}
func dumpPtrInterface(refV reflect.Value, hashPtrAddr bool, buf *bytes.Buffer, ps PtrSeen) {
if refV.IsNil() {
buf.WriteString("null")
return
}
isPtr := refV.Kind() == reflect.Ptr
if isPtr && !ps.Add(refV) {
buf.WriteString("<cycle pointer>")
return
}
if hashPtrAddr && isPtr {
buf.WriteString(fmt.Sprintf("*0x%x", refV.Pointer()))
} else {
refV = refV.Elem()
buf.WriteString(fmt.Sprintf("&%s", dump(refV, hashPtrAddr, ps)))
}
}
func dumpSliceArray(refV reflect.Value, hashPtrAddr bool, buf *bytes.Buffer, ps PtrSeen) {
buf.WriteString("[")
for i := 0; i < refV.Len(); i++ {
buf.Write(dump(refV.Index(i), hashPtrAddr, ps))
if i != refV.Len()-1 {
buf.WriteString(",")
}
}
buf.WriteString("]")
}
func dumpStruct(refV reflect.Value, hashPtrAddr bool, buf *bytes.Buffer, ps PtrSeen) {
name := refV.Type().Name()
buf.WriteString(name + "{")
for i := 0; i < refV.NumField(); i++ {
buf.WriteString(refV.Type().Field(i).Name)
buf.WriteString(":")
buf.Write(dump(refV.Field(i), hashPtrAddr, ps))
if i != refV.NumField()-1 {
buf.WriteString(",")
}
}
buf.WriteString("}")
}
func dumpMap(refV reflect.Value, hashPtrAddr bool, buf *bytes.Buffer, ps PtrSeen) {
sli := make([][]byte, len(refV.MapKeys()))
for i, key := range refV.MapKeys() {
keyVal := append(dump(key, hashPtrAddr, ps), ':')
valbytes := dump(refV.MapIndex(key), hashPtrAddr, ps)
sli[i] = append(keyVal, valbytes...)
}
slices.SortFunc(sli, func(a, b []byte) int {
return slices.Compare(a, b)
})
buf.WriteString("{")
buf.Write(bytes.Join(sli, []byte{','}))
buf.WriteString("}")
}