github.com/gotranspile/cxgo@v0.3.7/runtime/libc/string.go (about)

     1  package libc
     2  
     3  import (
     4  	"bytes"
     5  	"strings"
     6  	"unsafe"
     7  )
     8  
     9  // CString makes a new zero-terminated byte array containing a given string.
    10  func CString(s string) *byte {
    11  	p := makePad(len(s)+1, 0)
    12  	copy(p, s)
    13  	return &p[0]
    14  }
    15  
    16  // CBytes makes a new zero-terminated byte array containing a given byte slice.
    17  func CBytes(b []byte) *byte {
    18  	p := makePad(len(b)+1, 0)
    19  	copy(p, b)
    20  	return &p[0]
    21  }
    22  
    23  // GoBytes makes a Go byte slice from a pointer to a zero-terminated byte array.
    24  // The slice will point to the same memory as ptr.
    25  func GoBytes(ptr *byte) []byte {
    26  	n := findnull(ptr)
    27  	if n == 0 {
    28  		return nil
    29  	}
    30  	return unsafe.Slice(ptr, n)
    31  }
    32  
    33  // GoString makes a Go string from a pointer to a zero-terminated byte array.
    34  func GoString(s *byte) string {
    35  	return gostring(s)
    36  }
    37  
    38  // GoBytesS is a Go-friendly analog of GoBytes.
    39  func GoBytesS(s []byte) []byte {
    40  	n := StrLenS(s)
    41  	if n == 0 {
    42  		return nil
    43  	}
    44  	return s[:n]
    45  }
    46  
    47  // GoStringS is a Go-friendly analog of GoString.
    48  func GoStringS(s []byte) string {
    49  	n := StrLenS(s)
    50  	if n == 0 {
    51  		return ""
    52  	}
    53  	return string(s[:n])
    54  }
    55  
    56  // CStringSlice convers a Go string slice to a zero-terminated array of C string pointers.
    57  func CStringSlice(arr []string) **byte {
    58  	out := make([]*byte, len(arr)+1)
    59  	for i, s := range arr {
    60  		out[i] = CString(s)
    61  	}
    62  	return &out[0]
    63  }
    64  
    65  // MemCmp compares the first count characters of the objects pointed to by lhs and rhs. The comparison is done lexicographically.
    66  //
    67  // The sign of the result is the sign of the difference between the values of the first pair of bytes (both interpreted
    68  // as byte) that differ in the objects being compared.
    69  //
    70  // The behavior is undefined if access occurs beyond the end of either object pointed to by lhs and rhs. The behavior is
    71  // undefined if either lhs or rhs is a null pointer.
    72  func MemCmp(lhs, rhs unsafe.Pointer, sz int) int {
    73  	b1, b2 := unsafe.Slice((*byte)(lhs), sz), unsafe.Slice((*byte)(rhs), sz)
    74  	return bytes.Compare(b1, b2)
    75  }
    76  
    77  // MemSet copies the value ch (after conversion to byte as if by byte(ch)) into each of the first count characters of
    78  // the object pointed to by dest.
    79  //
    80  // The behavior is undefined if access occurs beyond the end of the dest array. The behavior is undefined if dest is a
    81  // null pointer.
    82  func MemSet(p unsafe.Pointer, ch byte, sz int) unsafe.Pointer {
    83  	b := unsafe.Slice((*byte)(p), sz)
    84  	if ch == 0 {
    85  		copy(b, make([]byte, len(b)))
    86  	} else {
    87  		copy(b, bytes.Repeat([]byte{ch}, len(b)))
    88  	}
    89  	return p
    90  }
    91  
    92  // MemMove copies count characters from the object pointed to by src to the object pointed to by dest. Both objects are
    93  // interpreted as arrays of byte. The objects may overlap: copying takes place as if the characters were copied to a
    94  // temporary character array and then the characters were copied from the array to dest.
    95  //
    96  // The behavior is undefined if access occurs beyond the end of the dest array. The behavior is undefined if either dest
    97  // or src is a null pointer.
    98  func MemMove(dst, src unsafe.Pointer, sz int) unsafe.Pointer {
    99  	if sz == 0 {
   100  		return dst
   101  	}
   102  	return MemCpy(dst, src, sz)
   103  }
   104  
   105  // MemCpy copies count characters from the object pointed to by src to the object pointed to by dest. Both objects are
   106  // interpreted as arrays of byte.
   107  //
   108  // The behavior is undefined if access occurs beyond the end of the dest array. If the objects overlap (which is a
   109  // violation of the restrict contract), the behavior is undefined. The behavior is undefined if either dest or src is a
   110  // null pointer.
   111  func MemCpy(dst, src unsafe.Pointer, sz int) unsafe.Pointer {
   112  	if dst == nil {
   113  		panic("nil destination")
   114  	}
   115  	if sz == 0 || src == nil {
   116  		return dst
   117  	}
   118  	bdst := unsafe.Slice((*byte)(dst), sz)
   119  	bsrc := unsafe.Slice((*byte)(src), sz)
   120  	copy(bdst, bsrc)
   121  	return dst
   122  }
   123  
   124  // MemChr finds the first occurrence of ch (after conversion to byte as if by byte(ch)) in the initial count characters
   125  // (each interpreted as byte) of the object pointed to by ptr.
   126  //
   127  // The behavior is undefined if access occurs beyond the end of the array searched. The behavior is undefined if ptr is
   128  // a null pointer.
   129  func MemChr(ptr *byte, ch byte, sz int) *byte {
   130  	if ptr == nil || sz == 0 {
   131  		return nil
   132  	}
   133  	b := unsafe.Slice(ptr, sz)
   134  	i := bytes.IndexByte(b, ch)
   135  	if i < 0 {
   136  		return nil
   137  	}
   138  	return &b[i]
   139  }
   140  
   141  // StrLen returns the length of the given null-terminated byte string, that is, the number of characters in a character
   142  // array whose first element is pointed to by str up to and not including the first null character.
   143  //
   144  // The behavior is undefined if str is not a pointer to a null-terminated byte string.
   145  func StrLen(str *byte) int {
   146  	return findnull(str)
   147  }
   148  
   149  // StrLenS is a Go-friendly analog of StrLen.
   150  func StrLenS(s []byte) int {
   151  	if len(s) == 0 {
   152  		return 0
   153  	}
   154  	i := bytes.IndexByte(s, 0)
   155  	if i < 0 {
   156  		return len(s)
   157  	}
   158  	return i
   159  }
   160  
   161  // StrChr finds the first occurrence of ch (after conversion to byte as if by byte(ch)) in the null-terminated byte
   162  // string pointed to by str (each character interpreted as unsigned char). The terminating null character is considered
   163  // to be a part of the string and can be found when searching for '\x00'.
   164  //
   165  // The behavior is undefined if str is not a pointer to a null-terminated byte string.
   166  //
   167  // The return value is a pointer to the found character in str, or null pointer if no such character is found.
   168  func StrChr(str *byte, ch byte) *byte {
   169  	if str == nil {
   170  		return nil
   171  	}
   172  	b := GoBytes(str)
   173  	i := bytes.IndexByte(b, ch)
   174  	if i < 0 {
   175  		return nil
   176  	}
   177  	return &b[i]
   178  }
   179  
   180  // StrRChr finds the last occurrence of ch (after conversion to byte as if by byte(ch)) in the null-terminated byte
   181  // string pointed to by str (each character interpreted as unsigned char). The terminating null character is considered
   182  // to be a part of the string and can be found when searching for '\x00'.
   183  //
   184  // The behavior is undefined if str is not a pointer to a null-terminated byte string.
   185  //
   186  // The return value is a pointer to the found character in str, or null pointer if no such character is found.
   187  func StrRChr(str *byte, ch byte) *byte {
   188  	if str == nil {
   189  		return nil
   190  	}
   191  	b := GoBytes(str)
   192  	i := bytes.LastIndexByte(b, ch)
   193  	if i < 0 {
   194  		return nil
   195  	}
   196  	return &b[i]
   197  }
   198  
   199  // StrStr finds the first occurrence of the null-terminated byte string pointed to by substr in the null-terminated byte
   200  // string pointed to by str. The terminating null characters are not compared.
   201  //
   202  // The behavior is undefined if either str or substr is not a pointer to a null-terminated byte string.
   203  //
   204  // The return value is a pointer to the first character of the found substring in str, or NULL if such substring is not
   205  // found. If substr points to an empty string, str is returned.
   206  func StrStr(str, substr *byte) *byte {
   207  	if str == nil {
   208  		return nil
   209  	} else if substr == nil {
   210  		return str
   211  	}
   212  	sub := GoBytes(substr)
   213  	if len(sub) == 0 {
   214  		return str
   215  	}
   216  	b := GoBytes(str)
   217  	if len(b) == 0 {
   218  		return nil
   219  	}
   220  	i := bytes.Index(b, sub)
   221  	if i < 0 {
   222  		return nil
   223  	}
   224  	return &b[i]
   225  }
   226  
   227  func StrCmp(a, b *byte) int {
   228  	s1 := GoString(a)
   229  	s2 := GoString(b)
   230  	return strings.Compare(s1, s2)
   231  }
   232  
   233  func StrNCmp(a, b *byte, sz int) int {
   234  	s1 := GoString(a)
   235  	s2 := GoString(b)
   236  	if len(s1) > sz {
   237  		s1 = s1[:sz]
   238  	}
   239  	if len(s2) > sz {
   240  		s2 = s2[:sz]
   241  	}
   242  	return strings.Compare(s1, s2)
   243  }
   244  
   245  func StrCaseCmp(a, b *byte) int {
   246  	s1 := strings.ToLower(GoString(a))
   247  	s2 := strings.ToLower(GoString(b))
   248  	return strings.Compare(s1, s2)
   249  }
   250  
   251  func StrNCaseCmp(a, b *byte, sz int) int {
   252  	s1 := strings.ToLower(GoString(a))
   253  	s2 := strings.ToLower(GoString(b))
   254  	if len(s1) > sz {
   255  		s1 = s1[:sz]
   256  	}
   257  	if len(s2) > sz {
   258  		s2 = s2[:sz]
   259  	}
   260  	return strings.Compare(s1, s2)
   261  }
   262  
   263  // StrCpyGo copies a Go slice into a C string pointed by dst. It won't add the null terminator.
   264  func StrCpyGo(dst *byte, src []byte) {
   265  	d := unsafe.Slice(dst, len(src))
   266  	copy(d, src)
   267  }
   268  
   269  // StrCpyGoZero is the same as StrCpyGo, but adds a null terminator.
   270  func StrCpyGoZero(dst *byte, src []byte) {
   271  	d := unsafe.Slice(dst, len(src)+1)
   272  	n := copy(d, src)
   273  	d[n] = 0
   274  }
   275  
   276  // StrCpy copies the C string pointed by source into the array pointed by destination, including the terminating null character
   277  // (and stopping at that point).
   278  //
   279  // To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C string as source
   280  // (including the terminating null character), and should not overlap in memory with source.
   281  func StrCpy(dst, src *byte) *byte {
   282  	s := GoBytes(src)
   283  	StrCpyGoZero(dst, s)
   284  	return dst
   285  }
   286  
   287  // StrNCpy copies the first num characters of source to destination. If the end of the source C string
   288  // (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros
   289  // until a total of num characters have been written to it.
   290  //
   291  // No null-character is implicitly appended at the end of destination if source is longer than num.
   292  // Thus, in this case, destination shall not be considered a null terminated C string (reading it as such would overflow).
   293  //
   294  // Destination and source shall not overlap (see MemMove for a safer alternative when overlapping).
   295  func StrNCpy(dst, src *byte, sz int) *byte {
   296  	d := unsafe.Slice(dst, sz)
   297  	if len(d) == 0 {
   298  		return dst
   299  	}
   300  	s := GoBytes(src)
   301  	pad := 0
   302  	if len(s) > sz {
   303  		s = s[:sz]
   304  	} else if len(s) < sz {
   305  		pad = sz - len(s)
   306  	}
   307  	n := copy(d, s)
   308  	for i := 0; i < pad; i++ {
   309  		d[n+i] = 0
   310  	}
   311  	return &d[0]
   312  }
   313  
   314  // StrCat appends a copy of the source string to the destination string. The terminating null character in destination
   315  // is overwritten by the first character of source, and a null-character is included at the end of the new string
   316  // formed by the concatenation of both in destination.
   317  //
   318  // Destination and source shall not overlap.
   319  func StrCat(dst, src *byte) *byte {
   320  	s := GoBytes(src)
   321  	i := StrLen(dst)
   322  	n := i + len(s)
   323  	d := unsafe.Slice(dst, n+1)
   324  	copy(d[i:], s)
   325  	d[n] = 0
   326  	return &d[0]
   327  }
   328  
   329  // StrNCat appends the first num characters of source to destination, plus a terminating null-character.
   330  //
   331  // If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.
   332  func StrNCat(dst, src *byte, sz int) *byte {
   333  	s := GoBytes(src)
   334  	if len(s) > sz {
   335  		s = s[:sz]
   336  	}
   337  	n := StrLen(dst)
   338  	d := unsafe.Slice(dst, n+len(s)+1)[n:]
   339  	n = copy(d, s)
   340  	d[n] = 0
   341  	return dst
   342  }
   343  
   344  var strtok struct {
   345  	data []byte
   346  	ind  int
   347  }
   348  
   349  // StrTok is used in a sequence of calls to split str into tokens, which are sequences of contiguous characters
   350  // separated by any of the characters that are part of delimiters.
   351  //
   352  // On a first call, the function expects a C string as argument for str, whose first character is used as the starting
   353  // location to scan for tokens. In subsequent calls, the function expects a null pointer and uses the position
   354  // right after the end of the last token as the new starting location for scanning.
   355  //
   356  // To determine the beginning and the end of a token, the function first scans from the starting location for the first
   357  // character not contained in delimiters (which becomes the beginning of the token). And then scans starting from this
   358  // beginning of the token for the first character contained in delimiters, which becomes the end of the token.
   359  // The scan also stops if the terminating null character is found.
   360  //
   361  // This end of the token is automatically replaced by a null-character, and the beginning of the token is returned by the function.
   362  //
   363  // Once the terminating null character of str is found in a call to strtok, all subsequent calls to this function
   364  // (with a null pointer as the first argument) return a null pointer.
   365  //
   366  // The point where the last token was found is kept internally by the function to be used on the next call
   367  // (particular library implementations are not required to avoid data races).
   368  func StrTok(src, delim *byte) *byte {
   369  	if src != nil {
   370  		strtok.data = GoBytes(src)
   371  		strtok.ind = 0
   372  	}
   373  	d := GoString(delim)
   374  	for ; strtok.ind < len(strtok.data); strtok.ind++ {
   375  		if strings.IndexByte(d, strtok.data[strtok.ind]) < 0 {
   376  			// start of a new token
   377  			tok := strtok.data[strtok.ind:]
   378  			if i := bytes.IndexAny(tok, d); i >= 0 {
   379  				tok[i] = 0
   380  				strtok.ind += i + 1
   381  			} else {
   382  				strtok.data = nil
   383  				strtok.ind = 0
   384  			}
   385  			return &tok[0]
   386  		}
   387  		// skip delimiters
   388  	}
   389  	strtok.data = nil
   390  	strtok.ind = 0
   391  	return nil
   392  }
   393  
   394  // StrSpn returns the length of the initial portion of str1 which consists only of characters that are part of str2.
   395  //
   396  // The search does not include the terminating null-characters of either strings, but ends there.
   397  func StrSpn(str, chars *byte) int {
   398  	s := GoBytes(str)
   399  	c := GoBytes(chars)
   400  	i := 0
   401  	for ; i < len(s); i++ {
   402  		if bytes.IndexByte(c, s[i]) < 0 {
   403  			break
   404  		}
   405  	}
   406  	return i
   407  }
   408  
   409  func StrCSpn(a, b *byte) int {
   410  	panic("TODO")
   411  }
   412  
   413  // StrDup copies a null-terminated C string.
   414  func StrDup(s *byte) *byte {
   415  	return CString(GoString(s))
   416  }
   417  
   418  // StrNDup copies a null-terminated C string up to N bytes.
   419  // Null terminator is added if the string is larger than N.
   420  func StrNDup(s *byte, n int) *byte {
   421  	sz := findnull(s)
   422  	src := unsafe.Slice(s, sz)
   423  	if sz > n {
   424  		src = src[:n]
   425  	}
   426  	out := make([]byte, len(src)+1)
   427  	copy(out, src)
   428  	return &out[0]
   429  }