github.com/primecitizens/pcz/std@v0.2.1/runtime/builtin_string.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright 2023 The Prime Citizens 3 4 //go:build pcz 5 6 package runtime 7 8 import ( 9 "unsafe" 10 11 "github.com/primecitizens/pcz/std/core/abi" 12 "github.com/primecitizens/pcz/std/core/arch" 13 "github.com/primecitizens/pcz/std/core/asan" 14 "github.com/primecitizens/pcz/std/core/assert" 15 "github.com/primecitizens/pcz/std/core/cmp" 16 "github.com/primecitizens/pcz/std/core/mem" 17 "github.com/primecitizens/pcz/std/core/msan" 18 "github.com/primecitizens/pcz/std/core/os" 19 "github.com/primecitizens/pcz/std/core/race" 20 "github.com/primecitizens/pcz/std/text/unicode/common" 21 "github.com/primecitizens/pcz/std/text/unicode/utf8" 22 ) 23 24 // The constant is known to the compiler. 25 // There is no fundamental theory behind this number. 26 const tmpStringBufSize = 32 27 28 type tmpBuf [tmpStringBufSize]byte 29 30 // 31 // string operations 32 // 33 34 func concatstring2(buf *tmpBuf, a0, a1 string) string { 35 return concatstrings(buf, []string{a0, a1}) 36 } 37 38 func concatstring3(buf *tmpBuf, a0, a1, a2 string) string { 39 return concatstrings(buf, []string{a0, a1, a2}) 40 } 41 42 func concatstring4(buf *tmpBuf, a0, a1, a2, a3 string) string { 43 return concatstrings(buf, []string{a0, a1, a2, a3}) 44 } 45 46 func concatstring5(buf *tmpBuf, a0, a1, a2, a3, a4 string) string { 47 return concatstrings(buf, []string{a0, a1, a2, a3, a4}) 48 } 49 50 func concatstrings(buf *tmpBuf, a []string) string { 51 idx := 0 52 l := 0 53 count := 0 54 for i, x := range a { 55 n := len(x) 56 if n == 0 { 57 continue 58 } 59 if l+n < l { 60 assert.Throw("string", "concatenation", "too", "long") 61 } 62 l += n 63 count++ 64 idx = i 65 } 66 if count == 0 { 67 return "" 68 } 69 70 // If there is just one string and either it is not on the stack 71 // or our result does not escape the calling frame (buf != nil), 72 // then we can return that string directly. 73 if count == 1 && (buf != nil || !stringDataOnStack(a[idx])) { 74 return a[idx] 75 } 76 s, b := rawstringtmp(buf, l) 77 for _, x := range a { 78 copy(b, x) 79 b = b[len(x):] 80 } 81 return s 82 } 83 84 func cmpstring(a0, a1 string) int { return cmp.String(a0, a1) } 85 86 func intstring(buf *[4]byte, v int64) (s string) { 87 var b []byte 88 if buf != nil { 89 b = buf[:] 90 s = slicebytetostringtmp(&b[0], len(b)) 91 } else { 92 s, b = rawstring(4) 93 } 94 if int64(rune(v)) != v { 95 v = common.RuneError 96 } 97 98 _, n := utf8.EncodeRune(b[:0], rune(v)) 99 return s[:n] 100 } 101 102 // slicebytetostring converts a byte slice to a string. 103 // It is inserted by the compiler into generated code. 104 // ptr is a pointer to the first element of the slice; 105 // n is the length of the slice. 106 // Buf is a fixed-size buffer for the result, 107 // it is not nil if the result does not escape. 108 func slicebytetostring(buf *tmpBuf, ptr *byte, n int) string { 109 if n == 0 { 110 // Turns out to be a relatively common case. 111 // Consider that you want to parse out data between parens in "foo()bar", 112 // you find the indices and convert the subslice to string. 113 return "" 114 } 115 116 if race.Enabled { 117 race.ReadRangePC(unsafe.Pointer(ptr), 118 uintptr(n), 119 getcallerpc(), 120 abi.FuncPCABIInternal(slicebytetostring)) 121 } 122 if msan.Enabled { 123 msan.Read(unsafe.Pointer(ptr), uintptr(n)) 124 } 125 if asan.Enabled { 126 asan.Read(unsafe.Pointer(ptr), uintptr(n)) 127 } 128 129 if n == 1 { 130 p := unsafe.Pointer(&staticuint64s[*ptr]) 131 if arch.BigEndian { 132 p = unsafe.Add(p, 7) 133 } 134 return unsafe.String((*byte)(p), 1) 135 } 136 137 var p unsafe.Pointer 138 if buf != nil && n <= len(buf) { 139 p = unsafe.Pointer(buf) 140 } else { 141 p = explicit_mallocgc(nil, uintptr(n), false) 142 } 143 144 mem.Move(p, unsafe.Pointer(ptr), uintptr(n)) 145 return unsafe.String((*byte)(p), n) 146 } 147 148 // slicebytetostringtmp returns a "string" referring to the actual []byte bytes. 149 // 150 // Callers need to ensure that the returned string will not be used after 151 // the calling goroutine modifies the original slice or synchronizes with 152 // another goroutine. 153 // 154 // The function is only called when instrumenting 155 // and otherwise intrinsified by the compiler. 156 // 157 // Some internal compiler optimizations use this function. 158 // - Used for m[T1{... Tn{..., string(k), ...} ...}] and m[string(k)] 159 // where k is []byte, T1 to Tn is a nesting of struct and array literals. 160 // - Used for "<"+string(b)+">" concatenation where b is []byte. 161 // - Used for string(b)=="foo" comparison where b is []byte. 162 // 163 // NOTE: this function is implemented as a compiler intrinsic when not instrumenting. 164 // See $GOROOT/src/cmd/compile/internal/ssagen/ssa.go#func:InitTables 165 func slicebytetostringtmp(ptr *byte, n int) string { 166 if race.Enabled && n > 0 { 167 race.ReadRangePC( 168 unsafe.Pointer(ptr), 169 uintptr(n), 170 getcallerpc(), 171 abi.FuncPCABIInternal(slicebytetostringtmp), 172 ) 173 } 174 if msan.Enabled && n > 0 { 175 msanread(unsafe.Pointer(ptr), uintptr(n)) 176 } 177 if asan.Enabled && n > 0 { 178 asanread(unsafe.Pointer(ptr), uintptr(n)) 179 } 180 181 return unsafe.String(ptr, n) 182 } 183 184 func slicerunetostring(buf *tmpBuf, a []rune) string { 185 if race.Enabled && len(a) > 0 { 186 race.ReadRangePC( 187 unsafe.Pointer(&a[0]), 188 uintptr(len(a))*unsafe.Sizeof(a[0]), 189 getcallerpc(), 190 abi.FuncPCABIInternal(slicerunetostring), 191 ) 192 } 193 if msan.Enabled && len(a) > 0 { 194 msanread(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0])) 195 } 196 if asan.Enabled && len(a) > 0 { 197 asanread(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0])) 198 } 199 200 var ( 201 size1, size2, n int 202 203 s, b = rawstringtmp(buf, size1+3) 204 ) 205 for _, r := range a { 206 size1 += utf8.RuneLen(r) 207 } 208 209 for _, r := range a { 210 // check for race 211 if size2 >= size1 { 212 break 213 } 214 b, n = utf8.EncodeRune(b, r) 215 size2 += n 216 } 217 218 return s[:size2] 219 } 220 221 func stringtoslicebyte(buf *tmpBuf, s string) []byte { 222 var b []byte 223 if buf != nil && len(s) <= len(buf) { 224 *buf = tmpBuf{} 225 b = buf[:len(s)] 226 } else { 227 b = rawbyteslice(len(s)) 228 } 229 copy(b, s) 230 return b 231 } 232 233 func stringtoslicerune(buf *[32]rune, s string) []rune { 234 // two passes. 235 // unlike slicerunetostring, no race because strings are immutable. 236 n := 0 237 for range s { 238 n++ 239 } 240 241 var a []rune 242 if buf != nil && n <= len(buf) { 243 *buf = [tmpStringBufSize]rune{} 244 a = buf[:n] 245 } else { 246 a = rawruneslice(n) 247 } 248 249 n = 0 250 for _, r := range s { 251 a[n] = r 252 n++ 253 } 254 return a 255 } 256 257 func decoderune(s string, k int) (rune, int) { return utf8.IterRune(s, k) } 258 func countrunes(s string) int { return utf8.Count(s) } 259 260 func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) { 261 if buf != nil && l <= len(buf) { 262 b = buf[:l] 263 s = slicebytetostringtmp(&b[0], len(b)) 264 } else { 265 s, b = rawstring(l) 266 } 267 return 268 } 269 270 // stringDataOnStack reports whether the string's data is 271 // stored on the current goroutine's stack. 272 func stringDataOnStack(s string) bool { 273 return getg().Stack.PointerOnStack(uintptr(unsafe.Pointer(unsafe.StringData(s)))) 274 } 275 276 // rawruneslice allocates a new rune slice. The rune slice is not zeroed. 277 func rawruneslice(size int) (b []rune) { 278 if uintptr(size) > os.MaxAlloc/4 { 279 assert.Throw("out", "of", "memory") 280 } 281 sz := uintptr(size) * 4 282 p := explicit_mallocgc(nil, sz, false) 283 if sz != uintptr(size)*4 { 284 mem.Clear(unsafe.Add(p, uintptr(size)*4), sz-uintptr(size)*4) 285 } 286 287 *(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(sz / 4)} 288 return 289 } 290 291 // rawbyteslice allocates a new byte slice. The byte slice is not zeroed. 292 func rawbyteslice(size int) (b []byte) { 293 p := explicit_mallocgc(nil, uintptr(size), false) 294 295 *(*slice)(unsafe.Pointer(&b)) = slice{p, size, size} 296 return 297 } 298 299 // rawstring allocates storage for a new string. The returned 300 // string and byte slice both refer to the same storage. 301 // The storage is not zeroed. Callers should use 302 // b to set the string contents and then drop b. 303 func rawstring(size int) (s string, b []byte) { 304 p := explicit_mallocgc(nil, uintptr(size), false) 305 return unsafe.String((*byte)(p), size), unsafe.Slice((*byte)(p), size) 306 }