HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
TT5.hpp
Go to the documentation of this file.
1// Copyright 2020 Pokitec
2// All rights reserved.
3
64#pragma once
65
66#include "TTauri/Foundation/required.hpp"
67#include "TTauri/Foundation/throw_exception.hpp"
68#include "TTauri/Foundation/bigint.hpp"
69#include <string>
70#include <string_view>
71#include <cstdint>
72
73namespace tt {
74
78[[nodiscard]] constexpr uint16_t tt5_code(uint16_t page, uint16_t prefix, uint16_t value)
79{
80 return (page << 10) | (prefix << 5) | value;
81}
82
86[[nodiscard]] constexpr uint16_t tt5_code_table_generate_entry(uint8_t c) noexcept
87{
88 switch (c) {
89 case '\0': return tt5_code(0, 0, 0);
90 case '_': return tt5_code(3, 0, 0x1b);
91 case '.': return tt5_code(3, 0, 0x1c);
92 case '-': return tt5_code(3, 0, 0x1d);
93 case ',': return tt5_code(2, 0, 0x0b);
94 case ':': return tt5_code(2, 0, 0x0c);
95 case ';': return tt5_code(2, 0, 0x0d);
96 case '/': return tt5_code(2, 0, 0x0e);
97 case '\n': return tt5_code(2, 0, 0x0f);
98 default:
99 if (c >= 'a' && c <= 'z') {
100 return tt5_code(0, 0, (c - 'a') + 1);
101 } else if (c >= 'A' && c <= 'Z') {
102 return tt5_code(1, 0, (c - 'A') + 1);
103 } else if (c >= '0' && c <= '9') {
104 return tt5_code(2, 0, (c - '0') + 1);
105 } else {
106 auto c_ = static_cast<uint16_t>(c);
107 return tt5_code(2, 0x10 | (c_ >> 5), (c_ & 0x1f)); // A0-A7
108 }
109 }
110}
111
112constexpr auto tt5_code_table_generator() noexcept
113{
115
116 for (ssize_t i = 0; i != ssize(r); ++i) {
117 r[i] = tt5_code_table_generate_entry(static_cast<uint8_t>(i));
118 }
119
120 return r;
121}
122
123constexpr auto tt5_code_table = tt5_code_table_generator();
124
125constexpr uint64_t tt5_code_from_char(uint8_t c) noexcept
126{
127 return static_cast<uint64_t>(tt5_code_table[c]) << 48;
128}
129
130/*
131* @param code 11:10 page_nr, 9:5 prefix-code, 4:0 data
132*/
133template<typename T>
134constexpr ssize_t tt5_add_code(T &r, uint16_t code) noexcept
135{
136 // Strip off the page_nr
137 code &= 0x3ff;
138
139 ssize_t nr_bits = static_cast<bool>(code >> 5) ? 10 : 5;
140
141 // If there is a prefix, then we need to add two code-units.
142 r <<= static_cast<unsigned int>(nr_bits);
143
144 // If prefix is zero, it doesn't matter if we 'or' the prefix on top
145 // of the non-shifted part of r.
146 r |= code;
147
148 return nr_bits;
149}
150
155[[nodiscard]] constexpr bool tt5_want_to_lock(uint64_t ring) noexcept
156{
157 ring >>= 10;
158 ttlet next_page = ring & 3;
159
160 bool r = true;
161 for (int i = 0; i != 3; ++i) {
162 ring >>= 16;
163 ttlet later_page = ring & 3;
164 r = r && (later_page == 3 || later_page == next_page);
165 }
166 return r;
167}
168
174template<typename T>
175[[nodiscard]] constexpr T tt5_encode(char const *str)
176{
177 auto r = T{};
178 ssize_t nr_bits = 0;
179 constexpr ssize_t max_nr_bits = sizeof (T) * CHAR_BIT;
180 uint64_t locked_page = 0;
181
182 // Start with adding 4 codes into the ring buffer.
183 uint64_t ring = 0;
184 uint64_t later_code = 1; // Not end-of-text.
185 for (int i = 0; i != 4; ++i) {
186 later_code = (later_code) ? tt5_code_from_char(*(str++)) : 0;
187 ring >>= 16;
188 ring |= later_code;
189 }
190
191 while (ring) {
192 ttlet next_page = (ring >> 10) & 3;
193
194 if (next_page != 3 && next_page != locked_page) {
195 if (tt5_want_to_lock(ring)) {
196 if (locked_page != 2) {
197 r <<= 5;
198 r |= 0x1f; // S2
199 _parse_assert2((nr_bits += 5) <= max_nr_bits, "String too long");
200 }
201
202 r <<= 5;
203 r |= 0x18 + next_page; // L0, L1, L2
204 _parse_assert2((nr_bits += 5) <= max_nr_bits, "String too long");
205
206 locked_page = next_page;
207
208 } else {
209 bool second_shift = (locked_page == 0 && next_page == 2) || (locked_page != 0 && next_page != 0);
210
211 r <<= 5;
212 r |= 0x1e + static_cast<int>(second_shift); // S0, S1, S2
213 _parse_assert2((nr_bits += 5) <= max_nr_bits, "String too long");
214 }
215 }
216
217 nr_bits += tt5_add_code(r, static_cast<uint16_t>(ring));
218 _parse_assert2(nr_bits <= max_nr_bits, "String too long");
219
220 // Add one code to the ring buffer.
221 later_code = (later_code) ? tt5_code_from_char(*(str++)) : 0;
222 ring >>= 16;
223 ring |= later_code;
224 }
225
226 return r;
227}
228
234template<typename T>
235[[nodiscard]] constexpr T tt5_encode(std::string const &s)
236{
237 return tt5_encode<T>(s.data());
238}
239
246template<typename T>
247constexpr T tt5_reverse(T value) noexcept
248{
249 auto r = T{};
250
251 while (value) {
252 r <<=5;
253 r |= value & 0x1f;
254 value >>= 5;
255 }
256
257 return r;
258}
259
260[[nodiscard]] constexpr uint8_t char_from_tt5_page0(char *&str, uint8_t code, uint8_t locked_page) noexcept
261{
262 switch (code) {
263 case 0x00: *(str++) = 0; return locked_page;
264 case 0x1b: *(str++) = '_'; return locked_page;
265 case 0x1c: *(str++) = '.'; return locked_page;
266 case 0x1d: *(str++) = '-'; return locked_page;
267 case 0x1e: return 1; // S1
268 case 0x1f: return 2; // S2
269 default:
270 *(str++) = (static_cast<char>(code) - 1) + 'a';
271 return locked_page;
272 }
273}
274
275[[nodiscard]] constexpr uint8_t char_from_tt5_page1(char *&str, uint8_t code, uint8_t locked_page) noexcept
276{
277 switch (code) {
278 case 0x00: *(str++) = 0; return locked_page;
279 case 0x1b: *(str++) = '_'; return locked_page;
280 case 0x1c: *(str++) = '.'; return locked_page;
281 case 0x1d: *(str++) = '-'; return locked_page;
282 case 0x1e: return 0; // S0
283 case 0x1f: return 2; // S2
284 default:
285 *(str++) = (static_cast<char>(code) - 1) + 'A';
286 return locked_page;
287 }
288}
289
290[[nodiscard]] constexpr uint8_t char_from_tt5_page2(char *&str, uint8_t code, uint8_t &locked_page) noexcept
291{
292 switch (code) {
293 case 0x00: *(str++) = 0; return locked_page;
294 case 0x0b: *(str++) = ','; return locked_page;
295 case 0x0c: *(str++) = ':'; return locked_page;
296 case 0x0d: *(str++) = ';'; return locked_page;
297 case 0x0e: *(str++) = '/'; return locked_page;
298 case 0x0f: *(str++) = '\n'; return locked_page;
299 case 0x10: return 0x03; // B0
300 case 0x11: return 0x13; // B1
301 case 0x12: return 0x23; // B2
302 case 0x13: return 0x33; // B3
303 case 0x14: return 0x43; // B4
304 case 0x15: return 0x53; // B5
305 case 0x16: return 0x63; // B6
306 case 0x17: return 0x73; // B7
307 case 0x18: return locked_page = 0; // L0
308 case 0x19: return locked_page = 1; // L1
309 case 0x1a: return locked_page = 2; // L2
310 case 0x1b: *(str++) = '_'; return locked_page;
311 case 0x1c: *(str++) = '.'; return locked_page;
312 case 0x1d: *(str++) = '-'; return locked_page;
313 case 0x1e: return 0; // S0
314 case 0x1f: return 1; // S1
315 default:
316 *(str++) = (static_cast<char>(code) - 1) + '0';
317 return locked_page;
318 }
319}
320
321[[nodiscard]] constexpr uint8_t char_from_tt5_binary(char *&str, uint8_t code, uint8_t locked_page, uint8_t high_bits) noexcept
322{
323 *(str++) = (high_bits << 5) | code;
324 return locked_page;
325}
326
330template<typename T>
331constexpr void fill_buffer_from_tt5(char *str, T const &value) noexcept
332{
333 auto value_ = tt5_reverse(value);
334
335 uint8_t current_page = 0;
336 uint8_t locked_page = 0;
337 while (value_) {
338 auto code = static_cast<uint8_t>(value_ & 0x1f);
339 value_ >>= 5;
340
341 switch (current_page & 3) {
342 case 0: current_page = char_from_tt5_page0(str, code, locked_page); break;
343 case 1: current_page = char_from_tt5_page1(str, code, locked_page); break;
344 case 2: current_page = char_from_tt5_page2(str, code, locked_page); break;
345 case 3: current_page = char_from_tt5_binary(str, code, locked_page, current_page >> 4); break;
346 default: tt_no_default;
347 }
348 }
349 *str = '\0';
350}
351
357template<typename T>
358[[nodiscard]] std::string tt5_decode(T const &value) noexcept {
359 constexpr ssize_t max_nr_bits = sizeof(T) * CHAR_BIT;
360 constexpr ssize_t max_nr_chars = max_nr_bits / 5;
361
363 fill_buffer_from_tt5(buffer.data(), value);
364 return {buffer.data()};
365}
366
367using tt5_64 = uint64_t;
368using tt5_128 = ubig128;
369
372constexpr auto operator""_tt5(char const *str, size_t) noexcept
373{
374 return tt5_encode<tt5_64>(str);
375}
376
379constexpr auto operator""_tt5_64(char const *str, size_t) noexcept
380{
381 return tt5_encode<tt5_64>(str);
382}
383
386constexpr auto operator""_tt5_128(char const *str, size_t) noexcept
387{
388 return tt5_encode<tt5_128>(str);
389}
390
391
392
393}
constexpr void fill_buffer_from_tt5(char *str, T const &value) noexcept
Convert a tt5 value to a string.
Definition TT5.hpp:331
constexpr T tt5_encode(char const *str)
Encode a UTF-8 string into an integer using TT5-encoding.
Definition TT5.hpp:175
constexpr uint16_t tt5_code(uint16_t page, uint16_t prefix, uint16_t value)
Create a tt5_code.
Definition TT5.hpp:78
constexpr uint16_t tt5_code_table_generate_entry(uint8_t c) noexcept
Convert a unicode character to a tt5-code.
Definition TT5.hpp:86
constexpr T tt5_reverse(T value) noexcept
Reverse the character in a TT5-packet-integer.
Definition TT5.hpp:247
std::string tt5_decode(T const &value) noexcept
Decode a TT5-packed integer into a UTF-8 string.
Definition TT5.hpp:358
constexpr bool tt5_want_to_lock(uint64_t ring) noexcept
Check if we want to use the lock command.
Definition TT5.hpp:155
T data(T... args)