Broiler.Unicode.Emoji.StringProperties
0.1.0-preview.4
See the version list below for details.
dotnet add package Broiler.Unicode.Emoji.StringProperties --version 0.1.0-preview.4
NuGet\Install-Package Broiler.Unicode.Emoji.StringProperties -Version 0.1.0-preview.4
<PackageReference Include="Broiler.Unicode.Emoji.StringProperties" Version="0.1.0-preview.4" />
<PackageVersion Include="Broiler.Unicode.Emoji.StringProperties" Version="0.1.0-preview.4" />
<PackageReference Include="Broiler.Unicode.Emoji.StringProperties" />
paket add Broiler.Unicode.Emoji.StringProperties --version 0.1.0-preview.4
#r "nuget: Broiler.Unicode.Emoji.StringProperties, 0.1.0-preview.4"
#:package Broiler.Unicode.Emoji.StringProperties@0.1.0-preview.4
#addin nuget:?package=Broiler.Unicode.Emoji.StringProperties&version=0.1.0-preview.4&prerelease
#tool nuget:?package=Broiler.Unicode.Emoji.StringProperties&version=0.1.0-preview.4&prerelease
Broiler.Unicode
Unicode property, emoji string-property, and CLDR locale-data components used by
Broiler.JS, and the Unicode character data and algorithms of the shared JavaScript libraries,
which both Broiler JavaScript engines consume. The
shared libraries' packages are described
first; the rest of this README focuses on the Broiler.Unicode.Emoji.StringProperties package.
Preview status: This is unstable, substantially AI-assisted first-preview software. Generated tables and their generators require human source and data-provenance review. Human-review approval is revision-scoped; consult HUMAN_REVIEW.md for the reviewed revision and conditions before describing the current checkout as approved.
The shared JavaScript libraries' Unicode packages
The shared JavaScript libraries programme gives each JavaScript language algorithm and data set one maintained implementation, in libraries that name neither JavaScript engine. Both Broiler.JS and Broiler.VM's JavaScript profile consume them. Its Unicode area, milestone SJL-7, lives here:
- Broiler.VM's JSD-0074 placed all six sub-areas in this repository.
- This repository's roadmap carries its items and the owner conditions it adopted.
- The programme's plan lives in the Broiler.VM repository, under
docs/javascript-shared-libraries/.
| Package | Holds |
|---|---|
Broiler.Unicode.Algorithms |
Exact property lookups by the names ECMA-262 admits, per-code-point classes, the ECMAScript lexical classes, case folding with its orbits, the full case mappings with Final_Sigma and the Turkic and Lithuanian languages, NFC, NFD, NFKC and NFKD, and the properties of strings. It carries no table: every type reads its tables through the IUnicodeTables its caller passes. |
Broiler.Unicode.Segmentation |
UAX #29's grapheme, word and sentence boundaries, with word statuses. Optional: only an engine with Intl.Segmenter reads it. No table either. |
Broiler.Unicode.Tables |
This repository's own embedding of the generated tables, with UnicodeTables.Instance, for tests, probes and consumers other than the engines. Neither engine references it: each embeds its own tables (decision SJL-D-6 (c)). |
Broiler.Unicode.Generation |
Test-only. The generator: it verifies a pinned archive by length, SHA-256 and version header and writes every table byte for byte. It carries this repository's pin, so an engine's test can hold its own pin equal to it. |
using Broiler.Unicode.Algorithms;
using Broiler.Unicode.Tables;
var properties = new UnicodeProperties(UnicodeTables.Instance);
properties.TryResolve("Script_Extensions", "Grek", out var greek); // true
properties.TryResolve("letter", out _); // false: names are matched exactly
var normalization = new UnicodeNormalization(UnicodeTables.Instance);
normalization.Transform("eฬ", NormalForm.Nfc, maxLength: 1024, charge: null, out var nfc);
// UnicodeStatus.Success, nfc == "รฉ"
Scope. The engines call these packages for:
- the property escapes of regular expressions,
v-flag properties of strings included; - the identifier classes of the lexical grammar;
String.prototype.normalize;toUpperCase,toLowerCaseand their locale forms;- the case folding of
i-flag matching; Intl.Segmenter.
What stays in each engine is argument conversion, locale negotiation, error construction and its own resource policy. The adapter guide gives each operation's contract.
Pins.
- Data. Unicode 17.0.0 (
UnicodeAlgorithms.UnicodeVersion), from the archivedata/unicode/pins/. Itsunicode.pinhashes every file the generator reads, the same files by the same hashes as Broiler.VM's pin. - Names. ECMA-262's three property tables, at
0248456c, from the same archive. - Regeneration. A test regenerates every table byte for byte (the programme's rule SL7).
Conventions. The four packages follow the programme's conventions, in the owner's copy with the same numbering. Among them:
- caller-owned metering and explicit maximum lengths;
- failure as data;
- no host Unicode, culture or regular expressions;
- no mutable statics;
- Native AOT and trimming with no warning suppressed;
- an XML documentation comment on every public member.
The rules that decide them, SL1 to SL10, run in
tests/Unicode.Architecture.Tests. The
Native AOT probe, probes/Unicode.Probe, runs Unicode's own test files in CI.
Consumers. None yet. Broiler.VM's and Broiler.JS's cut-overs are milestone SJL-7's work packages SJL-7.4 and SJL-7.5.
Target framework: net10.0. Dependencies: none. Changes: CHANGELOG.md.
The emoji string-property package
Managed, allocation-light matching of Unicode RGI emoji string properties (UTS #51) for .NET, backed by a generated trie. No ICU, no native dependency, no runtime downloads, no giant regex.
- Supported Unicode emoji version: 17.0 (corrected 2026-10-16, roadmap item U7: it read 16.0, while the generated data has carried 17.0)
- Target frameworks:
net8.0,net10.0 - Dependencies: none (pure managed C#)
using UnicodeEmoji.StringProperties;
EmojiStringProperties.IsRgiEmoji("๐จโ๐ฉโ๐งโ๐ฆ"); // true (family ZWJ sequence)
EmojiStringProperties.IsBasicEmoji("โค๏ธ"); // true (U+2764 U+FE0F)
EmojiStringProperties.IsBasicEmoji("โค"); // false (missing U+FE0F presentation selector)
EmojiStringProperties.IsEmojiZwjSequence("๐ฉ๐ฝโ๐ป"); // true (woman technologist, medium skin tone)
Why a string property and not a code point property?
Emoji-ness is not a property of a single code point. The "woman technologist, medium skin tone"
emoji ๐ฉ๐ฝโ๐ป is the sequence U+1F469 U+1F3FD U+200D U+1F4BB โ four scalar values, six UTF-16
chars. Whether something is an emoji depends on the whole sequence: the presence of U+FE0F, the
zero-width joiners, regional-indicator pairs, tag characters, skin-tone modifiers, and so on.
RGI_Emoji (Recommended for General Interchange) is defined by UTS #51 as the union of
several string properties:
| Property | Example | Shape |
|---|---|---|
Basic_Emoji |
๐ โค๏ธ |
one emoji code point, optionally + U+FE0F |
Emoji_Keycap_Sequence |
1๏ธโฃ |
digit U+FE0F U+20E3 |
RGI_Emoji_Flag_Sequence |
๐ฉ๐ช |
a pair of regional indicator symbols |
RGI_Emoji_Tag_Sequence |
๐ด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ |
black flag + tag characters + U+E007F |
RGI_Emoji_Modifier_Sequence |
๐๐ฝ |
base emoji + skin-tone modifier |
RGI_Emoji_ZWJ_Sequence |
๐จโ๐ฉโ๐งโ๐ฆ ๐งโ๐คโ๐ง |
emoji joined with U+200D |
RGI_Emoji = Basic_Emoji
โช Emoji_Keycap_Sequence
โช RGI_Emoji_Flag_Sequence
โช RGI_Emoji_Tag_Sequence
โช RGI_Emoji_Modifier_Sequence
โช RGI_Emoji_ZWJ_Sequence
This library matches on Unicode scalar values, decoding surrogate pairs first, so it never assumes
one UTF-16 char equals one character.
Installation
The repository ships seven packages, all on one version:
| Package | Purpose | Target frameworks |
|---|---|---|
Broiler.Unicode.Properties |
Unicode character properties, scripts, categories, and case mappings | net8.0, net10.0 |
Broiler.Unicode.Emoji.StringProperties |
Unicode RGI emoji sequence matching | net8.0, net10.0 |
Broiler.Unicode.Cldr.LocaleData |
CLDR locale data for Intl implementations | net8.0, net10.0 |
Broiler.Unicode.Algorithms |
The shared JavaScript libraries' Unicode algorithms, over tables the caller passes | net10.0 |
Broiler.Unicode.Segmentation |
UAX #29 boundaries, over a break table the caller passes | net10.0 |
Broiler.Unicode.Tables |
The generated tables, and a provider over them | net10.0 |
Broiler.Unicode.Generation |
Test-only: the table generator and this repository's pin | net10.0 |
Select prerelease packages while the libraries are in preview:
dotnet add package Broiler.Unicode.Emoji.StringProperties --prerelease
For maintainer instructions, see publishing previews.
Earlier GitHub previews used the package IDs UnicodeEmoji.StringProperties and
UnicodeCldr.LocaleData. Update those PackageReference entries (and central
PackageVersion entries, if used) to the new IDs above. C# namespaces remain
UnicodeEmoji.StringProperties and UnicodeCldr.LocaleData.
API
All exact-match APIs require the entire input to be a single, fully-qualified emoji. They come in
string and ReadOnlySpan<char> overloads.
// Exact whole-string predicates
bool EmojiStringProperties.IsRgiEmoji(string value);
bool EmojiStringProperties.IsRgiEmoji(ReadOnlySpan<char> value);
bool EmojiStringProperties.IsBasicEmoji(/* string | ReadOnlySpan<char> */);
bool EmojiStringProperties.IsEmojiZwjSequence(/* string | ReadOnlySpan<char> */);
// Full property set for a single sequence (None if not an exact RGI emoji)
EmojiSequenceProperties EmojiStringProperties.GetProperties(/* string | ReadOnlySpan<char> */);
// Test for a specific combination of properties
bool EmojiStringProperties.HasProperties(ReadOnlySpan<char> value, EmojiSequenceProperties mask);
// Allocation-free enumeration of emoji embedded in text (greedy / leftmost-longest)
EmojiSequenceEnumerator EmojiStringProperties.EnumerateRgiEmoji(ReadOnlySpan<char> text);
EmojiSequenceEnumerator EmojiStringProperties.Enumerate(ReadOnlySpan<char> text, EmojiSequenceProperties filter);
// The Unicode emoji version the tables were generated from
string EmojiStringProperties.UnicodeEmojiVersion; // "17.0"
Enumeration without allocating strings
EnumerateRgiEmoji returns a ref struct enumerator that yields EmojiMatch values describing each
emoji by index and length (in UTF-16 code units). No substrings are allocated โ slice the original
text yourself if you need the characters.
ReadOnlySpan<char> text = "Hi ๐ family ๐จโ๐ฉโ๐งโ๐ฆ from ๐ฉ๐ช!";
foreach (EmojiMatch m in EmojiStringProperties.EnumerateRgiEmoji(text))
{
ReadOnlySpan<char> emoji = text.Slice(m.Index, m.Length);
Console.WriteLine($"{m.Index}..{m.End} -> {m.Properties}");
}
Classifying a single value
EmojiSequenceProperties p = EmojiStringProperties.GetProperties("๐ฉ๐ช");
// p == EmojiSequenceProperties.RgiEmojiFlagSequence
// (and p has the RgiEmoji bits, since every sequence property is a subset of RGI_Emoji)
How it works
The official Unicode data files are treated as the source of truth and are committed under
data/unicode/17.0/, whose two sequence files carry the hashes
data/unicode/pins/unicode.pin records for them:
emoji-test.txtemoji-sequences.txtemoji-zwj-sequences.txt
A build-time generator parses those files, normalizes every entry into an array of Unicode scalar
values, classifies it into one of the six properties above, and builds a flattened trie keyed on
scalar values. The trie is emitted as compact, diff-friendly C# arrays
(EmojiTrieData.g.cs) that are
committed to source control. At runtime the library only walks those arrays โ it never parses data
files, uses reflection, or emits code.
Matching uses leftmost-longest semantics so the longest valid emoji (e.g. the complete four-person family) wins over its shorter prefixes, and exact-match APIs require the entire input to terminate on a trie node, so partial sequences are never reported as RGI.
Updating to a new Unicode version
The DataTool downloads the official files and regenerates the lookup tables:
# Download data/unicode/<version>/ and regenerate src/.../Generated/EmojiTrieData.g.cs
dotnet run --project src/UnicodeEmoji.StringProperties.DataTool -- update 17.0
# Or as separate steps:
dotnet run --project src/UnicodeEmoji.StringProperties.DataTool -- download 17.0
dotnet run --project src/UnicodeEmoji.StringProperties.DataTool -- generate 17.0
Then bump UnicodeEmojiVersion in Directory.Build.props, review the diff of
the generated file, run the tests, and commit. Data is fetched only by this tool โ never during a
normal build or at runtime.
This does not move the shared JavaScript libraries' Unicode version. Their archive,
data/unicode/pins/, moves only by a dated decision that moves
Broiler.VM's pin in the same change, and its files are never downloaded again by a tool.
Benchmarks
benchmarks/ compares the generated trie against a naive HashSet<string> and a single
large compiled Regex, for both exact matching and enumeration:
dotnet run -c Release --project benchmarks/UnicodeEmoji.StringProperties.Benchmarks -- --filter '*'
The trie matches in time proportional to the input length (independent of the ~3,800-entry data set), allocates nothing on the hot path, and avoids the large startup and memory cost of compiling a giant alternation regex.
Limitations
- RGI only. This library implements the recommended-for-general-interchange set. It does not recognize valid-but-not-recommended emoji ZWJ sequences, arbitrary tag sequences, or every emoji-presentation code point in isolation.
- Exact match means fully-qualified. Minimally-qualified / unqualified forms (those missing a required U+FE0F) are intentionally not reported as RGI by the exact-match APIs.
- Not a grapheme segmenter.
EnumerateRgiEmojifinds RGI emoji; it is not a general Unicode text-segmentation (UAX #29) implementation. - Emoji presentation is matching-only. The library reports properties of text; it does not render, normalize, or transform emoji.
- Version-pinned. The data is generated for a single Unicode version (currently 17.0). Use the
DataToolto move to another version.
Project layout
src/
UnicodeEmoji.StringProperties/ # the NuGet library (net8.0; net10.0)
UnicodeEmoji.StringProperties.Generator/ # parser + trie builder + source emitter
UnicodeEmoji.StringProperties.DataTool/ # downloads data + drives the generator
Unicode.Algorithms/ # the shared libraries' algorithms (net10.0)
Unicode.Segmentation/ # UAX #29 boundaries (net10.0)
Unicode.Tables/ # the generated tables and a provider (net10.0)
Unicode.Generation/ # the pin's checks and the table generator (net10.0)
assurance.config.json # the assurance gate over the four projects above
tests/
UnicodeEmoji.StringProperties.Tests/ # golden + data-driven xUnit tests
Unicode.Algorithms.Tests/ # Unicode's test files, ECMA-262's tables, the contract, SL7
Unicode.Architecture.Tests/ # the conventions' rules SL1 to SL10
probes/
Unicode.Probe/ # the Native AOT probe
benchmarks/
UnicodeEmoji.StringProperties.Benchmarks/ # trie vs HashSet vs Regex
data/
unicode/17.0/ # committed official Unicode emoji data files
unicode/pins/ # the pinned archive the shared libraries' tables come from
docs/
roadmap.md, conventions.md, adapter-guide-unicode.md, evidence/
License
Broiler.Unicode source code is licensed under the Apache License 2.0. Unicode-provided data files and derived tables remain ยฉ Unicode, Inc. and subject to the Unicode Terms of Use; see THIRD_PARTY_NOTICES.md. The Apache license provides the software on an โAS ISโ basis without warranties or conditions.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Broiler.Unicode.Emoji.StringProperties:
| Package | Downloads |
|---|---|
|
Broiler.Regex
Experimental .NET 10 regular-expression engine implementing ECMAScript matching semantics for Broiler.JS. Preview with documented limitations. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-preview.5 | 32 | 10/17/2026 |
| 0.1.0-preview.4 | 44 | 10/16/2026 |
| 0.1.0-preview.3 | 540 | 10/11/2026 |
| 0.1.0-preview.2 | 2,052 | 9/23/2026 |