-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathinternal.go
204 lines (171 loc) · 4.55 KB
/
internal.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
package gg
import (
"io/fs"
"math"
"path/filepath"
r "reflect"
"regexp"
"strings"
u "unsafe"
)
func typeBitSize(typ r.Type) int { return int(typ.Size() * 8) }
// Borrowed from the standard library. Requires caution.
func noescape(src u.Pointer) u.Pointer {
out := uintptr(src)
//nolint:staticcheck
return u.Pointer(out ^ 0)
}
func errAppendInner(buf Buf, err error) Buf {
if err != nil {
buf.AppendString(`: `)
buf.AppendError(err)
}
return buf
}
func errAppendTraceIndentWithNewline(buf Buf, trace Trace) Buf {
if trace.IsNotEmpty() {
buf.AppendNewline()
return errAppendTraceIndent(buf, trace)
}
return buf
}
func errAppendTraceIndent(buf Buf, trace Trace) Buf {
if trace.IsNotEmpty() {
buf.AppendString(`trace:`)
buf = trace.AppendIndentTo(buf, 1)
}
return buf
}
func isFuncNameAnon(val string) bool {
const pre = `func`
return strings.HasPrefix(val, pre) && hasPrefixDigit(val[len(pre):])
}
func hasPrefixDigit(val string) bool { return isDigit(TextHeadByte(val)) }
func isDigit(val byte) bool { return val >= '0' && val <= '9' }
func validateLenMatch(one, two int) {
if one != two {
panic(Errf(
`unable to iterate pairwise: length mismatch: %v and %v`,
one, two,
))
}
}
// Note: `strconv.ParseBool` is too permissive for our taste.
func parseBool(src string, out r.Value) error {
switch src {
case `true`:
out.SetBool(true)
return nil
case `false`:
out.SetBool(false)
return nil
default:
return ErrParse(ErrInvalidInput, src, Type[bool]())
}
}
/*
Somewhat similar to `filepath.Rel`, but doesn't support `..`, performs
significantly better, and returns the path as-is when it doesn't start
with the given base.
*/
func relOpt(base, src string) string {
if strings.HasPrefix(src, base) {
rem := src[len(base):]
if len(rem) > 0 && rem[0] == filepath.Separator {
return rem[1:]
}
}
return src
}
func isIntString(val string) bool {
if len(val) <= 0 {
return false
}
if len(val) > 0 && (val[0] == '+' || val[0] == '-') {
val = val[1:]
}
if len(val) <= 0 {
return false
}
// Note: here we iterate bytes rather than UTF-8 characters because digits
// are always single byte and we abort on the first mismatch. This may be
// slightly more efficient than iterating characters.
for ind := 0; ind < len(val); ind++ {
if !isDigit(val[ind]) {
return false
}
}
return true
}
func isCliFlag(val string) bool { return TextHeadByte(val) == '-' }
func isCliFlagValid(val string) bool { return reCliFlag.Get().MatchString(val) }
/*
Must begin with `-` and consist of alphanumeric characters, optionally
containing `-` between those characters.
TODO test.
*/
var reCliFlag = NewLazy(func() *regexp.Regexp {
return regexp.MustCompile(`^-+[\p{L}\d]+(?:[\p{L}\d-]*[\p{L}\d])?$`)
})
func cliFlagSplit(src string) (_ string, _ string, _ bool) {
if !isCliFlag(src) {
return
}
ind := strings.IndexRune(src, '=')
if ind >= 0 {
return src[:ind], src[ind+1:], true
}
return src, ``, false
}
/*
Represents nodes in a linked list. Normally in Go, linked lists tend to be an
anti-pattern; slices perform better in most scenarios, and don't require an
additional abstraction. However, there is one valid scenario for linked lists:
when nodes are pointers to local variables, when those local variables don't
escape, and when they represent addresses to actual memory regions in stack
frames. In this case, this may provide us with a resizable data structure
allocated entirely on the stack, which is useful for book-keeping in recursive
tree-walking or graph-walking algorithms. We currently do not verify if the
trick has the expected efficiency, as the overheads are minimal.
*/
type node[A comparable] struct {
tail *node[A]
val A
}
func (self node[A]) has(val A) bool {
return self.val == val || (self.tail != nil && self.tail.has(val))
}
func (self *node[A]) cons(val A) (out node[A]) {
out.tail = self
out.val = val
return
}
/*
Suboptimal: doesn't preallocate capacity. We only call this in case of errors,
so the overhead should be negligible.
*/
func (self node[A]) vals() (out []A) {
out = append(out, self.val)
node := self.tail
for node != nil {
out = append(out, node.val)
node = node.tail
}
return
}
func safeUintToInt(src uint) int {
if src > math.MaxInt {
return math.MaxInt
}
return int(src)
}
func isByteNewline(val byte) bool { return val == '\n' || val == '\r' }
func errCollMissing[Val, Key any](key Key) Err {
return Errf(`missing value of type %v for key %v`, Type[Val](), key)
}
func dirEntryToFileName(src fs.DirEntry) (_ string) {
if src == nil || src.IsDir() {
return
}
return src.Name()
}