const std = @import("std"); const builtin = @import("builtin"); const mem = std.mem; const Allocator = mem.Allocator; const compress = std.compress; const unicode = std.unicode; const CodePoint = @import("code_point").CodePoint; const CodePointIterator = @import("code_point").Iterator; s1: []u16 = undefined, s2: []u6 = undefined, const Emoji = @This(); // This must be an exact match of `Emoji` from `codegen/emoji.zig`. pub const Properties = packed struct { Emoji: bool = false, Emoji_Presentation: bool = false, Emoji_Modifier: bool = false, Emoji_Modifier_Base: bool = false, Emoji_Component: bool = false, Extended_Pictographic: bool = false, }; pub fn init(allocator: Allocator) Allocator.Error!Emoji { var emoji = Emoji{}; try emoji.setup(allocator); return emoji; } pub fn setup(emoji: *Emoji, allocator: Allocator) Allocator.Error!void { const decompressor = compress.flate.inflate.decompressor; const in_bytes = @embedFile("emoji"); var in_fbs = std.io.fixedBufferStream(in_bytes); var in_decomp = decompressor(.raw, in_fbs.reader()); var reader = in_decomp.reader(); const endian = builtin.cpu.arch.endian(); const s1_len: u16 = reader.readInt(u16, endian) catch unreachable; emoji.s1 = try allocator.alloc(u16, s1_len); errdefer allocator.free(emoji.s1); for (0..s1_len) |i| emoji.s1[i] = reader.readInt(u16, endian) catch unreachable; const s2_len: u16 = reader.readInt(u16, endian) catch unreachable; emoji.s2 = try allocator.alloc(u6, s2_len); errdefer allocator.free(emoji.s2); for (0..s2_len) |i| emoji.s2[i] = @intCast(reader.readInt(u8, endian) catch unreachable); } pub fn deinit(emoji: *const Emoji, allocator: Allocator) void { allocator.free(emoji.s1); allocator.free(emoji.s2); } /// Lookup the emoji properties for a code point. fn properties(emoji: Emoji, cp: u21) Properties { return @bitCast(emoji.s2[emoji.s1[cp >> 8] + (cp & 0xff)]); } pub fn isEmoji(emoji: Emoji, cp: u21) bool { return properties(emoji, cp).Emoji; } pub fn isEmojiPresentation(emoji: Emoji, cp: u21) bool { return properties(emoji, cp).Emoji_Presentation; } pub fn isEmojiModifier(emoji: Emoji, cp: u21) bool { return properties(emoji, cp).Emoji_Modifier; } pub fn isEmojiModifierBase(emoji: Emoji, cp: u21) bool { return properties(emoji, cp).Emoji_Modifier_Base; } pub fn isEmojiComponent(emoji: Emoji, cp: u21) bool { return properties(emoji, cp).Emoji_Component; } pub fn isExtendedPictographic(emoji: Emoji, cp: u21) bool { return properties(emoji, cp).Extended_Pictographic; } test "isEmoji" { const emoji = try Emoji.init(std.testing.allocator); defer emoji.deinit(std.testing.allocator); try std.testing.expect(emoji.isEmoji(0x1F415)); // 🐕 try std.testing.expect(!emoji.isEmoji(0x3042)); // あ } test "isEmojiPresentation" { const emoji = try Emoji.init(std.testing.allocator); defer emoji.deinit(std.testing.allocator); try std.testing.expect(emoji.isEmojiPresentation(0x1F408)); // 🐈 try std.testing.expect(!emoji.isEmojiPresentation(0x267E)); // ♾ } test "isEmojiModifier" { const emoji = try Emoji.init(std.testing.allocator); defer emoji.deinit(std.testing.allocator); try std.testing.expect(emoji.isEmojiModifier(0x1F3FF)); // 🏿 try std.testing.expect(!emoji.isEmojiModifier(0x1F385)); // 🎅 } test "isEmojiModifierBase" { const emoji = try Emoji.init(std.testing.allocator); defer emoji.deinit(std.testing.allocator); try std.testing.expect(emoji.isEmojiModifierBase(0x1F977)); // 🥷 try std.testing.expect(!emoji.isEmojiModifierBase(0x1F4F8)); // 📸 } test "isEmojiComponent" { const emoji = try Emoji.init(std.testing.allocator); defer emoji.deinit(std.testing.allocator); try std.testing.expect(emoji.isEmojiComponent(0x1F9B0)); // 🦰 try std.testing.expect(!emoji.isEmojiComponent(0x1F9B5)); // 🦵 } test "isExtendedPictographic" { const emoji = try Emoji.init(std.testing.allocator); defer emoji.deinit(std.testing.allocator); try std.testing.expect(emoji.isExtendedPictographic(0x1F005)); // 🀅 try std.testing.expect(!emoji.isExtendedPictographic(0x2A)); // * }