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 {...@@ -57,11 +57,12 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 {
57 return length;57 return length;
58}58}
5959
60const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error;
60/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.61/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
61/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.62/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
62/// If you already know the length at comptime, you can call one of63/// If you already know the length at comptime, you can call one of
63/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.64/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
64pub fn utf8Decode(bytes: []const u8) !u32 {65pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 {
65 return switch (bytes.len) {66 return switch (bytes.len) {
66 1 => u32(bytes[0]),67 1 => u32(bytes[0]),
67 2 => utf8Decode2(bytes),68 2 => utf8Decode2(bytes),
...@@ -71,7 +72,11 @@ pub fn utf8Decode(bytes: []const u8) !u32 {...@@ -71,7 +72,11 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
71 };72 };
72}73}
7374
74pub fn utf8Decode2(bytes: []const u8) !u32 {75const Utf8Decode2Error = error{
76 Utf8ExpectedContinuation,
77 Utf8OverlongEncoding,
78};
79pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 {
75 debug.assert(bytes.len == 2);80 debug.assert(bytes.len == 2);
76 debug.assert(bytes[0] & 0b11100000 == 0b11000000);81 debug.assert(bytes[0] & 0b11100000 == 0b11000000);
77 var value: u32 = bytes[0] & 0b00011111;82 var value: u32 = bytes[0] & 0b00011111;
...@@ -85,7 +90,12 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {...@@ -85,7 +90,12 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
85 return value;90 return value;
86}91}
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 {
89 debug.assert(bytes.len == 3);99 debug.assert(bytes.len == 3);
90 debug.assert(bytes[0] & 0b11110000 == 0b11100000);100 debug.assert(bytes[0] & 0b11110000 == 0b11100000);
91 var value: u32 = bytes[0] & 0b00001111;101 var value: u32 = bytes[0] & 0b00001111;
...@@ -104,7 +114,12 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {...@@ -104,7 +114,12 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
104 return value;114 return value;
105}115}
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 {
108 debug.assert(bytes.len == 4);123 debug.assert(bytes.len == 4);
109 debug.assert(bytes[0] & 0b11111000 == 0b11110000);124 debug.assert(bytes[0] & 0b11111000 == 0b11110000);
110 var value: u32 = bytes[0] & 0b00000111;125 var value: u32 = bytes[0] & 0b00000111;
...@@ -206,19 +221,21 @@ const Utf8Iterator = struct {...@@ -206,19 +221,21 @@ const Utf8Iterator = struct {
206 pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {221 pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {
207 const slice = it.nextCodepointSlice() ?? return null;222 const slice = it.nextCodepointSlice() ?? return null;
208223
209 const r = switch (slice.len) {224 switch (slice.len) {
210 1 => u32(slice[0]),225 1 => return u32(slice[0]),
211 2 => utf8Decode2(slice),226 2 => return utf8Decode2(slice) catch unreachable,
212 3 => utf8Decode3(slice),227 3 => return utf8Decode3(slice) catch unreachable,
213 4 => utf8Decode4(slice),228 4 => return utf8Decode4(slice) catch unreachable,
214 else => unreachable,229 else => unreachable,
215 };230 }
216
217 return r catch unreachable;
218 }231 }
219};232};
220233
221test "utf8 encode" {234test "utf8 encode" {
235 comptime testUtf8Encode() catch unreachable;
236 try testUtf8Encode();
237}
238fn testUtf8Encode() !void {
222 // A few taken from wikipedia a few taken elsewhere239 // A few taken from wikipedia a few taken elsewhere
223 var array: [4]u8 = undefined;240 var array: [4]u8 = undefined;
224 debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3);241 debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3);
...@@ -241,6 +258,10 @@ test "utf8 encode" {...@@ -241,6 +258,10 @@ test "utf8 encode" {
241}258}
242259
243test "utf8 encode error" {260test "utf8 encode error" {
261 comptime testUtf8EncodeError();
262 testUtf8EncodeError();
263}
264fn testUtf8EncodeError() void {
244 var array: [4]u8 = undefined;265 var array: [4]u8 = undefined;
245 testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf);266 testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf);
246 testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf);267 testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf);
...@@ -257,6 +278,10 @@ fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {...@@ -257,6 +278,10 @@ fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
257}278}
258279
259test "utf8 iterator on ascii" {280test "utf8 iterator on ascii" {
281 comptime testUtf8IteratorOnAscii();
282 testUtf8IteratorOnAscii();
283}
284fn testUtf8IteratorOnAscii() void {
260 const s = Utf8View.initComptime("abc");285 const s = Utf8View.initComptime("abc");
261286
262 var it1 = s.iterator();287 var it1 = s.iterator();
...@@ -273,6 +298,10 @@ test "utf8 iterator on ascii" {...@@ -273,6 +298,10 @@ test "utf8 iterator on ascii" {
273}298}
274299
275test "utf8 view bad" {300test "utf8 view bad" {
301 comptime testUtf8ViewBad();
302 testUtf8ViewBad();
303}
304fn testUtf8ViewBad() void {
276 // Compile-time error.305 // Compile-time error.
277 // const s3 = Utf8View.initComptime("\xfe\xf2");306 // const s3 = Utf8View.initComptime("\xfe\xf2");
278307
...@@ -281,6 +310,10 @@ test "utf8 view bad" {...@@ -281,6 +310,10 @@ test "utf8 view bad" {
281}310}
282311
283test "utf8 view ok" {312test "utf8 view ok" {
313 comptime testUtf8ViewOk();
314 testUtf8ViewOk();
315}
316fn testUtf8ViewOk() void {
284 const s = Utf8View.initComptime("東京市");317 const s = Utf8View.initComptime("東京市");
285318
286 var it1 = s.iterator();319 var it1 = s.iterator();
...@@ -297,6 +330,10 @@ test "utf8 view ok" {...@@ -297,6 +330,10 @@ test "utf8 view ok" {
297}330}
298331
299test "bad utf8 slice" {332test "bad utf8 slice" {
333 comptime testBadUtf8Slice();
334 testBadUtf8Slice();
335}
336fn testBadUtf8Slice() void {
300 debug.assert(utf8ValidateSlice("abc"));337 debug.assert(utf8ValidateSlice("abc"));
301 debug.assert(!utf8ValidateSlice("abc\xc0"));338 debug.assert(!utf8ValidateSlice("abc\xc0"));
302 debug.assert(!utf8ValidateSlice("abc\xc0abc"));339 debug.assert(!utf8ValidateSlice("abc\xc0abc"));
...@@ -304,6 +341,10 @@ test "bad utf8 slice" {...@@ -304,6 +341,10 @@ test "bad utf8 slice" {
304}341}
305342
306test "valid utf8" {343test "valid utf8" {
344 comptime testValidUtf8();
345 testValidUtf8();
346}
347fn testValidUtf8() void {
307 testValid("\x00", 0x0);348 testValid("\x00", 0x0);
308 testValid("\x20", 0x20);349 testValid("\x20", 0x20);
309 testValid("\x7f", 0x7f);350 testValid("\x7f", 0x7f);
...@@ -319,6 +360,10 @@ test "valid utf8" {...@@ -319,6 +360,10 @@ test "valid utf8" {
319}360}
320361
321test "invalid utf8 continuation bytes" {362test "invalid utf8 continuation bytes" {
363 comptime testInvalidUtf8ContinuationBytes();
364 testInvalidUtf8ContinuationBytes();
365}
366fn testInvalidUtf8ContinuationBytes() void {
322 // unexpected continuation367 // unexpected continuation
323 testError("\x80", error.Utf8InvalidStartByte);368 testError("\x80", error.Utf8InvalidStartByte);
324 testError("\xbf", error.Utf8InvalidStartByte);369 testError("\xbf", error.Utf8InvalidStartByte);
...@@ -347,6 +392,10 @@ test "invalid utf8 continuation bytes" {...@@ -347,6 +392,10 @@ test "invalid utf8 continuation bytes" {
347}392}
348393
349test "overlong utf8 codepoint" {394test "overlong utf8 codepoint" {
395 comptime testOverlongUtf8Codepoint();
396 testOverlongUtf8Codepoint();
397}
398fn testOverlongUtf8Codepoint() void {
350 testError("\xc0\x80", error.Utf8OverlongEncoding);399 testError("\xc0\x80", error.Utf8OverlongEncoding);
351 testError("\xc1\xbf", error.Utf8OverlongEncoding);400 testError("\xc1\xbf", error.Utf8OverlongEncoding);
352 testError("\xe0\x80\x80", error.Utf8OverlongEncoding);401 testError("\xe0\x80\x80", error.Utf8OverlongEncoding);
...@@ -356,6 +405,10 @@ test "overlong utf8 codepoint" {...@@ -356,6 +405,10 @@ test "overlong utf8 codepoint" {
356}405}
357406
358test "misc invalid utf8" {407test "misc invalid utf8" {
408 comptime testMiscInvalidUtf8();
409 testMiscInvalidUtf8();
410}
411fn testMiscInvalidUtf8() void {
359 // codepoint out of bounds412 // codepoint out of bounds
360 testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);413 testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);
361 testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);414 testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);