-
Notifications
You must be signed in to change notification settings - Fork 25
/
key.go
111 lines (105 loc) · 3.21 KB
/
key.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
package rollupprocessor
import (
"math"
"sort"
"go.opentelemetry.io/collector/pdata/pcommon"
"google.golang.org/protobuf/encoding/protowire"
)
// key returns key used in the hashmap.
//
// The exact key generated by this function is not important. We only care that
// same set of attributes map to the same key and different – to different.
//
// The returned key is not-really a textual string, but using string here bo be
// able to use it as hashmap keys.
//
// This function also Sort()s attributes, but note that this doesn't affect
// values of list type – they need to be in repeatable order for "same"
// attributes to map into the same key.
//
// Note: Map is just a wrapper over list of KeyValues, which are
// protobuf-generated structs, so we could in theory just serialize them using
// protobuf, but these KeyValues are unfortunately not exposed.
//
// Note: Previous implementation was doing json.Marshal(am.AsRaw()), but it
// turned to be too slow.
func key(am pcommon.Map, ignore map[string]struct{}) string {
// Provide some initial capacity to avoid too many reallocs.
key := make([]byte, 0, am.Len()*20)
sortedRange(am, func(k string, v pcommon.Value) bool {
if _, exists := ignore[k]; exists {
// Skipping all fields from which we will get rolled up values, as
// those are dimensions not to be considered as "key".
return true
}
key = keyAppendString(key, k)
key = keyAppendValue(key, v)
return true
})
return string(key)
}
func keyAppendString(key []byte, s string) []byte {
key = append(key, s...)
key = append(key, '\xff')
return key
}
func keyAppendValue(key []byte, value pcommon.Value) []byte {
// Note: We don't really expect non-ignored attributes of types other than
// str and list, but handle all types somehow anyway.
switch value.Type() {
case pcommon.ValueTypeStr:
key = append(key, 'S')
key = keyAppendString(key, value.Str())
case pcommon.ValueTypeBytes:
key = append(key, 'B')
key = protowire.AppendBytes(key, value.Bytes().AsRaw())
case pcommon.ValueTypeBool:
if value.Bool() {
key = append(key, 'T')
} else {
key = append(key, 'F')
}
case pcommon.ValueTypeSlice:
key = append(key, 'L')
s := value.Slice()
key = protowire.AppendVarint(key, uint64(s.Len()))
for i := 0; i < s.Len(); i++ {
key = keyAppendValue(key, s.At(i))
}
case pcommon.ValueTypeMap:
key = append(key, 'M')
m := value.Map()
key = protowire.AppendVarint(key, uint64(m.Len()))
sortedRange(m, func(k string, v pcommon.Value) bool {
key = keyAppendString(key, k)
key = keyAppendValue(key, v)
return true
})
case pcommon.ValueTypeEmpty:
key = append(key, 'E')
case pcommon.ValueTypeInt:
key = append(key, 'I')
key = protowire.AppendVarint(key, uint64(value.Int()))
case pcommon.ValueTypeDouble:
key = append(key, 'D')
key = protowire.AppendFixed64(key, math.Float64bits(value.Double()))
}
return key
}
func sortedRange(m pcommon.Map, fn func(k string, v pcommon.Value) bool) {
sortedKeys := make([]string, 0, m.Len())
m.Range(func(k string, _ pcommon.Value) bool {
sortedKeys = append(sortedKeys, k)
return true
})
sort.Strings(sortedKeys)
for _, k := range sortedKeys {
v, ok := m.Get(k)
if !ok {
continue
}
if !fn(k, v) {
return
}
}
}