| ... | ... | @@ -153,6 +153,23 @@ pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u21 { |
| 153 | 153 | return value; |
| 154 | 154 | } |
| 155 | 155 | |
| 156 | /// Returns the length of a supplied UTF-8 string literal in terms of unicode |
| 157 | /// codepoints. |
| 158 | /// Asserts that the data is valid UTF-8. |
| 159 | pub fn utf8CountCodepoints(s: []const u8) !usize { |
| 160 | var len: usize = 0; |
| 161 | |
| 162 | var i: usize = 0; |
| 163 | while (i < s.len) : (len += 1) { |
| 164 | const n = try utf8ByteSequenceLength(s[i]); |
| 165 | if (i + n > s.len) return error.TruncatedInput; |
| 166 | _ = try utf8Decode(s[i .. i + n]); |
| 167 | i += n; |
| 168 | } |
| 169 | |
| 170 | return len; |
| 171 | } |
| 172 | |
| 156 | 173 | pub fn utf8ValidateSlice(s: []const u8) bool { |
| 157 | 174 | var i: usize = 0; |
| 158 | 175 | while (i < s.len) { |
| ... | ... | @@ -687,7 +704,6 @@ pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16Le |
| 687 | 704 | } |
| 688 | 705 | } |
| 689 | 706 | |
| 690 | | /// Returns length of a supplied UTF-8 string literal. Asserts that the data is valid UTF-8. |
| 691 | 707 | fn calcUtf16LeLen(utf8: []const u8) usize { |
| 692 | 708 | var src_i: usize = 0; |
| 693 | 709 | var dest_len: usize = 0; |
| ... | ... | @@ -757,3 +773,15 @@ test "utf8ToUtf16LeStringLiteral" { |
| 757 | 773 | testing.expect(utf16[2] == 0); |
| 758 | 774 | } |
| 759 | 775 | } |
| 776 | |
| 777 | fn testUtf8CountCodepoints() !void { |
| 778 | testing.expectEqual(@as(usize, 10), try utf8CountCodepoints("abcdefghij")); |
| 779 | testing.expectEqual(@as(usize, 10), try utf8CountCodepoints("äåéëþüúíóö")); |
| 780 | testing.expectEqual(@as(usize, 5), try utf8CountCodepoints("こんにちは")); |
| 781 | testing.expectError(error.Utf8EncodesSurrogateHalf, utf8CountCodepoints("\xED\xA0\x80")); |
| 782 | } |
| 783 | |
| 784 | test "utf8 count codepoints" { |
| 785 | try testUtf8CountCodepoints(); |
| 786 | comptime testUtf8CountCodepoints() catch unreachable; |
| 787 | } |