This repository has been archived by the owner on May 13, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmemory.go
255 lines (200 loc) · 5.33 KB
/
memory.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package bite
import (
"fmt"
"reflect"
"sync"
"github.com/spf13/cobra"
)
// Memory describes a temporary storage for each application, useful to store different kind of custom values.
type Memory struct {
tmp map[uint8]interface{} // why not string and uint8 as key? With uint8(0-255) we set a kind of limit of the elements stored without any further complications.
mu sync.RWMutex
}
// m.Set(MyKey, MyValue{}) == existed and replaced, safe for concurrent access.
func (m *Memory) Set(key uint8, value interface{}) (replacement bool) {
if m.tmp == nil {
return
}
replacement = m.Has(key)
m.mu.Lock()
m.tmp[key] = value
m.mu.Unlock()
return
}
// SetOnce will store a value based on its key if it's not there already, do not confuse its action with immutability.
func (m *Memory) SetOnce(key uint8, value interface{}) bool {
if m.tmp == nil {
return false
}
if len(m.tmp) > 0 && m.Has(key) {
return false
}
m.mu.Lock()
m.tmp[key] = value
m.mu.Unlock()
return true
}
var (
emptyIn []reflect.Value
errorTyp = reflect.TypeOf((*error)(nil)).Elem()
)
func (m *Memory) SetOnceFunc(key uint8, receiverFunc interface{}) error {
if m.tmp == nil {
return nil
}
if len(m.tmp) > 0 && m.Has(key) {
return nil
}
// execute the receiverFunc to get its value.
fn := reflect.Indirect(reflect.ValueOf(receiverFunc))
if fn.Kind() == reflect.Interface {
fn = fn.Elem()
}
if fn.Kind() != reflect.Func {
return fmt.Errorf("mem: receiverFunc not type of func")
}
fnTyp := fn.Type()
if fnTyp.NumIn() != 0 {
return fmt.Errorf("mem: receiverFunc should not accept any input argument")
}
fnOut := fnTyp.NumOut()
if fnOut < 1 || fnOut > 2 {
return fmt.Errorf("mem: receiverFunc should return one(value) or two (value, error) but returns %d values", fnOut)
}
// check if second output value is error, if so we will return that to the caller.
if fnOut == 2 && fnTyp.Out(1) != errorTyp {
return fmt.Errorf("mem: receiverFunc second output value should be type of error")
}
out := fn.Call(emptyIn)
// first is the value we must set.
if got := out[0]; got.CanInterface() {
m.Set(key, got.Interface()) // allow nil setting is part of our design decision.
}
// }else if fnOut == 1{
// return fmt.Errorf("mem: nothing to set")
// }
if fnOut == 2 {
// second value was error, return that.
if got := out[1]; got.CanInterface() {
if errG := got.Interface(); errG != nil {
if err, sure := errG.(error); sure {
return err
}
}
}
}
return nil
}
// m.Unset(MyKey) == removed, safe for concurrent access.
func (m *Memory) Unset(key uint8) (removed bool) {
if len(m.tmp) == 0 {
return
}
if m.Has(key) {
m.mu.Lock()
delete(m.tmp, key)
m.mu.Unlock()
removed = true
}
return
}
// m.Has(MyKey) == exists, safe for concurrent access.
func (m *Memory) Has(key uint8) bool {
if len(m.tmp) == 0 {
return false
}
m.mu.RLock()
_, exists := m.tmp[key]
m.mu.RUnlock()
return exists
}
// value, found := m.Get(MyKey), safe for concurrent access.
func (m *Memory) Get(key uint8) (value interface{}, found bool) {
if len(m.tmp) == 0 {
return
}
m.mu.Lock()
value, found = m.tmp[key]
m.mu.Unlock()
return
}
func (m *Memory) MustGet(key uint8) interface{} {
v, ok := m.Get(key)
if !ok {
panic(fmt.Sprintf("mem: key for %d missing", key))
}
if v == nil {
panic(fmt.Sprintf("mem: value for key %d is nil", key))
}
return v
}
// GetAll returns a clone of all the stored values, safe for concurrent access.
func (m *Memory) GetAll() map[uint8]interface{} {
if len(m.tmp) == 0 {
return make(map[uint8]interface{})
}
clone := make(map[uint8]interface{}, len(m.tmp))
m.mu.RLock()
for key, value := range m.tmp {
clone[key] = value
}
m.mu.RUnlock()
return clone
}
// m.Visit(MyKey, func(value MyValue) { do something with the value if found }) == visitor function was compatible and executed successfully.
// Note that it doesn't lock until the function ends, this is because the function may access other memory's function which locks and that will result on a deadlock.
func (m *Memory) Visit(key uint8, visitorFunc interface{}) bool {
if visitorFunc == nil || len(m.tmp) == 0 {
return false
}
value, ok := m.Get(key)
if !ok || value == nil { // we can't work with a nil value, even if it's there (it's possible if dev do it), so return with false.
return false
}
fn := reflect.Indirect(reflect.ValueOf(visitorFunc))
if fn.Kind() == reflect.Interface {
fn = fn.Elem()
}
if fn.Kind() != reflect.Func {
return false
}
fnTyp := fn.Type()
if fnTyp.NumIn() != 1 {
return false
}
v := reflect.ValueOf(value)
if fnTyp.In(0) != v.Type() {
return false
}
fn.Call([]reflect.Value{v})
return true
}
// Clear removes all the stored elements and returns the total length of the elements removed, safe for concurrent access.
func (m *Memory) Clear() int {
n := len(m.tmp)
if n == 0 {
return 0
}
m.mu.Lock()
for key := range m.tmp {
delete(m.tmp, key)
}
m.mu.Unlock()
return n
}
func makeMemory() *Memory {
return &Memory{tmp: make(map[uint8]interface{})}
}
func GetMemory(cmd *cobra.Command) *Memory {
if cmd == nil {
return nil
}
app := Get(cmd)
if app == nil ||
// do not initialize if memory is nil,
// package-level function with *cobra.Command as a receiver means that it should be used after the `Build/Run` state.
app.Memory == nil {
return nil
}
return app.Memory
}