-
-
Notifications
You must be signed in to change notification settings - Fork 564
/
Copy pathhasher.go
134 lines (124 loc) Β· 4.25 KB
/
hasher.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
package expr
import (
"fmt"
"sort"
"strings"
)
var (
arrayPrefix = "_a_"
attributePrefix = "-"
attributeTypePrefix = "/"
mapElemPrefix = ":"
mapPrefix = "_m_"
unionTypePrefix = "_u_"
unionAttributePrefix = "_*_"
unionAttributeTypePrefix = "_|_"
objectPrefix = "_o_"
tagPrefix = "+"
userTypeHashPrefix = "!"
userTypePrefix = "_t_"
)
// Hash returns a hash value for the given data type. Two types have the same
// hash if:
// - both types have the same kind
// - array types have elements whose types have the same hash
// - map types have keys and elements whose types have the same hash
// - user types have the same name if ignoreNames is false or ignoreFields is true
// - user types have the same attribute names and the attribute types have the same hash if ignoreFields is false
// - object attributes have the same "struct:field:xxx" tags if ignoreTags is false
func Hash(dt DataType, ignoreFields, ignoreNames, ignoreTags bool) string {
return *hash(dt, ignoreFields, ignoreNames, ignoreTags, make(map[*Object]*string))
}
func hash(dt DataType, ignoreFields, ignoreNames, ignoreTags bool, seen map[*Object]*string) *string {
if seen == nil {
seen = make(map[*Object]*string)
}
switch dt.Kind() {
case BooleanKind, IntKind, Int32Kind, Int64Kind, UIntKind, UInt32Kind, UInt64Kind, Float32Kind, Float64Kind, StringKind, BytesKind, AnyKind:
n := dt.Name()
return &n
case ArrayKind:
return hashArray(dt.(*Array), ignoreFields, ignoreNames, ignoreTags, seen)
case MapKind:
return hashMap(dt.(*Map), ignoreFields, ignoreNames, ignoreTags, seen)
case UnionKind:
return hashUnion(dt.(*Union), ignoreFields, ignoreNames, ignoreTags, seen)
case UserTypeKind, ResultTypeKind:
return hashUserType(dt.(UserType), ignoreFields, ignoreNames, ignoreTags, seen)
case ObjectKind:
return hashObject(dt.(*Object), ignoreFields, ignoreNames, ignoreTags, seen)
default:
panic(fmt.Sprintf("invalid type for hashing: %T", dt))
}
}
func hashArray(a *Array, ignoreFields, ignoreNames, ignoreTags bool, seen map[*Object]*string) *string {
h := arrayPrefix + *hash(a.ElemType.Type, ignoreFields, ignoreNames, ignoreTags, seen)
return &h
}
func hashMap(m *Map, ignoreFields, ignoreNames, ignoreTags bool, seen map[*Object]*string) *string {
h := mapPrefix + *hash(m.KeyType.Type, ignoreFields, ignoreNames, ignoreTags, seen) +
mapElemPrefix + *hash(m.ElemType.Type, ignoreFields, ignoreNames, ignoreTags, seen)
return &h
}
func hashUnion(u *Union, ignoreFields, ignoreNames, ignoreTags bool, seen map[*Object]*string) *string {
sorted := make([]*NamedAttributeExpr, len(u.Values))
copy(sorted, u.Values)
sort.Slice(sorted, func(i, j int) bool {
return u.Values[i].Name < u.Values[j].Name
})
h := unionTypePrefix + u.TypeName
for _, nat := range sorted {
h += unionAttributePrefix + nat.Name + unionAttributeTypePrefix + *hash(nat.Attribute.Type, ignoreFields, ignoreNames, ignoreTags, seen)
}
return &h
}
func hashUserType(ut UserType, ignoreFields, ignoreNames, ignoreTags bool, seen map[*Object]*string) *string {
h := userTypePrefix
if !ignoreNames || ignoreFields {
h += ut.Name()
}
if ignoreFields {
return &h
}
att := ut.Attribute()
if !ignoreTags {
for k, v := range att.Meta {
if !strings.HasPrefix(k, "struct:field:") {
continue
}
h += fmt.Sprintf("%s%s%s", tagPrefix, k, v)
}
}
h += userTypeHashPrefix + *hash(att.Type, ignoreFields, ignoreNames, ignoreTags, seen)
return &h
}
func hashObject(o *Object, ignoreFields, ignoreNames, ignoreTags bool, seen map[*Object]*string) *string {
if s, ok := seen[o]; ok {
return s
}
h := objectPrefix
ph := &h
seen[o] = ph
for _, a := range sorted(o) {
*ph += attributePrefix + a.Name +
attributeTypePrefix + *hash(a.Attribute.Type, ignoreFields, ignoreNames, ignoreTags, seen)
if !ignoreTags {
for k, v := range a.Attribute.Meta {
if !strings.HasPrefix(k, "struct:field:") {
continue
}
*ph += fmt.Sprintf("%s%s%s", tagPrefix, k, v)
}
}
}
return ph
}
func sorted(o *Object) Object {
if o == nil {
return nil
}
s := make([]*NamedAttributeExpr, len(*o))
copy(s, *o)
sort.Slice(s, func(i, j int) bool { return s[i].Name < s[j].Name })
return Object(s)
}