This repository has been archived by the owner on Feb 21, 2023. It is now read-only.
/
attrs.go
156 lines (128 loc) · 3.47 KB
/
attrs.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
147
148
149
150
151
152
153
154
155
156
package gomol
import (
"github.com/spaolacci/murmur3"
"fmt"
"reflect"
"sync"
)
// Attrs represents a collection of key/value attributes
type Attrs struct {
attrs map[uint32]interface{}
attrsLock sync.RWMutex
}
// NewAttrs will create a new Attrs struct with an empty set of attributes.
func NewAttrs() *Attrs {
return &Attrs{
attrs: make(map[uint32]interface{}),
}
}
// NewAttrsFromMap will create a new Attrs struct with the given attributes pre-populated
func NewAttrsFromMap(attrs map[string]interface{}) *Attrs {
newAttrs := NewAttrs()
for attrKey, attrVal := range attrs {
newAttrs.SetAttr(attrKey, attrVal)
}
return newAttrs
}
// NewAttrsFromAttrs is a convenience function that will accept zero or more existing Attrs, create
// a new Attrs and then merge all the supplied Attrs values into the new Attrs instance.
func NewAttrsFromAttrs(attrs ...*Attrs) *Attrs {
newAttrs := NewAttrs()
for _, attr := range attrs {
newAttrs.MergeAttrs(attr)
}
return newAttrs
}
// MergeAttrs accepts another existing Attrs and merges the attributes into its own.
func (a *Attrs) MergeAttrs(attrs *Attrs) {
if attrs == nil {
return
}
a.attrsLock.Lock()
defer a.attrsLock.Unlock()
for hash, val := range attrs.attrs {
a.attrs[hash] = val
}
}
func (a *Attrs) clone() *Attrs {
attrs := NewAttrs()
for hash, val := range a.attrs {
attrs.attrs[hash] = val
}
return attrs
}
// SetAttr will set key to the provided value. If the attribute already exists the value will
// be replaced with the new value.
func (a *Attrs) SetAttr(key string, value interface{}) *Attrs {
a.attrsLock.Lock()
defer a.attrsLock.Unlock()
valVal := reflect.ValueOf(value)
switch valVal.Kind() {
case reflect.Func:
value = valVal.Type().String()
}
hash := getAttrHash(key)
a.attrs[hash] = value
return a
}
// GetAttr gets the value of the attribute with the provided name. If the attribute does not
// exist, nil will be returned
func (a *Attrs) GetAttr(key string) interface{} {
a.attrsLock.RLock()
defer a.attrsLock.RUnlock()
return a.attrs[getAttrHash(key)]
}
// RemoveAttr will remove the attribute with the provided name.
func (a *Attrs) RemoveAttr(key string) {
a.attrsLock.Lock()
defer a.attrsLock.Unlock()
delete(a.attrs, getAttrHash(key))
}
// Attrs will return a map of the attributes added to the struct.
func (a *Attrs) Attrs() map[string]interface{} {
a.attrsLock.RLock()
defer a.attrsLock.RUnlock()
attrs := make(map[string]interface{})
for hash, val := range a.attrs {
key, _ := getHashAttr(hash)
attrs[key] = val
}
return attrs
}
type logAttr struct {
Name string
Value interface{}
}
var hashMutex sync.RWMutex
var attrHashes = make(map[string]uint32)
var hashAttrs = make(map[uint32]string)
func getAttrHash(attr string) uint32 {
// First try to acquire a read lock to see if we even need to hash
// the string at all
hashMutex.RLock()
if hash, ok := attrHashes[attr]; ok {
hashMutex.RUnlock()
return hash
}
// We do need to hash it so release the read lock and acquire a write lock
hashMutex.RUnlock()
hashMutex.Lock()
defer hashMutex.Unlock()
if hash, ok := attrHashes[attr]; ok {
return hash
}
hasher := murmur3.New32()
hasher.Write([]byte(attr))
hash := hasher.Sum32()
hashAttrs[hash] = attr
attrHashes[attr] = hash
return hash
}
func getHashAttr(hash uint32) (string, error) {
hashMutex.RLock()
defer hashMutex.RUnlock()
if attr, ok := hashAttrs[hash]; ok {
return attr, nil
}
return "", fmt.Errorf("Could not find attr for hash %v", hash)
}