diff options
Diffstat (limited to 'src/Emoji.zig')
| -rw-r--r-- | src/Emoji.zig | 130 |
1 files changed, 44 insertions, 86 deletions
diff --git a/src/Emoji.zig b/src/Emoji.zig index 75b44c2..13f675b 100644 --- a/src/Emoji.zig +++ b/src/Emoji.zig | |||
| @@ -1,17 +1,17 @@ | |||
| 1 | const std = @import("std"); | 1 | //! Emoji module |
| 2 | const builtin = @import("builtin"); | ||
| 3 | const mem = std.mem; | ||
| 4 | const Allocator = mem.Allocator; | ||
| 5 | const compress = std.compress; | ||
| 6 | const unicode = std.unicode; | ||
| 7 | |||
| 8 | const CodePoint = @import("code_point").CodePoint; | ||
| 9 | const CodePointIterator = @import("code_point").Iterator; | ||
| 10 | 2 | ||
| 11 | s1: []u16 = undefined, | 3 | const Data = struct { |
| 12 | s2: []u6 = undefined, | 4 | s1: []const u16 = undefined, |
| 5 | s2: []const u6 = undefined, | ||
| 6 | }; | ||
| 13 | 7 | ||
| 14 | const Emoji = @This(); | 8 | const emoji = emoji: { |
| 9 | const data = @import("emoji"); | ||
| 10 | break :emoji Data{ | ||
| 11 | .s1 = &data.s1, | ||
| 12 | .s2 = &data.s2, | ||
| 13 | }; | ||
| 14 | }; | ||
| 15 | 15 | ||
| 16 | // This must be an exact match of `Emoji` from `codegen/emoji.zig`. | 16 | // This must be an exact match of `Emoji` from `codegen/emoji.zig`. |
| 17 | pub const Properties = packed struct { | 17 | pub const Properties = packed struct { |
| @@ -23,110 +23,68 @@ pub const Properties = packed struct { | |||
| 23 | Extended_Pictographic: bool = false, | 23 | Extended_Pictographic: bool = false, |
| 24 | }; | 24 | }; |
| 25 | 25 | ||
| 26 | pub fn init(allocator: Allocator) Allocator.Error!Emoji { | ||
| 27 | var emoji = Emoji{}; | ||
| 28 | try emoji.setup(allocator); | ||
| 29 | return emoji; | ||
| 30 | } | ||
| 31 | |||
| 32 | pub fn setup(emoji: *Emoji, allocator: Allocator) Allocator.Error!void { | ||
| 33 | const decompressor = compress.flate.inflate.decompressor; | ||
| 34 | const in_bytes = @embedFile("emoji"); | ||
| 35 | var in_fbs = std.io.fixedBufferStream(in_bytes); | ||
| 36 | var in_decomp = decompressor(.raw, in_fbs.reader()); | ||
| 37 | var reader = in_decomp.reader(); | ||
| 38 | |||
| 39 | const endian = builtin.cpu.arch.endian(); | ||
| 40 | |||
| 41 | const s1_len: u16 = reader.readInt(u16, endian) catch unreachable; | ||
| 42 | emoji.s1 = try allocator.alloc(u16, s1_len); | ||
| 43 | errdefer allocator.free(emoji.s1); | ||
| 44 | for (0..s1_len) |i| emoji.s1[i] = reader.readInt(u16, endian) catch unreachable; | ||
| 45 | |||
| 46 | const s2_len: u16 = reader.readInt(u16, endian) catch unreachable; | ||
| 47 | emoji.s2 = try allocator.alloc(u6, s2_len); | ||
| 48 | errdefer allocator.free(emoji.s2); | ||
| 49 | for (0..s2_len) |i| emoji.s2[i] = @intCast(reader.readInt(u8, endian) catch unreachable); | ||
| 50 | } | ||
| 51 | |||
| 52 | pub fn deinit(emoji: *const Emoji, allocator: Allocator) void { | ||
| 53 | allocator.free(emoji.s1); | ||
| 54 | allocator.free(emoji.s2); | ||
| 55 | } | ||
| 56 | |||
| 57 | /// Lookup the emoji properties for a code point. | 26 | /// Lookup the emoji properties for a code point. |
| 58 | fn properties(emoji: Emoji, cp: u21) Properties { | 27 | fn properties(cp: u21) Properties { |
| 59 | return @bitCast(emoji.s2[emoji.s1[cp >> 8] + (cp & 0xff)]); | 28 | return @bitCast(emoji.s2[emoji.s1[cp >> 8] + (cp & 0xff)]); |
| 60 | } | 29 | } |
| 61 | 30 | ||
| 62 | pub fn isEmoji(emoji: Emoji, cp: u21) bool { | 31 | pub fn isEmoji(cp: u21) bool { |
| 63 | return properties(emoji, cp).Emoji; | 32 | return properties(cp).Emoji; |
| 64 | } | 33 | } |
| 65 | 34 | ||
| 66 | pub fn isEmojiPresentation(emoji: Emoji, cp: u21) bool { | 35 | pub fn isEmojiPresentation(cp: u21) bool { |
| 67 | return properties(emoji, cp).Emoji_Presentation; | 36 | return properties(cp).Emoji_Presentation; |
| 68 | } | 37 | } |
| 69 | 38 | ||
| 70 | pub fn isEmojiModifier(emoji: Emoji, cp: u21) bool { | 39 | pub fn isEmojiModifier(cp: u21) bool { |
| 71 | return properties(emoji, cp).Emoji_Modifier; | 40 | return properties(cp).Emoji_Modifier; |
| 72 | } | 41 | } |
| 73 | 42 | ||
| 74 | pub fn isEmojiModifierBase(emoji: Emoji, cp: u21) bool { | 43 | pub fn isEmojiModifierBase(cp: u21) bool { |
| 75 | return properties(emoji, cp).Emoji_Modifier_Base; | 44 | return properties(cp).Emoji_Modifier_Base; |
| 76 | } | 45 | } |
| 77 | 46 | ||
| 78 | pub fn isEmojiComponent(emoji: Emoji, cp: u21) bool { | 47 | pub fn isEmojiComponent(cp: u21) bool { |
| 79 | return properties(emoji, cp).Emoji_Component; | 48 | return properties(cp).Emoji_Component; |
| 80 | } | 49 | } |
| 81 | 50 | ||
| 82 | pub fn isExtendedPictographic(emoji: Emoji, cp: u21) bool { | 51 | pub fn isExtendedPictographic(cp: u21) bool { |
| 83 | return properties(emoji, cp).Extended_Pictographic; | 52 | return properties(cp).Extended_Pictographic; |
| 84 | } | 53 | } |
| 85 | 54 | ||
| 86 | test "isEmoji" { | 55 | test "isEmoji" { |
| 87 | const emoji = try Emoji.init(std.testing.allocator); | 56 | try std.testing.expect(isEmoji(0x1F415)); // 🐕 |
| 88 | defer emoji.deinit(std.testing.allocator); | 57 | try std.testing.expect(!isEmoji(0x3042)); // あ |
| 89 | |||
| 90 | try std.testing.expect(emoji.isEmoji(0x1F415)); // 🐕 | ||
| 91 | try std.testing.expect(!emoji.isEmoji(0x3042)); // あ | ||
| 92 | } | 58 | } |
| 93 | 59 | ||
| 94 | test "isEmojiPresentation" { | 60 | test "isEmojiPresentation" { |
| 95 | const emoji = try Emoji.init(std.testing.allocator); | 61 | try std.testing.expect(isEmojiPresentation(0x1F408)); // 🐈 |
| 96 | defer emoji.deinit(std.testing.allocator); | 62 | try std.testing.expect(!isEmojiPresentation(0x267E)); // ♾ |
| 97 | |||
| 98 | try std.testing.expect(emoji.isEmojiPresentation(0x1F408)); // 🐈 | ||
| 99 | try std.testing.expect(!emoji.isEmojiPresentation(0x267E)); // ♾ | ||
| 100 | } | 63 | } |
| 101 | 64 | ||
| 102 | test "isEmojiModifier" { | 65 | test "isEmojiModifier" { |
| 103 | const emoji = try Emoji.init(std.testing.allocator); | 66 | try std.testing.expect(isEmojiModifier(0x1F3FF)); // |
| 104 | defer emoji.deinit(std.testing.allocator); | 67 | try std.testing.expect(!isEmojiModifier(0x1F385)); // 🎅 |
| 105 | |||
| 106 | try std.testing.expect(emoji.isEmojiModifier(0x1F3FF)); // 🏿 | ||
| 107 | try std.testing.expect(!emoji.isEmojiModifier(0x1F385)); // 🎅 | ||
| 108 | } | 68 | } |
| 109 | 69 | ||
| 110 | test "isEmojiModifierBase" { | 70 | test "isEmojiModifierBase" { |
| 111 | const emoji = try Emoji.init(std.testing.allocator); | 71 | try std.testing.expect(isEmojiModifierBase(0x1F977)); // 🥷 |
| 112 | defer emoji.deinit(std.testing.allocator); | 72 | try std.testing.expect(!isEmojiModifierBase(0x1F4F8)); // 📸 |
| 113 | |||
| 114 | try std.testing.expect(emoji.isEmojiModifierBase(0x1F977)); // 🥷 | ||
| 115 | try std.testing.expect(!emoji.isEmojiModifierBase(0x1F4F8)); // 📸 | ||
| 116 | } | 73 | } |
| 117 | 74 | ||
| 118 | test "isEmojiComponent" { | 75 | test "isEmojiComponent" { |
| 119 | const emoji = try Emoji.init(std.testing.allocator); | 76 | try std.testing.expect(isEmojiComponent(0x1F9B0)); // 🦰 |
| 120 | defer emoji.deinit(std.testing.allocator); | 77 | try std.testing.expect(!isEmojiComponent(0x1F9B5)); // 🦵 |
| 121 | |||
| 122 | try std.testing.expect(emoji.isEmojiComponent(0x1F9B0)); // 🦰 | ||
| 123 | try std.testing.expect(!emoji.isEmojiComponent(0x1F9B5)); // 🦵 | ||
| 124 | } | 78 | } |
| 125 | 79 | ||
| 126 | test "isExtendedPictographic" { | 80 | test "isExtendedPictographic" { |
| 127 | const emoji = try Emoji.init(std.testing.allocator); | 81 | try std.testing.expect(isExtendedPictographic(0x1F005)); // 🀅 |
| 128 | defer emoji.deinit(std.testing.allocator); | 82 | try std.testing.expect(!isExtendedPictographic(0x2A)); // * |
| 129 | |||
| 130 | try std.testing.expect(emoji.isExtendedPictographic(0x1F005)); // 🀅 | ||
| 131 | try std.testing.expect(!emoji.isExtendedPictographic(0x2A)); // * | ||
| 132 | } | 83 | } |
| 84 | |||
| 85 | const std = @import("std"); | ||
| 86 | const builtin = @import("builtin"); | ||
| 87 | const unicode = std.unicode; | ||
| 88 | |||
| 89 | const CodePoint = @import("code_point").CodePoint; | ||
| 90 | const CodePointIterator = @import("code_point").Iterator; | ||