authorgravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2018-04-29 17:38:41-04:00
committergravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2018-04-29 18:07:18-04:00
log9543c0a7cc0bbdccc405370e224b546c56b76a0f
tree52eaed9efd6fb2b309bbe10a4a7284d0709a6492
parentc03b9010db55e52dfa227b17e35203b93b5ee1df

use explicit error sets for utf8Decode functions

and run unicode tests at comptime also

1 files changed, 65 insertions(+), 12 deletions(-)

std/unicode.zig+65-12
......@@ -57,11 +57,12 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 {
5757 return length;
5858}
5959
60const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error;
6061/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
6162/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
6263/// If you already know the length at comptime, you can call one of
6364/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
64pub fn utf8Decode(bytes: []const u8) !u32 {
65pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 {
6566 return switch (bytes.len) {
6667 1 => u32(bytes[0]),
6768 2 => utf8Decode2(bytes),
......@@ -71,7 +72,11 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
7172 };
7273}
7374
74pub fn utf8Decode2(bytes: []const u8) !u32 {
75const Utf8Decode2Error = error{
76 Utf8ExpectedContinuation,
77 Utf8OverlongEncoding,
78};
79pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 {
7580 debug.assert(bytes.len == 2);
7681 debug.assert(bytes[0] & 0b11100000 == 0b11000000);
7782 var value: u32 = bytes[0] & 0b00011111;
......@@ -85,7 +90,12 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
8590 return value;
8691}
8792
88pub fn utf8Decode3(bytes: []const u8) !u32 {
93const Utf8Decode3Error = error{
94 Utf8ExpectedContinuation,
95 Utf8OverlongEncoding,
96 Utf8EncodesSurrogateHalf,
97};
98pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 {
8999 debug.assert(bytes.len == 3);
90100 debug.assert(bytes[0] & 0b11110000 == 0b11100000);
91101 var value: u32 = bytes[0] & 0b00001111;
......@@ -104,7 +114,12 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
104114 return value;
105115}
106116
107pub fn utf8Decode4(bytes: []const u8) !u32 {
117const Utf8Decode4Error = error{
118 Utf8ExpectedContinuation,
119 Utf8OverlongEncoding,
120 Utf8CodepointTooLarge,
121};
122pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 {
108123 debug.assert(bytes.len == 4);
109124 debug.assert(bytes[0] & 0b11111000 == 0b11110000);
110125 var value: u32 = bytes[0] & 0b00000111;
......@@ -206,19 +221,21 @@ const Utf8Iterator = struct {
206221 pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {
207222 const slice = it.nextCodepointSlice() ?? return null;
208223
209 const r = switch (slice.len) {
210 1 => u32(slice[0]),
211 2 => utf8Decode2(slice),
212 3 => utf8Decode3(slice),
213 4 => utf8Decode4(slice),
224 switch (slice.len) {
225 1 => return u32(slice[0]),
226 2 => return utf8Decode2(slice) catch unreachable,
227 3 => return utf8Decode3(slice) catch unreachable,
228 4 => return utf8Decode4(slice) catch unreachable,
214229 else => unreachable,
215 };
216
217 return r catch unreachable;
230 }
218231 }
219232};
220233
221234test "utf8 encode" {
235 comptime testUtf8Encode() catch unreachable;
236 try testUtf8Encode();
237}
238fn testUtf8Encode() !void {
222239 // A few taken from wikipedia a few taken elsewhere
223240 var array: [4]u8 = undefined;
224241 debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3);
......@@ -241,6 +258,10 @@ test "utf8 encode" {
241258}
242259
243260test "utf8 encode error" {
261 comptime testUtf8EncodeError();
262 testUtf8EncodeError();
263}
264fn testUtf8EncodeError() void {
244265 var array: [4]u8 = undefined;
245266 testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf);
246267 testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf);
......@@ -257,6 +278,10 @@ fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
257278}
258279
259280test "utf8 iterator on ascii" {
281 comptime testUtf8IteratorOnAscii();
282 testUtf8IteratorOnAscii();
283}
284fn testUtf8IteratorOnAscii() void {
260285 const s = Utf8View.initComptime("abc");
261286
262287 var it1 = s.iterator();
......@@ -273,6 +298,10 @@ test "utf8 iterator on ascii" {
273298}
274299
275300test "utf8 view bad" {
301 comptime testUtf8ViewBad();
302 testUtf8ViewBad();
303}
304fn testUtf8ViewBad() void {
276305 // Compile-time error.
277306 // const s3 = Utf8View.initComptime("\xfe\xf2");
278307
......@@ -281,6 +310,10 @@ test "utf8 view bad" {
281310}
282311
283312test "utf8 view ok" {
313 comptime testUtf8ViewOk();
314 testUtf8ViewOk();
315}
316fn testUtf8ViewOk() void {
284317 const s = Utf8View.initComptime("東京市");
285318
286319 var it1 = s.iterator();
......@@ -297,6 +330,10 @@ test "utf8 view ok" {
297330}
298331
299332test "bad utf8 slice" {
333 comptime testBadUtf8Slice();
334 testBadUtf8Slice();
335}
336fn testBadUtf8Slice() void {
300337 debug.assert(utf8ValidateSlice("abc"));
301338 debug.assert(!utf8ValidateSlice("abc\xc0"));
302339 debug.assert(!utf8ValidateSlice("abc\xc0abc"));
......@@ -304,6 +341,10 @@ test "bad utf8 slice" {
304341}
305342
306343test "valid utf8" {
344 comptime testValidUtf8();
345 testValidUtf8();
346}
347fn testValidUtf8() void {
307348 testValid("\x00", 0x0);
308349 testValid("\x20", 0x20);
309350 testValid("\x7f", 0x7f);
......@@ -319,6 +360,10 @@ test "valid utf8" {
319360}
320361
321362test "invalid utf8 continuation bytes" {
363 comptime testInvalidUtf8ContinuationBytes();
364 testInvalidUtf8ContinuationBytes();
365}
366fn testInvalidUtf8ContinuationBytes() void {
322367 // unexpected continuation
323368 testError("\x80", error.Utf8InvalidStartByte);
324369 testError("\xbf", error.Utf8InvalidStartByte);
......@@ -347,6 +392,10 @@ test "invalid utf8 continuation bytes" {
347392}
348393
349394test "overlong utf8 codepoint" {
395 comptime testOverlongUtf8Codepoint();
396 testOverlongUtf8Codepoint();
397}
398fn testOverlongUtf8Codepoint() void {
350399 testError("\xc0\x80", error.Utf8OverlongEncoding);
351400 testError("\xc1\xbf", error.Utf8OverlongEncoding);
352401 testError("\xe0\x80\x80", error.Utf8OverlongEncoding);
......@@ -356,6 +405,10 @@ test "overlong utf8 codepoint" {
356405}
357406
358407test "misc invalid utf8" {
408 comptime testMiscInvalidUtf8();
409 testMiscInvalidUtf8();
410}
411fn testMiscInvalidUtf8() void {
359412 // codepoint out of bounds
360413 testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);
361414 testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);