forked from alexflint/go-memdump
-
Notifications
You must be signed in to change notification settings - Fork 0
/
descriptor.go
120 lines (108 loc) · 2.62 KB
/
descriptor.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
package memdump
import (
"fmt"
"reflect"
)
// pointer - type
// slice - type
// array - type, length
// string
// int - size
// float - size
// A Descriptor describes a type such that if two types have the
// same descriptor then their memory layout is identical.
type descriptor []typ
// a class contains information about a type
type typ struct {
Kind reflect.Kind // Kind is the kind of this type
Size uintptr // Size is the size in bits, as per reflect.Value.Size
Elem int // Elem is index of the underlying type for pointers, slices, and arrays
Fields []field // Fields contains the fields for structs
}
type field struct {
Name string // Name is the name of this field, or its memdump tag if present
Offset uintptr // Offset is the position of this field relative to the beginning of the struct
Type int // ID is the index of the type of this field in the descripto
}
// descriptorsEqual compares two descriptors
func descriptorsEqual(a, b descriptor) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i].Kind != b[i].Kind {
return false
}
if a[i].Size != b[i].Size {
return false
}
if a[i].Elem != b[i].Elem {
return false
}
if len(a[i].Fields) != len(b[i].Fields) {
return false
}
for j := range a[i].Fields {
if a[i].Fields[j].Name != b[i].Fields[j].Name {
return false
}
if a[i].Fields[j].Offset != b[i].Fields[j].Offset {
return false
}
if a[i].Fields[j].Type != b[i].Fields[j].Type {
return false
}
}
}
return true
}
// describe computes the descriptor for a type
func describe(t reflect.Type) descriptor {
var nextID int
var desc descriptor
var queue []reflect.Type
seen := make(map[reflect.Type]int)
push := func(t reflect.Type) int {
if id, found := seen[t]; found {
return id
}
id := nextID
seen[t] = id
nextID++
queue = append(queue, t)
return id
}
push(t)
for len(queue) > 0 {
cur := queue[0]
queue = queue[1:]
t := typ{
Size: cur.Size(),
Kind: cur.Kind(),
}
switch cur.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map:
panic(fmt.Sprintf("cannot compute descriptor for %v", cur.Kind()))
case reflect.Array, reflect.Slice, reflect.Ptr:
t.Elem = push(cur.Elem())
case reflect.Struct:
for i := 0; i < cur.NumField(); i++ {
f := cur.Field(i)
if f.Type.Size() == 0 {
continue
}
name := f.Name
if tag := f.Tag.Get("memdump"); tag != "" {
name = tag
}
t.Fields = append(t.Fields, field{
Name: name,
Offset: f.Offset,
Type: push(f.Type),
})
}
}
desc = append(desc, t)
}
return desc
}