==> tests/graphemes.c <==1/* Grapheme boundaries against Rust's unicode-segmentation 1.13.3 (graphemes(true)): hand-picked
2 * sequences, and a 7.5 kB mixed text (every 7th code point below U+3000, emoji, Hangul, flags,
3 * Indic conjuncts, combining accents and ZWJs sprinkled in) whose cluster lengths hash to what
4 * Rust computed. */
5#include "mesh/check.h"
6#include "tui/grapheme.h"
7
8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13static size_t count(const char *s) {
14 size_t n = 0, len = strlen(s);
15 for (size_t i = 0; i < len; i = tui_grapheme_next(s, len, i)) n++;
16 return n;
17}
18
19int main(void) {
20 CHECK_U(count(""), 0);
21 CHECK_U(count("abc"), 3);
22 CHECK_U(count("\r\n"), 1);
23 CHECK_U(count("e\xCC\x81"), 1); /* e + U+0301 */
24 CHECK_U(count("\xF0\x9F\x91\x8D\xF0\x9F\x8F\xBD"), 1); /* thumbs up + skin tone */
25 CHECK_U(count("\xF0\x9F\x91\xA8\xE2\x80\x8D\xF0\x9F\x91\xA9\xE2\x80\x8D\xF0\x9F\x91\xA7"), 1); /* family */
26 CHECK_U(count("\xF0\x9F\x87\xA6\xF0\x9F\x87\xBA\xF0\x9F\x87\xB3"), 2); /* AU + N */
27 CHECK_U(count("#\xEF\xB8\x8F\xE2\x83\xA3"), 1); /* keycap */
28 CHECK_U(count("\xE1\x84\x80\xE1\x85\xA1"), 1); /* Hangul L V */
29 CHECK_U(count("a\xE2\x80\x8D" "b"), 2); /* a ZWJ | b */
30 CHECK_U(count("\xEF\xBD\xB1\xEF\xBE\x9E"), 1); /* halfwidth ka + voiced mark */
31 CHECK_U(count("\xE0\xA4\x95\xE0\xA5\x8D\xE0\xA4\xB7"), 1); /* ka virama ssa (GB9c) */
32
33 FILE *f = fopen("tests/data/mixed.txt", "rb");
34 CHECK(f != NULL);
35 if (f) {
36 char *text = malloc(16384);
37 size_t len = fread(text, 1, 16384, f);
38 fclose(f);
39 CHECK_U(len, 7582);
40 uint64_t h = 0xcbf29ce484222325u, n = 0;
41 for (size_t i = 0; i < len;) {
42 size_t end = tui_grapheme_next(text, len, i);
43 uint32_t g = (uint32_t)(end - i);
44 for (int k = 0; k < 4; k++) {
45 h ^= (g >> (8 * k)) & 0xff;
46 h *= 0x100000001b3u;
47 }
48 n++;
49 i = end;
50 }
51 CHECK_U(n, 1768);
52 CHECK_U(h, 0xf7e2321a1b4685f5u);
53 free(text);
54 }
55 return mesh_check_done();
56}