strings.ha (3270B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use encoding::utf8; 5 use types; 6 7 let empty: [_]u8 = [0]; 8 9 // An empty NUL-terminated C string. 10 export let empty_string: *const char = &empty[0]: *const char; 11 12 // Computes the length of a NUL-terminated C string, in octets, in O(n). The 13 // computed length does not include the NUL terminator. 14 export fn strlen(cstr: *const char) size = { 15 const ptr = cstr: *[*]u8; 16 let ln = 0z; 17 for (ptr[ln] != 0; ln += 1) void; 18 return ln; 19 }; 20 21 // Computes the length of a NUL-terminated C string, only looking at the first 22 // maxlen bytes. The computed length does not include the NUL terminator. 23 export fn strnlen(cstr: *const char, maxlen: size) size = { 24 const ptr = cstr: *[*]u8; 25 let ln = 0z; 26 for (ln < maxlen && ptr[ln] != 0; ln += 1) void; 27 return ln; 28 }; 29 30 // Converts a C string to a Hare string in O(n), and does not check if it's 31 // valid UTF-8. 32 export fn tostr_unsafe(cstr: *const char) const str = { 33 return tostrn_unsafe(cstr, strlen(cstr)); 34 }; 35 36 // Converts a C string with a given length to a Hare string, and does not check 37 // if it's valid UTF-8. 38 export fn tostrn_unsafe(cstr: *const char, length: size) const str = { 39 const s = types::string { 40 data = cstr: *[*]u8, 41 length = length, 42 capacity = length + 1, 43 }; 44 return *(&s: *const str); 45 }; 46 47 // Converts a C string to a Hare string in O(n). If the string is not valid 48 // UTF-8, return [[encoding::utf8::invalid]]. 49 export fn tostr(cstr: *const char) (const str | utf8::invalid) = { 50 return tostrn(cstr, strlen(cstr)); 51 }; 52 53 // Converts a C string with a given length to a Hare string. If the string is 54 // not valid UTF-8, return [[encoding::utf8::invalid]]. 55 export fn tostrn(cstr: *const char, length: size) (const str | utf8::invalid) = { 56 utf8::validate((cstr: *[*]u8)[..length])?; 57 return tostrn_unsafe(cstr, length); 58 }; 59 60 // Converts a Hare string to a C string. The result is allocated; the caller 61 // must free it when they're done. 62 export fn fromstr(s: const str) *char = { 63 let slice: []char = alloc([0...], len(s) + 1)!; 64 return fromstr_buf(s, slice); 65 }; 66 67 // Converts a Hare string to a C string. The result is stored into a 68 // user-supplied buffer. 69 export fn fromstr_buf(s: const str, sl: []char) *char = { 70 if (len(sl) < len(s) + 1) { 71 abort("types::c::fromstr_buf: buffer has insufficient space for string plus NUL"); 72 }; 73 74 const s = &s: *[]char; 75 sl[..len(s)] = s[..]; 76 sl[len(s)] = 0; 77 78 return (*(&sl: *types::slice)).data: *char; 79 }; 80 81 // Converts a NUL-terminated Hare string to a C string. Aborts if the input 82 // string isn't NUL-terminated. The result is borrowed from the input. 83 export fn nulstr(s: const str) *const char = { 84 let s = &s: *types::string; 85 let data = s.data as *[*]u8; 86 assert(data[s.length - 1] == '\0', "types::c::nulstr input must be NUL-terminated"); 87 return s.data: *const char; 88 }; 89 90 // Converts a non-NUL-terminated Hare string to a *const [[char]]. The return 91 // value is borrowed from the input, except in the case of an empty string, in 92 // which case it is statically allocated. 93 // 94 // Use with caution! 95 export fn unterminatedstr(s: const str) *const char = { 96 let s = &s: *types::string; 97 if (s.data == null) { 98 return empty_string; 99 }; 100 let data = s.data as *[*]u8; 101 return data: *const char; 102 };