github.com/mitranim/gg@v0.1.17/internal.go (about) 1 package gg 2 3 import ( 4 "math" 5 "path/filepath" 6 r "reflect" 7 "regexp" 8 "strings" 9 u "unsafe" 10 ) 11 12 func typeBitSize(typ r.Type) int { return int(typ.Size() * 8) } 13 14 // Borrowed from the standard library. Requires caution. 15 func noescape(src u.Pointer) u.Pointer { 16 out := uintptr(src) 17 //nolint:staticcheck 18 return u.Pointer(out ^ 0) 19 } 20 21 func errAppendInner(buf Buf, err error) Buf { 22 if err != nil { 23 buf.AppendString(`: `) 24 buf.AppendError(err) 25 } 26 return buf 27 } 28 29 func errAppendTraceIndent(buf Buf, trace Trace) Buf { 30 if trace.IsNotEmpty() { 31 buf.AppendNewline() 32 buf.AppendString(`trace:`) 33 buf = trace.AppendIndent(buf, 1) 34 } 35 return buf 36 } 37 38 func isFuncNameAnon(val string) bool { 39 const pre = `func` 40 return strings.HasPrefix(val, pre) && hasPrefixDigit(val[len(pre):]) 41 } 42 43 func hasPrefixDigit(val string) bool { return isDigit(TextHeadByte(val)) } 44 45 func isDigit(val byte) bool { return val >= '0' && val <= '9' } 46 47 func validateLenMatch(one, two int) { 48 if one != two { 49 panic(Errf( 50 `unable to iterate pairwise: length mismatch: %v and %v`, 51 one, two, 52 )) 53 } 54 } 55 56 // Note: `strconv.ParseBool` is too permissive for our taste. 57 func parseBool(src string, out r.Value) error { 58 switch src { 59 case `true`: 60 out.SetBool(true) 61 return nil 62 63 case `false`: 64 out.SetBool(false) 65 return nil 66 67 default: 68 return ErrParse(ErrInvalidInput, src, Type[bool]()) 69 } 70 } 71 72 /* 73 Somewhat similar to `filepath.Rel`, but doesn't support `..`, performs 74 significantly better, and returns the path as-is when it doesn't start 75 with the given base. 76 */ 77 func relOpt(base, src string) string { 78 if strings.HasPrefix(src, base) { 79 rem := src[len(base):] 80 if len(rem) > 0 && rem[0] == filepath.Separator { 81 return rem[1:] 82 } 83 } 84 return src 85 } 86 87 func isIntString(val string) bool { 88 if len(val) <= 0 { 89 return false 90 } 91 92 if len(val) > 0 && (val[0] == '+' || val[0] == '-') { 93 val = val[1:] 94 } 95 96 if len(val) <= 0 { 97 return false 98 } 99 100 // Note: here we iterate bytes rather than UTF-8 characters because digits 101 // are always single byte and we abort on the first mismatch. This may be 102 // slightly more efficient than iterating characters. 103 for ind := 0; ind < len(val); ind++ { 104 if !isDigit(val[ind]) { 105 return false 106 } 107 } 108 return true 109 } 110 111 func isCliFlag(val string) bool { return TextHeadByte(val) == '-' } 112 113 func isCliFlagValid(val string) bool { return reCliFlag.Get().MatchString(val) } 114 115 /* 116 Must begin with `-` and consist of alphanumeric characters, optionally 117 containing `-` between those characters. 118 119 TODO test. 120 */ 121 var reCliFlag = NewLazy(func() *regexp.Regexp { 122 return regexp.MustCompile(`^-+[\p{L}\d]+(?:[\p{L}\d-]*[\p{L}\d])?$`) 123 }) 124 125 func cliFlagSplit(src string) (_ string, _ string, _ bool) { 126 if !isCliFlag(src) { 127 return 128 } 129 130 ind := strings.IndexRune(src, '=') 131 if ind >= 0 { 132 return src[:ind], src[ind+1:], true 133 } 134 135 return src, ``, false 136 } 137 138 /* 139 Represents nodes in a linked list. Normally in Go, linked lists tend to be an 140 anti-pattern; slices perform better in most scenarios, and don't require an 141 additional abstraction. However, there is one valid scenario for linked lists: 142 when nodes are pointers to local variables, when those local variables don't 143 escape, and when they represent addresses to actual memory regions in stack 144 frames. In this case, this may provide us with a resizable data structure 145 allocated entirely on the stack, which is useful for book-keeping in recursive 146 tree-walking or graph-walking algorithms. We currently do not verify if the 147 trick has the expected efficiency, as the overheads are minimal. 148 */ 149 type node[A comparable] struct { 150 tail *node[A] 151 val A 152 } 153 154 func (self node[A]) has(val A) bool { 155 return self.val == val || (self.tail != nil && self.tail.has(val)) 156 } 157 158 func (self *node[A]) cons(val A) (out node[A]) { 159 out.tail = self 160 out.val = val 161 return 162 } 163 164 /* 165 Suboptimal: doesn't preallocate capacity. We only call this in case of errors, 166 so the overhead should be negligible. 167 */ 168 func (self node[A]) vals() (out []A) { 169 out = append(out, self.val) 170 node := self.tail 171 for node != nil { 172 out = append(out, node.val) 173 node = node.tail 174 } 175 return 176 } 177 178 func safeUintToInt(src uint) int { 179 if src > math.MaxInt { 180 return math.MaxInt 181 } 182 return int(src) 183 } 184 185 func isByteNewline(val byte) bool { return val == '\n' || val == '\r' } 186 187 func errCollMissing[Val, Key any](key Key) Err { 188 return Errf(`missing value of type %v for key %v`, Type[Val](), key) 189 }