-
Notifications
You must be signed in to change notification settings - Fork 43
/
util.go
410 lines (376 loc) · 8.71 KB
/
util.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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
)
// WithMutex extends the Mutex type with the convenient .With(func) function
type WithMutex struct {
sync.Mutex
}
// With executes the given function with the mutex locked
func (m *WithMutex) With(f func()) {
m.Mutex.Lock()
f()
m.Mutex.Unlock()
}
// Converts the given Unix timestamp to time.Time
func unixTimeStampToUTCTime(ts int) time.Time {
return time.Unix(int64(ts), 0)
}
// Gets the current Unix timestamp in UTC
func getNowUTC() int64 {
return time.Now().UTC().Unix()
}
// Mashals the given map of strings to JSON
func stringMap2JsonBytes(m map[string]string) []byte {
b, err := json.Marshal(m)
if err != nil {
log.Panicln("Cannot json-ise the map:", err)
}
return b
}
// Returns a hex-encoded hash of the given byte slice
func hashBytesToHexString(b []byte) string {
hash := sha256.Sum256(b)
return hex.EncodeToString(hash[:])
}
// Returns a hex-encoded hash of the given file
func hashFileToHexString(fileName string) (string, error) {
file, err := os.Open(fileName)
if err != nil {
return "", err
}
defer func() {
err = file.Close()
if err != nil {
log.Printf("hashFileToHexString file.Close: %v", err)
}
}()
hash := sha256.New()
_, err = io.Copy(hash, file)
if err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
func hashFileToBytes(fileName string) ([]byte, error) {
file, err := os.Open(fileName)
if err != nil {
return nil, err
}
defer func() {
err = file.Close()
if err != nil {
log.Printf("hashFileToHexString file.Close: %v", err)
}
}()
hash := sha256.New()
_, err = io.Copy(hash, file)
if err != nil {
return nil, err
}
return hash.Sum(nil), nil
}
func mustDecodeHex(hexs string) []byte {
b, err := hex.DecodeString(hexs)
if err != nil {
log.Panic("mustDecodeHex:", err)
}
return b
}
// StrIfMap is a convenient data type for dealing with maps of strings to interface{}
type StrIfMap map[string]interface{}
// GetString returns a string from this map.
func (m StrIfMap) GetString(key string) (string, error) {
var ok bool
var ii interface{}
if ii, ok = m[key]; !ok {
return "", fmt.Errorf("No '%s' key in map", key)
}
var val string
if val, ok = ii.(string); !ok {
return "", fmt.Errorf("The '%s' key in map is not a string", key)
}
return val, nil
}
// GetInt64 returns an Int64 from this map.
func (m StrIfMap) GetInt64(key string) (int64, error) {
var ok bool
var ii interface{}
if ii, ok = m[key]; !ok {
return 0, fmt.Errorf("No '%s' key in map", key)
}
var val float64
if val, ok = ii.(float64); !ok {
return 0, fmt.Errorf("The '%s' key in map is not an int64", key)
}
return int64(val), nil
}
// GetInt returns an int from this map.
func (m StrIfMap) GetInt(key string) (int, error) {
var ok bool
var ii interface{}
if ii, ok = m[key]; !ok {
return 0, fmt.Errorf("No '%s' key in map", key)
}
var val float64
if val, ok = ii.(float64); !ok {
return 0, fmt.Errorf("The '%s' key in map is not an int64", key)
}
return int(val), nil
}
// GetIntStringMap returns a map of integers to strings from this map.
func (m StrIfMap) GetIntStringMap(key string) (map[int]string, error) {
var ok bool
var ii interface{}
if ii, ok = m[key]; !ok {
return nil, fmt.Errorf("No '%s' key in map", key)
}
var val map[string]interface{}
if val, ok = ii.(map[string]interface{}); !ok {
return nil, fmt.Errorf("The '%s' key in map is not a map[string]interface{}", key)
}
var val2 = make(map[int]string)
for k, v := range val {
i, err := strconv.Atoi(k)
if err != nil {
return nil, err
}
var s string
if s, ok = v.(string); !ok {
return nil, fmt.Errorf("The value in the hashes map is not a string?")
}
val2[i] = s
}
return val2, nil
}
// GetStringList returns a slice of strings from this map
func (m StrIfMap) GetStringList(key string) ([]string, error) {
var ok bool
var ii interface{}
if ii, ok = m[key]; !ok {
return nil, fmt.Errorf("No '%s' key in map", key)
}
var ilist []interface{}
if ilist, ok = ii.([]interface{}); !ok {
return nil, fmt.Errorf("The '%s' key in map is not an array of interface{}", key)
}
var result []string
for n, is := range ilist {
var s string
if s, ok = is.(string); !ok {
return nil, fmt.Errorf("Element of %d the '%s' key is not a string", n, key)
}
result = append(result, s)
}
return result, nil
}
// StringSetWithExpiry is a set of strings whose entries disappear after a given time.
type StringSetWithExpiry struct {
data map[string]time.Time
age time.Duration
lock WithMutex
}
// NewStringSetWithExpiry returns a new StringSetWithExpiry, with the given expiry duration.
func NewStringSetWithExpiry(d time.Duration) *StringSetWithExpiry {
ss := StringSetWithExpiry{data: make(map[string]time.Time), age: d}
return &ss
}
// Add adds the given string to the set
func (ss *StringSetWithExpiry) Add(s string) {
ss.lock.With(func() {
ss.data[s] = time.Now()
})
ss.CheckExpire()
}
// CheckExpire walks the set and removes the entries which have expired.
func (ss *StringSetWithExpiry) CheckExpire() int {
count := 0
ss.lock.With(func() {
var toExpire []string
for s, t := range ss.data {
d := time.Since(t)
if d >= ss.age {
toExpire = append(toExpire, s)
}
}
for _, s := range toExpire {
delete(ss.data, s)
}
count = len(toExpire)
})
return count
}
// Has tests if a string is present and not expired in this set.
func (ss *StringSetWithExpiry) Has(s string) bool {
var ok bool
ss.lock.With(func() {
var t time.Time
t, ok = ss.data[s]
if ok {
if time.Since(t) >= ss.age {
// It's there but it's expired.
ok = false
}
}
})
return ok
}
// TestAndSet atomically tests if the string s is present in the set and adds it if it isn't.
// Returns true iff it was in the set.
func (ss *StringSetWithExpiry) TestAndSet(s string) bool {
var ok bool
ss.lock.With(func() {
var t time.Time
t, ok = ss.data[s]
if !ok {
ss.data[s] = time.Now()
} else {
if time.Since(t) >= ss.age {
// It's there but it's expired.
ok = false
}
}
})
return ok
}
// Convert whatever to a JSON string
func jsonifyWhatever(i interface{}) string {
jsonb, err := json.Marshal(i)
if err != nil {
log.Panic(err)
}
return string(jsonb)
}
// Convert whatever to JSON bytes
func jsonifyWhateverToBytes(i interface{}) []byte {
jsonb, err := json.Marshal(i)
if err != nil {
log.Panic(err)
}
return jsonb
}
// Splits an address string in the form of "host:port" into its separate host and port parts
func splitAddress(address string) (string, int, error) {
i := strings.LastIndex(address, ":") // Not using strings.Split because of IPv6
var host string
var port int
var err error
if i > -1 {
host = address[0:i]
port, err = strconv.Atoi(address[i+1:])
if err != nil {
return "", 0, err
}
} else {
host = address
}
return host, port, nil
}
// Returns a list of local IP addresses
func getLocalAddresses() []string {
addresses := []string{}
ifaces, err := net.Interfaces()
if err != nil {
log.Println(err)
return addresses
}
for _, i := range ifaces {
if strings.HasPrefix(i.Name, "lo") {
continue
}
addrs, err := i.Addrs()
if err != nil {
log.Println(err)
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if strings.HasPrefix(ip.String(), "127.") {
continue
}
addresses = append(addresses, ip.String())
}
}
return addresses
}
// Returns true if s is in list
func inStrings(s string, list []string) bool {
for _, x := range list {
if s == x {
return true
}
}
return false
}
// isDirEmpty returns true if a directory is empty.
func isDirEmpty(name string) (bool, error) {
f, err := os.Open(name)
if err != nil {
return false, err
}
defer f.Close()
_, err = f.Readdirnames(1) // Or f.Readdir(1)
if err == io.EOF {
return true, nil
}
return false, err // Either not empty or error, suits both cases
}
func fileExists(name string) bool {
if _, err := os.Stat(name); err == nil {
return true
}
return false
}
// Copy the src file to dst. Any existing file will be overwritten and will not
// copy file attributes.
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Close()
}
func countStartZeroBits(b []byte) int {
nBits := 0
for i := 0; i < len(b); i++ {
if b[i] == 0 {
nBits += 8
} else {
for z := uint(7); z >= 0; z-- {
if b[i]&(1<<z) == 0 {
nBits++
} else {
break
}
}
}
}
return nBits
}