From d7df2b4b92e198fbdbe5cfc29095d48980675004 Mon Sep 17 00:00:00 2001 From: Jacob Sandlund Date: Tue, 24 Jun 2025 07:55:16 -0400 Subject: Add Emoji module and codegen/emoji --- src/Emoji.zig | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/Emoji.zig (limited to 'src/Emoji.zig') diff --git a/src/Emoji.zig b/src/Emoji.zig new file mode 100644 index 0000000..bf7014d --- /dev/null +++ b/src/Emoji.zig @@ -0,0 +1,132 @@ +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)); // * +} -- cgit v1.2.3 From 6ec7bae5e541f6f471cd3ca5801f2a976553573f Mon Sep 17 00:00:00 2001 From: Jacob Sandlund Date: Tue, 24 Jun 2025 08:00:03 -0400 Subject: fix infinity --- src/Emoji.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Emoji.zig') diff --git a/src/Emoji.zig b/src/Emoji.zig index bf7014d..75b44c2 100644 --- a/src/Emoji.zig +++ b/src/Emoji.zig @@ -96,7 +96,7 @@ test "isEmojiPresentation" { defer emoji.deinit(std.testing.allocator); try std.testing.expect(emoji.isEmojiPresentation(0x1F408)); // 🐈 - try std.testing.expect(!emoji.isEmojiPresentation(0x267E)); // ♾️ + try std.testing.expect(!emoji.isEmojiPresentation(0x267E)); // β™Ύ } test "isEmojiModifier" { -- cgit v1.2.3