| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
1 |
#!/usr/bin/env python3 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
2 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
3 |
# This script is based on |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
4 |
# https://github.com/rust-lang/rust/blob/master/library/core/src/unicode/printable.py |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
5 |
# distributed under https://github.com/rust-lang/rust/blob/master/LICENSE-MIT. |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
6 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
7 |
# This script uses the following Unicode tables: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
8 |
# - UnicodeData.txt |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
9 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
10 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
11 |
from collections import namedtuple |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
12 |
import csv |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
13 |
import os |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
14 |
import subprocess |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
15 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
16 |
NUM_CODEPOINTS=0x110000 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
17 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
18 |
def to_ranges(iter): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
19 |
current = None |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
20 |
for i in iter: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
21 |
if current is None or i != current[1] or i in (0x10000, 0x20000): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
22 |
if current is not None: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
23 |
yield tuple(current) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
24 |
current = [i, i + 1] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
25 |
else: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
26 |
current[1] += 1 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
27 |
if current is not None: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
28 |
yield tuple(current) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
29 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
30 |
def get_escaped(codepoints): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
31 |
for c in codepoints: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
32 |
if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
33 |
yield c.value |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
34 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
35 |
def get_file(f): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
36 |
try: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
37 |
return open(os.path.basename(f)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
38 |
except FileNotFoundError: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
39 |
subprocess.run(["curl", "-O", f], check=True) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
40 |
return open(os.path.basename(f)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
41 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
42 |
Codepoint = namedtuple('Codepoint', 'value class_') |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
43 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
44 |
def get_codepoints(f): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
45 |
r = csv.reader(f, delimiter=";") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
46 |
prev_codepoint = 0 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
47 |
class_first = None |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
48 |
for row in r: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
49 |
codepoint = int(row[0], 16) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
50 |
name = row[1] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
51 |
class_ = row[2] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
52 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
53 |
if class_first is not None: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
54 |
if not name.endswith("Last>"): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
55 |
raise ValueError("Missing Last after First") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
56 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
57 |
for c in range(prev_codepoint + 1, codepoint): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
58 |
yield Codepoint(c, class_first) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
59 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
60 |
class_first = None |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
61 |
if name.endswith("First>"): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
62 |
class_first = class_ |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
63 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
64 |
yield Codepoint(codepoint, class_) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
65 |
prev_codepoint = codepoint |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
66 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
67 |
if class_first is not None: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
68 |
raise ValueError("Missing Last after First") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
69 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
70 |
for c in range(prev_codepoint + 1, NUM_CODEPOINTS): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
71 |
yield Codepoint(c, None) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
72 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
73 |
def compress_singletons(singletons): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
74 |
uppers = [] # (upper, # items in lowers) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
75 |
lowers = [] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
76 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
77 |
for i in singletons: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
78 |
upper = i >> 8 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
79 |
lower = i & 0xff |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
80 |
if len(uppers) == 0 or uppers[-1][0] != upper: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
81 |
uppers.append((upper, 1)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
82 |
else: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
83 |
upper, count = uppers[-1] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
84 |
uppers[-1] = upper, count + 1 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
85 |
lowers.append(lower) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
86 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
87 |
return uppers, lowers |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
88 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
89 |
def compress_normal(normal): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
90 |
# lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
91 |
# lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
92 |
compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
93 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
94 |
prev_start = 0 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
95 |
for start, count in normal: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
96 |
truelen = start - prev_start |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
97 |
falselen = count |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
98 |
prev_start = start + count |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
99 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
100 |
assert truelen < 0x8000 and falselen < 0x8000 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
101 |
entry = [] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
102 |
if truelen > 0x7f: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
103 |
entry.append(0x80 | (truelen >> 8)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
104 |
entry.append(truelen & 0xff) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
105 |
else: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
106 |
entry.append(truelen & 0x7f) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
107 |
if falselen > 0x7f: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
108 |
entry.append(0x80 | (falselen >> 8)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
109 |
entry.append(falselen & 0xff) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
110 |
else: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
111 |
entry.append(falselen & 0x7f) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
112 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
113 |
compressed.append(entry) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
114 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
115 |
return compressed |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
116 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
117 |
def print_singletons(uppers, lowers, uppersname, lowersname): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
118 |
print(" static constexpr singleton {}[] = {{".format(uppersname)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
119 |
for u, c in uppers: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
120 |
print(" {{{:#04x}, {}}},".format(u, c)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
121 |
print(" };") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
122 |
print(" static constexpr unsigned char {}[] = {{".format(lowersname)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
123 |
for i in range(0, len(lowers), 8): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
124 |
print(" {}".format(" ".join("{:#04x},".format(l) for l in lowers[i:i+8]))) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
125 |
print(" };") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
126 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
127 |
def print_normal(normal, normalname): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
128 |
print(" static constexpr unsigned char {}[] = {{".format(normalname)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
129 |
for v in normal: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
130 |
print(" {}".format(" ".join("{:#04x},".format(i) for i in v))) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
131 |
print(" };") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
132 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
133 |
def main(): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
134 |
file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
135 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
136 |
codepoints = get_codepoints(file) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
137 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
138 |
CUTOFF=0x10000 |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
139 |
singletons0 = [] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
140 |
singletons1 = [] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
141 |
normal0 = [] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
142 |
normal1 = [] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
143 |
extra = [] |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
144 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
145 |
for a, b in to_ranges(get_escaped(codepoints)): |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
146 |
if a > 2 * CUTOFF: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
147 |
extra.append((a, b - a)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
148 |
elif a == b - 1: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
149 |
if a & CUTOFF: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
150 |
singletons1.append(a & ~CUTOFF) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
151 |
else: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
152 |
singletons0.append(a) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
153 |
elif a == b - 2: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
154 |
if a & CUTOFF: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
155 |
singletons1.append(a & ~CUTOFF) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
156 |
singletons1.append((a + 1) & ~CUTOFF) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
157 |
else: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
158 |
singletons0.append(a) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
159 |
singletons0.append(a + 1) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
160 |
else: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
161 |
if a >= 2 * CUTOFF: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
162 |
extra.append((a, b - a)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
163 |
elif a & CUTOFF: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
164 |
normal1.append((a & ~CUTOFF, b - a)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
165 |
else: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
166 |
normal0.append((a, b - a)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
167 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
168 |
singletons0u, singletons0l = compress_singletons(singletons0) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
169 |
singletons1u, singletons1l = compress_singletons(singletons1) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
170 |
normal0 = compress_normal(normal0) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
171 |
normal1 = compress_normal(normal1) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
172 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
173 |
print("""\ |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
174 |
inline auto is_printable(uint32_t cp) -> bool {\ |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
175 |
""") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
176 |
print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower') |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
177 |
print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
178 |
print_normal(normal0, 'normal0') |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
179 |
print_normal(normal1, 'normal1') |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
180 |
print("""\ |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
181 |
auto lower = static_cast<uint16_t>(cp); |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
182 |
if (cp < 0x10000) { |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
183 |
return is_printable(lower, singletons0, |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
184 |
sizeof(singletons0) / sizeof(*singletons0), |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
185 |
singletons0_lower, normal0, sizeof(normal0)); |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
186 |
} |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
187 |
if (cp < 0x20000) { |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
188 |
return is_printable(lower, singletons1, |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
189 |
sizeof(singletons1) / sizeof(*singletons1), |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
190 |
singletons1_lower, normal1, sizeof(normal1)); |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
191 |
}\ |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
192 |
""") |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
193 |
for a, b in extra: |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
194 |
print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
195 |
print("""\ |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
196 |
return cp < 0x{:x}; |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
197 |
}}\ |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
198 |
""".format(NUM_CODEPOINTS)) |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
199 |
|
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
200 |
if __name__ == '__main__': |
| f8deb5b9 |
Poul Sander |
2022-01-15 13:55 |
201 |
main() |
|
|
1970-01-01 00:00 |
202 |
|