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 }