github.com/aykevl/tinygo@v0.5.0/src/runtime/string.go (about)

     1  package runtime
     2  
     3  // This file implements functions related to Go strings.
     4  
     5  import (
     6  	"unsafe"
     7  )
     8  
     9  // The underlying struct for the Go string type.
    10  type _string struct {
    11  	ptr    *byte
    12  	length uintptr
    13  }
    14  
    15  // The iterator state for a range over a string.
    16  type stringIterator struct {
    17  	byteindex uintptr
    18  }
    19  
    20  // Return true iff the strings match.
    21  //go:nobounds
    22  func stringEqual(x, y string) bool {
    23  	if len(x) != len(y) {
    24  		return false
    25  	}
    26  	for i := 0; i < len(x); i++ {
    27  		if x[i] != y[i] {
    28  			return false
    29  		}
    30  	}
    31  	return true
    32  }
    33  
    34  // Return true iff x < y.
    35  //go:nobounds
    36  func stringLess(x, y string) bool {
    37  	l := len(x)
    38  	if m := len(y); m < l {
    39  		l = m
    40  	}
    41  	for i := 0; i < l; i++ {
    42  		if x[i] < y[i] {
    43  			return true
    44  		}
    45  		if x[i] > y[i] {
    46  			return false
    47  		}
    48  	}
    49  	return len(x) < len(y)
    50  }
    51  
    52  // Add two strings together.
    53  func stringConcat(x, y _string) _string {
    54  	if x.length == 0 {
    55  		return y
    56  	} else if y.length == 0 {
    57  		return x
    58  	} else {
    59  		length := x.length + y.length
    60  		buf := alloc(length)
    61  		memcpy(buf, unsafe.Pointer(x.ptr), x.length)
    62  		memcpy(unsafe.Pointer(uintptr(buf)+x.length), unsafe.Pointer(y.ptr), y.length)
    63  		return _string{ptr: (*byte)(buf), length: length}
    64  	}
    65  }
    66  
    67  // Create a string from a []byte slice.
    68  func stringFromBytes(x struct {
    69  	ptr *byte
    70  	len uintptr
    71  	cap uintptr
    72  }) _string {
    73  	buf := alloc(x.len)
    74  	memcpy(buf, unsafe.Pointer(x.ptr), x.len)
    75  	return _string{ptr: (*byte)(buf), length: x.len}
    76  }
    77  
    78  // Convert a string to a []byte slice.
    79  func stringToBytes(x _string) (slice struct {
    80  	ptr *byte
    81  	len uintptr
    82  	cap uintptr
    83  }) {
    84  	buf := alloc(x.length)
    85  	memcpy(buf, unsafe.Pointer(x.ptr), x.length)
    86  	slice.ptr = (*byte)(buf)
    87  	slice.len = x.length
    88  	slice.cap = x.length
    89  	return
    90  }
    91  
    92  // Create a string from a Unicode code point.
    93  func stringFromUnicode(x rune) _string {
    94  	array, length := encodeUTF8(x)
    95  	// Array will be heap allocated.
    96  	// The heap most likely doesn't work with blocks below 4 bytes, so there's
    97  	// no point in allocating a smaller buffer for the string here.
    98  	return _string{ptr: (*byte)(unsafe.Pointer(&array)), length: length}
    99  }
   100  
   101  // Iterate over a string.
   102  // Returns (ok, key, value).
   103  func stringNext(s string, it *stringIterator) (bool, int, rune) {
   104  	if len(s) <= int(it.byteindex) {
   105  		return false, 0, 0
   106  	}
   107  	i := int(it.byteindex)
   108  	r, length := decodeUTF8(s, it.byteindex)
   109  	it.byteindex += length
   110  	return true, i, r
   111  }
   112  
   113  // Convert a Unicode code point into an array of bytes and its length.
   114  func encodeUTF8(x rune) ([4]byte, uintptr) {
   115  	// https://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16
   116  	// Note: this code can probably be optimized (in size and speed).
   117  	switch {
   118  	case x <= 0x7f:
   119  		return [4]byte{byte(x), 0, 0, 0}, 1
   120  	case x <= 0x7ff:
   121  		b1 := 0xc0 | byte(x>>6)
   122  		b2 := 0x80 | byte(x&0x3f)
   123  		return [4]byte{b1, b2, 0, 0}, 2
   124  	case x <= 0xffff:
   125  		b1 := 0xe0 | byte(x>>12)
   126  		b2 := 0x80 | byte((x>>6)&0x3f)
   127  		b3 := 0x80 | byte((x>>0)&0x3f)
   128  		return [4]byte{b1, b2, b3, 0}, 3
   129  	case x <= 0x10ffff:
   130  		b1 := 0xf0 | byte(x>>18)
   131  		b2 := 0x80 | byte((x>>12)&0x3f)
   132  		b3 := 0x80 | byte((x>>6)&0x3f)
   133  		b4 := 0x80 | byte((x>>0)&0x3f)
   134  		return [4]byte{b1, b2, b3, b4}, 4
   135  	default:
   136  		// Invalid Unicode code point.
   137  		return [4]byte{0xef, 0xbf, 0xbd, 0}, 3
   138  	}
   139  }
   140  
   141  // Decode a single UTF-8 character from a string.
   142  //go:nobounds
   143  func decodeUTF8(s string, index uintptr) (rune, uintptr) {
   144  	remaining := uintptr(len(s)) - index // must be >= 1 before calling this function
   145  	x := s[index]
   146  	switch {
   147  	case x&0x80 == 0x00: // 0xxxxxxx
   148  		return rune(x), 1
   149  	case x&0xe0 == 0xc0: // 110xxxxx
   150  		if remaining < 2 {
   151  			return 0xfffd, 1
   152  		}
   153  		return (rune(x&0x1f) << 6) | (rune(s[index+1]) & 0x3f), 2
   154  	case x&0xf0 == 0xe0: // 1110xxxx
   155  		if remaining < 3 {
   156  			return 0xfffd, 1
   157  		}
   158  		return (rune(x&0x0f) << 12) | ((rune(s[index+1]) & 0x3f) << 6) | (rune(s[index+2]) & 0x3f), 3
   159  	case x&0xf8 == 0xf0: // 11110xxx
   160  		if remaining < 4 {
   161  			return 0xfffd, 1
   162  		}
   163  		return (rune(x&0x07) << 18) | ((rune(s[index+1]) & 0x3f) << 12) | ((rune(s[index+2]) & 0x3f) << 6) | (rune(s[index+3]) & 0x3f), 4
   164  	default:
   165  		return 0xfffd, 1
   166  	}
   167  }
   168  
   169  // indexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
   170  //go:linkname indexByte strings.IndexByte
   171  func indexByte(s string, c byte) int {
   172  	for i := 0; i < len(s); i++ {
   173  		if s[i] == c {
   174  			return i
   175  		}
   176  	}
   177  	return -1
   178  }