HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
base_n.hpp
1// Copyright Take Vos 2020-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../container/module.hpp"
8#include "../utility/utility.hpp"
9#include "../macros.hpp"
10#include <span>
11#include <cstdint>
12#include <array>
13#include <string>
14#include <string_view>
15#include <bit>
16
17hi_export_module(hikogui.codec.base_n);
18
19namespace hi::inline v1 {
20namespace detail {
21
23 long long radix;
24 bool case_insensitive;
25 char padding_char;
26 std::array<int8_t, 256> int_from_char_table = {};
27 std::array<char, 127> char_from_int_table = {};
28
34 template<std::size_t StringLength>
35 constexpr base_n_alphabet(
36 char const (&str)[StringLength],
37 bool case_insensitive = StringLength <= 33,
38 char padding_char = '\0') noexcept :
39 radix(narrow_cast<long long>(StringLength - 1)), case_insensitive(case_insensitive), padding_char(padding_char)
40 {
41 static_assert(StringLength < 128);
42
43 // Mark the int_from_char_table to have invalid characters.
44 for (long long i = 0; i != 256; ++i) {
45 int_from_char_table[i] = -2;
46 }
47
48 // Mark white-space in the int_from_char_table as white-space.
49 int_from_char_table[std::bit_cast<uint8_t>(' ')] = -1;
50 int_from_char_table[std::bit_cast<uint8_t>('\t')] = -1;
51 int_from_char_table[std::bit_cast<uint8_t>('\r')] = -1;
52 int_from_char_table[std::bit_cast<uint8_t>('\n')] = -1;
53 int_from_char_table[std::bit_cast<uint8_t>('\f')] = -1;
54
55 if (padding_char != 0) {
56 int_from_char_table[std::bit_cast<uint8_t>(padding_char)] = -1;
57 }
58
59 for (long long i = 0; i != radix; ++i) {
60 auto c = str[i];
61 char_from_int_table[i] = c;
62
63 int_from_char_table[std::bit_cast<uint8_t>(c)] = narrow_cast<int8_t>(i);
64 if constexpr (StringLength <= 33) {
65 // Add an extra entry for case folded form.
66 if (c >= 'a' && c <= 'z') {
67 int_from_char_table[narrow_cast<uint8_t>((c - 'a') + 'A')] = narrow_cast<int8_t>(i);
68 } else if (c >= 'A' && c <= 'Z') {
69 int_from_char_table[narrow_cast<uint8_t>((c - 'A') + 'a')] = narrow_cast<int8_t>(i);
70 }
71 }
72 }
73 }
74
78 constexpr char char_from_int(int8_t x) const noexcept
79 {
80 hi_axiom(x < radix);
81 return char_from_int_table[x];
82 }
83
84 constexpr int8_t int_from_char(char c) const noexcept
85 {
86 return int_from_char_table[std::bit_cast<uint8_t>(c)];
87 }
88};
89
90constexpr auto base2_alphabet = base_n_alphabet{"01"};
91
92constexpr auto base8_alphabet = base_n_alphabet{"01234567"};
93
94constexpr auto base10_alphabet = base_n_alphabet{"0123456789"};
95
96constexpr auto base16_alphabet = base_n_alphabet{"0123456789ABCDEF"};
97
98constexpr auto base32_rfc4648_alphabet = base_n_alphabet{"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"};
99
100constexpr auto base32hex_rfc4648_alphabet = base_n_alphabet{"0123456789ABCDEFGHIJKLMNOPQRSTUV"};
101
102constexpr auto base64_rfc4648_alphabet =
103 base_n_alphabet{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", false, '='};
104
105constexpr auto base64url_rfc4648_alphabet =
106 base_n_alphabet{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", false, '='};
107
108constexpr auto base85_rfc1924_alphabet =
109 base_n_alphabet{"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"};
110
111constexpr auto base85_btoa_alphabet =
112 base_n_alphabet{"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu"};
113
114} // namespace detail
115
116template<detail::base_n_alphabet Alphabet, int CharsPerBlock, int BytesPerBlock>
117class base_n {
118public:
119 constexpr static detail::base_n_alphabet alphabet = Alphabet;
120 constexpr static char padding_char = alphabet.padding_char;
121 constexpr static long long radix = alphabet.radix;
122 constexpr static long long bytes_per_block = BytesPerBlock;
123 constexpr static long long chars_per_block = CharsPerBlock;
124 static_assert(bytes_per_block != 0, "radix must be 16, 32, 64 or 85");
125 static_assert(chars_per_block != 0, "radix must be 16, 32, 64 or 85");
126
127 template<typename T>
128 constexpr static T int_from_char(char c) noexcept
129 {
130 return narrow_cast<T>(alphabet.int_from_char(c));
131 }
132
133 template<typename T>
134 constexpr static char char_from_int(T x) noexcept
135 {
136 return alphabet.char_from_int(narrow_cast<int8_t>(x));
137 }
138
145 template<typename ItIn, typename ItOut>
146 constexpr static void encode(ItIn ptr, ItIn last, ItOut output)
147 {
148 long long byte_index_in_block = 0;
149 long long block = 0;
150
151 while (ptr != last) {
152 // Construct a block in big endian.
153 hilet shift = 8 * ((bytes_per_block - 1) - byte_index_in_block);
154 block |= static_cast<long long>(*(ptr++)) << shift;
155
156 if (++byte_index_in_block == bytes_per_block) {
157 encode_block(block, bytes_per_block, output);
158 block = 0;
159 byte_index_in_block = 0;
160 }
161 }
162
163 if (byte_index_in_block != 0) {
164 encode_block(block, byte_index_in_block, output);
165 }
166 }
167
174 template<typename ItIn>
175 static std::string encode(ItIn first, ItIn last) noexcept
176 {
177 std::string r;
178 encode(first, last, std::back_inserter(r));
179 return r;
180 }
181
187 constexpr static std::string encode(std::span<std::byte const> bytes) noexcept
188 {
189 return encode(begin(bytes), end(bytes));
190 }
191
199 template<typename ItIn, typename ItOut>
200 constexpr static ItIn decode(ItIn ptr, ItIn last, ItOut output)
201 {
202 int char_index_in_block = 0;
203 long long block = 0;
204
205 for (; ptr != last; ++ptr) {
206 hilet digit = int_from_char<long long>(*ptr);
207 if (digit == -1) {
208 // Whitespace is ignored.
209 continue;
210
211 } else if (digit == -2) {
212 // Other character means end
213 return ptr;
214
215 } else {
216 block *= radix;
217 block += digit;
218
219 if (++char_index_in_block == chars_per_block) {
220 decode_block(block, chars_per_block, output);
221 block = 0;
222 char_index_in_block = 0;
223 }
224 }
225 }
226
227 if (char_index_in_block != 0) {
228 // pad the block with zeros.
229 for (auto i = char_index_in_block; i != chars_per_block; ++i) {
230 block *= radix;
231 }
232 decode_block(block, char_index_in_block, output);
233 }
234 return ptr;
235 }
236
237 static bstring decode(std::string_view str)
238 {
239 auto r = bstring{};
240 auto i = decode(begin(str), end(str), std::back_inserter(r));
241 hi_check(i == end(str), "base-n encoded string not completely decoded");
242 return r;
243 }
244
245private:
246 template<typename ItOut>
247 static void encode_block(long long block, long long nr_bytes, ItOut output) noexcept
248 {
249 hilet padding = bytes_per_block - nr_bytes;
250
251 // Construct a block in little-endian, using easy division/modulo.
252 auto char_block = std::string{};
253 for (long long i = 0; i != chars_per_block; ++i) {
254 hilet v = block % radix;
255 block /= radix;
256
257 if (i < padding) {
258 hi_assume(v != 0);
259 if (padding_char != 0) {
260 char_block += padding_char;
261 }
262 } else {
263 char_block += char_from_int(v);
264 }
265 }
266
267 // A block should be output as a big-endian radix-number.
268 std::copy(rbegin(char_block), rend(char_block), output);
269 }
270
271 template<typename ItOut>
272 constexpr static void decode_block(long long block, long long nr_chars, ItOut output)
273 {
274 hilet padding = chars_per_block - nr_chars;
275
276 if (block and bytes_per_block == padding) {
277 throw parse_error("Invalid number of character to decode.");
278 }
279
280 // Construct a block in little-endian, using easy division/modulo.
281 for (long long i = 0; i != (bytes_per_block - padding); ++i) {
282 hilet shift = 8 * ((bytes_per_block - 1) - i);
283 hilet byte = static_cast<std::byte>((block >> shift) & 0xff);
284
285 *(output++) = byte;
286 }
287
288 // The output data will not contain the padding.
289 }
290};
291
292// Alphabet, CharsPerBlock, BytesPerBlock
293hi_export using base2 = base_n<detail::base2_alphabet, 8, 1>;
294hi_export using base8 = base_n<detail::base8_alphabet, 8, 3>;
295hi_export using base16 = base_n<detail::base16_alphabet, 2, 1>;
296hi_export using base32 = base_n<detail::base32_rfc4648_alphabet, 8, 5>;
297hi_export using base32hex = base_n<detail::base32hex_rfc4648_alphabet, 8, 5>;
298hi_export using base64 = base_n<detail::base64_rfc4648_alphabet, 4, 3>;
299hi_export using base64url = base_n<detail::base64url_rfc4648_alphabet, 4, 3>;
300hi_export using base85 = base_n<detail::base85_rfc1924_alphabet, 5, 4>;
301hi_export using ascii85 = base_n<detail::base85_btoa_alphabet, 5, 4>;
302
303} // namespace hi::inline v1
DOXYGEN BUG.
Definition algorithm.hpp:16
@ shift
The shift key is being held.
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Definition base_n.hpp:22
constexpr base_n_alphabet(char const (&str)[StringLength], bool case_insensitive=StringLength<=33, char padding_char='\0') noexcept
Construct an alphabet.
Definition base_n.hpp:35
constexpr char char_from_int(int8_t x) const noexcept
Get a character from an integer.
Definition base_n.hpp:78
Definition base_n.hpp:117
static constexpr void encode(ItIn ptr, ItIn last, ItOut output)
Encode bytes into a string.
Definition base_n.hpp:146
static std::string encode(ItIn first, ItIn last) noexcept
Encode bytes into a string.
Definition base_n.hpp:175
static constexpr std::string encode(std::span< std::byte const > bytes) noexcept
Encode bytes into a string.
Definition base_n.hpp:187
static constexpr ItIn decode(ItIn ptr, ItIn last, ItOut output)
Decodes a UTF-8 string into bytes.
Definition base_n.hpp:200
T back_inserter(T... args)
T copy(T... args)