forked from Velocidex/vfilter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.go
252 lines (203 loc) · 4.78 KB
/
utils.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
package vfilter
import (
"context"
"fmt"
"reflect"
"runtime"
"sort"
"strings"
"sync"
"unicode"
"github.com/Velocidex/ordereddict"
"github.com/alecthomas/repr"
)
func Debug(arg interface{}) {
if arg != nil {
repr.Println(arg)
} else {
repr.Println("nil")
}
}
func merge_channels(cs []<-chan Any) <-chan Any {
var wg sync.WaitGroup
out := make(chan Any)
// Start an output goroutine for each input channel in cs. output
// copies values from c to out until c is closed, then calls wg.Done.
output := func(c <-chan Any) {
for n := range c {
out <- n
}
wg.Done()
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
// Start a goroutine to close out once all the output goroutines are
// done. This must start after the wg.Add call.
go func() {
wg.Wait()
close(out)
}()
return out
}
// Is the symbol exported by Go? Only names with upper case are exported.
func is_exported(name string) bool {
switch name {
// Ignore common methods which should not be exported.
case "MarshalJSON", "MarshalYAML":
return false
default:
if len(name) == 0 || name[0] == '_' {
return false
}
runes := []rune(name)
return runes[0] == unicode.ToUpper(runes[0])
}
}
func _Callable(method_value reflect.Value, field_name string) bool {
if !method_value.IsValid() {
return false
}
// The name must be exportable.
if !is_exported(field_name) {
return false
}
// The function must have no args.
if method_value.Type().NumIn() != 0 {
return false
}
return true
}
func IsNil(a interface{}) bool {
defer func() { recover() }()
return a == nil || reflect.ValueOf(a).IsNil()
}
// A real type which encodes to JSON NULL. Using go's nil is dangerous
// because it forces constant checking for nil pointer dereference. It
// is safer to just return this value when VQL needs to return NULL.
type Null struct{}
func (self Null) MarshalJSON() ([]byte, error) {
return []byte("null"), nil
}
func (self Null) String() string {
return "Null"
}
func is_null_obj(a Any) bool {
if a == nil {
return true
}
switch a.(type) {
case Null, *Null:
return true
default:
return false
}
}
type _NullAssociative struct{}
func (self _NullAssociative) Applicable(a Any, b Any) bool {
return is_null_obj(a)
}
func (self _NullAssociative) Associative(scope *Scope, a Any, b Any) (Any, bool) {
return Null{}, true
}
func (self _NullAssociative) GetMembers(scope *Scope, a Any) []string {
return []string{}
}
type _NullEqProtocol struct{}
func (self _NullEqProtocol) Applicable(a Any, b Any) bool {
return is_null_obj(a) || is_null_obj(b)
}
func (self _NullEqProtocol) Eq(scope *Scope, a Any, b Any) bool {
if is_null_obj(a) && is_null_obj(b) {
return true
}
return false
}
type _NullBoolProtocol struct{}
func (self _NullBoolProtocol) Applicable(a Any) bool {
return is_null_obj(a)
}
func (self _NullBoolProtocol) Bool(scope *Scope, a Any) bool {
if is_null_obj(a) {
return false
}
return true
}
func InString(hay *[]string, needle string) bool {
for _, x := range *hay {
if x == needle {
return true
}
}
return false
}
// Returns a unique ID for the object.
func GetID(obj Any) string {
return fmt.Sprintf("%p", obj)
}
// RowToDict reduces the row into a simple Dict. This materializes any
// lazy queries that are stored in the row into a stable materialized
// dict.
func RowToDict(
ctx context.Context,
scope *Scope, row Row) *ordereddict.Dict {
// Even if it is already a dict we still need to iterate its
// values to make sure they are fully materialized.
result := ordereddict.NewDict()
for _, column := range scope.GetMembers(row) {
value, pres := scope.Associative(row, column)
if pres {
result.Set(column, normalize_value(ctx, scope, value, 0))
}
}
return result
}
func normalize_value(ctx context.Context, scope *Scope, value Any, depth int) Any {
if depth > 10 {
return Null{}
}
if value == nil {
value = Null{}
}
switch t := value.(type) {
case Null:
return value
case fmt.Stringer:
return value
case []byte:
return string(t)
case LazyExpr:
return normalize_value(ctx, scope, t.Reduce(), depth+1)
case StoredQuery:
return Materialize(ctx, scope, t)
default:
// Pass directly to Json Marshal
return value
}
}
// Get a list of similar sounding plugins.
func getSimilarPlugins(scope *Scope, name string) []string {
result := []string{}
parts := strings.Split(name, "_")
scope.Lock()
defer scope.Unlock()
for _, part := range parts {
for k, _ := range scope.plugins {
if strings.Contains(k, part) && !InString(&result, k) {
result = append(result, k)
}
}
}
sort.Strings(result)
return result
}
func RecoverVQL(scope *Scope) {
r := recover()
if r != nil {
scope.Log("PANIC: %v\n", r)
buffer := make([]byte, 4096)
n := runtime.Stack(buffer, false /* all */)
scope.Log("%s", buffer[:n])
}
}