strings.ha (2949B)
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 // Converts a C string to a Hare string in O(n), and does not check if it's 22 // valid UTF-8. 23 export fn tostr_unsafe(cstr: *const char) const str = { 24 return tostrn_unsafe(cstr, strlen(cstr)); 25 }; 26 27 // Converts a C string with a given length to a Hare string, and does not check 28 // if it's valid UTF-8. 29 export fn tostrn_unsafe(cstr: *const char, length: size) const str = { 30 const s = types::string { 31 data = cstr: *[*]u8, 32 length = length, 33 capacity = length + 1, 34 }; 35 return *(&s: *const str); 36 }; 37 38 // Converts a C string to a Hare string in O(n). If the string is not valid 39 // UTF-8, return [[encoding::utf8::invalid]]. 40 export fn tostr(cstr: *const char) (const str | utf8::invalid) = { 41 return tostrn(cstr, strlen(cstr)); 42 }; 43 44 // Converts a C string with a given length to a Hare string. If the string is 45 // not valid UTF-8, return [[encoding::utf8::invalid]]. 46 export fn tostrn(cstr: *const char, length: size) (const str | utf8::invalid) = { 47 utf8::validate((cstr: *[*]u8)[..length])?; 48 return tostrn_unsafe(cstr, length); 49 }; 50 51 // Converts a Hare string to a C string. The result is allocated; the caller 52 // must free it when they're done. 53 export fn fromstr(s: const str) *char = { 54 let slice: []char = alloc([0...], len(s) + 1); 55 return fromstr_buf(s, slice); 56 }; 57 58 // Converts a Hare string to a C string. The result is stored into a 59 // user-supplied buffer. 60 export fn fromstr_buf(s: const str, sl: []char) *char = { 61 if (len(sl) < len(s) + 1) { 62 abort("types::c::fromstr_buf: buffer has insufficient space for string plus NUL"); 63 }; 64 65 const s = &s: *[]char; 66 sl[..len(s)] = s[..]; 67 sl[len(s)] = 0; 68 69 return (*(&sl: *types::slice)).data: *char; 70 }; 71 72 // Converts a NUL-terminated Hare string to a C string. Aborts if the input 73 // string isn't NUL-terminated. The result is borrowed from the input. 74 export fn nulstr(s: const str) *const char = { 75 let s = &s: *types::string; 76 let data = s.data as *[*]u8; 77 assert(data[s.length - 1] == '\0', "types::c::nulstr input must be NUL-terminated"); 78 return s.data: *const char; 79 }; 80 81 // Converts a non-NUL-terminated Hare string to a *const [[char]]. The return 82 // value is borrowed from the input, except in the case of an empty string, in 83 // which case it is statically allocated. 84 // 85 // Use with caution! 86 export fn unterminatedstr(s: const str) *const char = { 87 let s = &s: *types::string; 88 if (s.data == null) { 89 return empty_string; 90 }; 91 let data = s.data as *[*]u8; 92 return data: *const char; 93 };