-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.go
156 lines (135 loc) · 2.87 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
// Package util contains various general utility functions used throughout
// the project.
package util
import (
"crypto/md5"
"encoding/base64"
)
// WrapError wraps error types to create compound error chains
func WrapError(text string, err error) error {
return wrappedError{
text: text,
inner: err,
}
}
type wrappedError struct {
text string
inner error
}
func (e wrappedError) Error() string {
text := e.text
if e.inner != nil {
text += ": " + e.inner.Error()
}
return text
}
// Waterfall executes a slice of functions until the first error returned. This
// error, if any, is returned to the caller.
func Waterfall(fns ...func() error) (err error) {
for _, fn := range fns {
err = fn()
if err != nil {
break
}
}
return
}
// Execute functions in parallel. The first error is returned, if any.
func Parallel(fns ...func() error) error {
ch := make(chan error)
for i := range fns {
fn := fns[i]
go func() {
ch <- fn()
}()
}
for range fns {
if err := <-ch; err != nil {
return err
}
}
return nil
}
// HashBuffer computes a base64 MD5 hash from a buffer
func HashBuffer(buf []byte) string {
hash := md5.Sum(buf)
return base64.RawStdEncoding.EncodeToString(hash[:])
}
// ConcatStrings efficiently concatenates strings with only one extra allocation
func ConcatStrings(s ...string) string {
l := 0
for _, s := range s {
l += len(s)
}
b := make([]byte, 0, l)
for _, s := range s {
b = append(b, s...)
}
return string(b)
}
// CloneBytes creates a copy of b
func CloneBytes(b []byte) []byte {
cp := make([]byte, len(b))
copy(cp, b)
return cp
}
// SplitPunctuation splits off one byte of leading and trailing punctuation,
// if any, and returns the 3 split parts. If there is no edge punctuation, the
// respective byte = 0.
func SplitPunctuation(word []byte) (leading byte, mid []byte, trailing byte) {
mid = word
// Split leading
if len(mid) < 2 {
return
}
if isPunctuation(mid[0]) {
leading = mid[0]
mid = mid[1:]
}
// Split trailing
l := len(mid)
if l < 2 {
return
}
if isPunctuation(mid[l-1]) {
trailing = mid[l-1]
mid = mid[:l-1]
}
return
}
// isPunctuation returns, if b is a punctuation symbol
func isPunctuation(b byte) bool {
switch b {
case '!', '"', '\'', '(', ')', ',', '-', '.', ':', ';', '?', '[', ']':
return true
default:
return false
}
}
// SplitPunctuationString splits off one byte of leading and trailing
// punctuation, if any, and returns the 3 split parts. If there is no edge
// punctuation, the respective byte = 0.
func SplitPunctuationString(word string) (
leading byte, mid string, trailing byte,
) {
// Generic copy paste :^)
mid = word
// Split leading
if len(mid) < 2 {
return
}
if isPunctuation(mid[0]) {
leading = mid[0]
mid = mid[1:]
}
// Split trailing
l := len(mid)
if l < 2 {
return
}
if isPunctuation(mid[l-1]) {
trailing = mid[l-1]
mid = mid[:l-1]
}
return
}