Skip to content

Commit f2a67d6

Browse files
committed
exp: apply #137
1 parent 3d86c1a commit f2a67d6

4 files changed

Lines changed: 32 additions & 30 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,9 @@ Since [Hermes doesn't support the `Intl.Segmenter` API](https://github.com/faceb
244244

245245
| Name | Unicode® | ESM? | Size | Size (min) | Size (min+gzip) | Size (min+br) | Size (min+zstd) |
246246
|-------------------------------------------------|----------|-------|--------:|-----------:|----------------:|--------------:|----------------:|
247-
| `unicode-segmenter/grapheme` | 17.0.0 | ✔️ | 8,820 | 5,199 | 2,509 | 2,253 | 2,569 |
248-
| `unicode-segmenter/grapheme-counter` | 17.0.0 | ✔️ | 7,723 | 4,880 | 2,368 | 2,149 | 2,423 |
249-
| `unicode-segmenter/grapheme + grapheme-counter` | 17.0.0 | ✔️ | 9,707 | 5,475 | 2,580 | 2,294 | 2,632 |
247+
| `unicode-segmenter/grapheme` | 17.0.0 | ✔️ | 8,812 | 5,243 | 2,527 | 2,276 | 2,588 |
248+
| `unicode-segmenter/grapheme-counter` | 17.0.0 | ✔️ | 7,716 | 4,924 | 2,385 | 2,160 | 2,436 |
249+
| `unicode-segmenter/grapheme + grapheme-counter` | 17.0.0 | ✔️ | 9,487 | 5,454 | 2,563 | 2,298 | 2,626 |
250250
| `graphemer` | 15.0.0 | ✖️ ️ | 410,435 | 95,104 | 15,752 | 10,660 | 15,911 |
251251
| `grapheme-splitter` | 10.0.0 | ✖️ | 122,254 | 23,682 | 7,852 | 4,802 | 6,753 |
252252
| `@formatjs/intl-segmenter`* | 17.0.0 | ✖️ | 268,301 | 176,759 | 45,988 | 31,701 | 45,370 |
@@ -262,9 +262,9 @@ Since [Hermes doesn't support the `Intl.Segmenter` API](https://github.com/faceb
262262

263263
| Name | Bytecode size | Bytecode size (gzip)* |
264264
|-------------------------------------------------|--------------:|----------------------:|
265-
| `unicode-segmenter/grapheme` | 19,928 | 11,028 |
266-
| `unicode-segmenter/grapheme-counter` | 18,421 | 10,212 |
267-
| `unicode-segmenter/grapheme + grapheme-counter` | 20,816 | 11,459 |
265+
| `unicode-segmenter/grapheme` | 20,002 | 11,086 |
266+
| `unicode-segmenter/grapheme-counter` | 18,491 | 10,278 |
267+
| `unicode-segmenter/grapheme + grapheme-counter` | 20,811 | 11,472 |
268268
| `graphemer` | 134,085 | 31,770 |
269269
| `grapheme-splitter` | 63,942 | 19,165 |
270270
| `@formatjs/intl-segmenter` | 329,547 | 136,751 |

src/grapheme-core.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,25 @@ export function cat(cp) {
120120
// - 2: GB12/GB13, no boundary iff odd run of RI precedes
121121
// - 3: GB11, no boundary iff the ZWJ was preceded by ExtPic Extend*
122122
// - 4: GB9c, no boundary iff InCB Consonant [Extend Linker]* Linker [Extend Linker]* precedes
123-
export const PAIR = Uint8Array.from(grapheme_pairs, Number);
123+
//
124+
// The table is expanded over the 32 packed states into `BND`, so the hot
125+
// loops resolve a boundary with a single indexed load,
126+
// `BND[(catBefore << 4 | catAfter) << 5 | st]`, and no branch chain.
127+
// Crucially the state comes from *before* the transition on `catAfter`,
128+
// so the load never depends on the previous iteration's load; a fully
129+
// fused (state x category) DFA measures 1.4-2x *slower* on V8 for exactly
130+
// that reason.
131+
export const BND = new Uint8Array(256 << 5);
132+
for (let p = 0; p < 256; p++) {
133+
let d = +grapheme_pairs[p];
134+
for (let st = 0; st < 32; st++) {
135+
BND[p << 5 | st] = d < 2 ? 1 - d
136+
: d === 2 ? ~st & 1
137+
: d === 3 ? ~st >> 2 & 1
138+
: +((st & 24) !== 16);
139+
}
140+
}
141+
// export const PAIR = Uint8Array.from(grapheme_pairs, Number);
124142

125143
/**
126144
* The Unicode `Indic_Conjunct_Break=Linker` set

src/grapheme-counter.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @ts-check
22

3-
import { PAIR, cat, nextState } from './grapheme-core.js';
3+
import { BND, cat, nextState } from './grapheme-core.js';
44

55
/**
66
* Count number of extended grapheme clusters in given text.
@@ -35,20 +35,12 @@ export function countGraphemes(text) {
3535
while (cursor < len) {
3636
cp = /** @type {number} */ (text.codePointAt(cursor));
3737
let catAfter = cat(cp);
38-
let d = PAIR[catBefore << 4 | catAfter];
39-
40-
let boundary;
41-
if (d === 0) boundary = true;
42-
else if (d === 1) boundary = false;
43-
else if (d === 2) boundary = !(st & 1);
44-
else if (d === 3) boundary = !(st & 4);
45-
else boundary = (st & 24) !== 16;
46-
47-
st = nextState(st, catAfter, cp);
38+
let boundary = BND[(catBefore << 4 | catAfter) << 5 | st];
4839

4940
if (boundary) count += 1;
5041
cursor += cp > 0xFFFF ? 2 : 1;
5142
catBefore = catAfter;
43+
st = nextState(st, catAfter, cp);
5244
}
5345

5446
return count;

src/grapheme.js

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @ts-check
22

3-
import { PAIR, cat, nextState } from './grapheme-core.js';
3+
import { BND, cat, nextState } from './grapheme-core.js';
44
import { GraphemeCategory } from './_grapheme_data.js';
55

66
/**
@@ -53,16 +53,7 @@ export function* graphemeSegments(input) {
5353
while (cursor < len) {
5454
cp = /** @type {number} */ (input.codePointAt(cursor));
5555
let catAfter = cat(cp);
56-
let d = PAIR[catBefore << 4 | catAfter];
57-
58-
let boundary;
59-
if (d === 0) boundary = true;
60-
else if (d === 1) boundary = false;
61-
else if (d === 2) boundary = !(st & 1);
62-
else if (d === 3) boundary = !(st & 4);
63-
else boundary = (st & 24) !== 16;
64-
65-
st = nextState(st, catAfter, cp);
56+
let boundary = BND[(catBefore << 4 | catAfter) << 5 | st];
6657

6758
if (boundary) {
6859
yield {
@@ -79,6 +70,7 @@ export function* graphemeSegments(input) {
7970
}
8071
cursor += cp > 0xFFFF ? 2 : 1;
8172
catBefore = catAfter;
73+
st = nextState(st, catAfter, cp);
8274
}
8375

8476
yield {
@@ -145,5 +137,5 @@ export function* splitGraphemes(text) {
145137
{
146138
let keep = graphemeSegments('_');
147139
// @ts-ignore intended expando
148-
PAIR._keep = [keep, keep.next()];
140+
BND._keep = [keep, keep.next()];
149141
}

0 commit comments

Comments
 (0)