| ... | ... | @@ -1,12 +1,11 @@ |
| 1 | 1 | const std = @import("./index.zig"); |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | |
| 4 | | // Given a Utf8-Codepoint returns how many (1-4) |
| 5 | | // bytes there are if represented as an array of bytes. |
| 4 | /// Returns how many bytes the UTF-8 representation would require |
| 5 | /// for the given codepoint. |
| 6 | 6 | pub fn utf8CodepointSequenceLength(c: u32) !u3 { |
| 7 | 7 | if (c < 0x80) return u3(1); |
| 8 | 8 | if (c < 0x800) return u3(2); |
| 9 | | if (c -% 0xd800 < 0x800) return error.InvalidCodepoint; |
| 10 | 9 | if (c < 0x10000) return u3(3); |
| 11 | 10 | if (c < 0x110000) return u3(4); |
| 12 | 11 | return error.CodepointTooLarge; |
| ... | ... | @@ -23,45 +22,39 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 { |
| 23 | 22 | return error.Utf8InvalidStartByte; |
| 24 | 23 | } |
| 25 | 24 | |
| 26 | | /// Encodes a code point back into utf8 |
| 27 | | /// c: the code point |
| 28 | | /// out: the out buffer to write to |
| 29 | | /// Notes: out has to have a len big enough for the bytes |
| 30 | | /// however this limit is dependent on the code point |
| 31 | | /// but giving it a minimum of 4 will ensure it will work |
| 32 | | /// for all code points. |
| 33 | | /// Errors: Will return an error if the code point is invalid. |
| 25 | /// Encodes the given codepoint into a UTF-8 byte sequence. |
| 26 | /// c: the codepoint. |
| 27 | /// out: the out buffer to write to. Must have a len >= utf8CodepointSequenceLength(c). |
| 28 | /// Errors: if c cannot be encoded in UTF-8. |
| 29 | /// Returns: the number of bytes written to out. |
| 34 | 30 | pub fn utf8Encode(c: u32, out: []u8) !u3 { |
| 35 | | if (utf8CodepointSequenceLength(c)) |length| { |
| 36 | | debug.assert(out.len >= length); |
| 37 | | switch (length) { |
| 38 | | // The pattern for each is the same |
| 39 | | // - Increasing the initial shift by 6 each time |
| 40 | | // - Each time after the first shorten the shifted |
| 41 | | // value to a max of 0b111111 (63) |
| 42 | | 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range |
| 43 | | 2 => { |
| 44 | | out[0] = u8(0b11000000 | (c >> 6)); |
| 45 | | out[1] = u8(0b10000000 | (c & 0b111111)); |
| 46 | | }, |
| 47 | | 3 => { |
| 48 | | out[0] = u8(0b11100000 | (c >> 12)); |
| 49 | | out[1] = u8(0b10000000 | ((c >> 6) & 0b111111)); |
| 50 | | out[2] = u8(0b10000000 | (c & 0b111111)); |
| 51 | | }, |
| 52 | | 4 => { |
| 53 | | out[0] = u8(0b11110000 | (c >> 18)); |
| 54 | | out[1] = u8(0b10000000 | ((c >> 12) & 0b111111)); |
| 55 | | out[2] = u8(0b10000000 | ((c >> 6) & 0b111111)); |
| 56 | | out[3] = u8(0b10000000 | (c & 0b111111)); |
| 57 | | }, |
| 58 | | else => unreachable, |
| 59 | | } |
| 60 | | |
| 61 | | return length; |
| 62 | | } else |err| { |
| 63 | | return err; |
| 31 | const length = try utf8CodepointSequenceLength(c); |
| 32 | debug.assert(out.len >= length); |
| 33 | switch (length) { |
| 34 | // The pattern for each is the same |
| 35 | // - Increasing the initial shift by 6 each time |
| 36 | // - Each time after the first shorten the shifted |
| 37 | // value to a max of 0b111111 (63) |
| 38 | 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range |
| 39 | 2 => { |
| 40 | out[0] = u8(0b11000000 | (c >> 6)); |
| 41 | out[1] = u8(0b10000000 | (c & 0b111111)); |
| 42 | }, |
| 43 | 3 => { |
| 44 | if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf; |
| 45 | out[0] = u8(0b11100000 | (c >> 12)); |
| 46 | out[1] = u8(0b10000000 | ((c >> 6) & 0b111111)); |
| 47 | out[2] = u8(0b10000000 | (c & 0b111111)); |
| 48 | }, |
| 49 | 4 => { |
| 50 | out[0] = u8(0b11110000 | (c >> 18)); |
| 51 | out[1] = u8(0b10000000 | ((c >> 12) & 0b111111)); |
| 52 | out[2] = u8(0b10000000 | ((c >> 6) & 0b111111)); |
| 53 | out[3] = u8(0b10000000 | (c & 0b111111)); |
| 54 | }, |
| 55 | else => unreachable, |
| 64 | 56 | } |
| 57 | return length; |
| 65 | 58 | } |
| 66 | 59 | |
| 67 | 60 | /// Decodes the UTF-8 codepoint encoded in the given slice of bytes. |
| ... | ... | @@ -249,8 +242,10 @@ test "utf8 encode" { |
| 249 | 242 | |
| 250 | 243 | test "utf8 encode error" { |
| 251 | 244 | var array: [4]u8 = undefined; |
| 252 | | testErrorEncode(0xFFFFFF, array[0..], error.CodepointTooLarge); |
| 253 | | testErrorEncode(0xd900, array[0..], error.InvalidCodepoint); |
| 245 | testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf); |
| 246 | testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf); |
| 247 | testErrorEncode(0x110000, array[0..], error.CodepointTooLarge); |
| 248 | testErrorEncode(0xffffffff, array[0..], error.CodepointTooLarge); |
| 254 | 249 | } |
| 255 | 250 | |
| 256 | 251 | fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void { |