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  }