File Coverage

/usr/local/lib/perl5/site_perl/5.42.0/x86_64-linux/Horus/include/horus_encode.h
Criterion Covered Total %
statement 38 38 100.0
branch 21 22 95.4
condition n/a
subroutine n/a
pod n/a
total 59 60 98.3


line stmt bran cond sub pod time code
1             #ifndef HORUS_ENCODE_H
2             #define HORUS_ENCODE_H
3              
4             /*
5             * horus_encode.h - High-performance encoding: hex, Base64, Base32, Crockford
6             *
7             * Hex uses a uint16_t lookup table: one table lookup per byte produces
8             * two hex characters. ~4x faster than sprintf("%02x").
9             */
10              
11             #include
12             #include
13              
14             /* ── Hex lookup tables (compile-time initialised) ───────────────── */
15              
16             #define HEX_PAIR(hi, lo) (uint16_t)( ((uint16_t)(lo) << 8) | (uint16_t)(hi) )
17              
18             /* Helper macro to generate 16 entries for a given high nibble */
19             #define HEX_ROW(h, H) \
20             HEX_PAIR(h,'0'), HEX_PAIR(h,'1'), HEX_PAIR(h,'2'), HEX_PAIR(h,'3'), \
21             HEX_PAIR(h,'4'), HEX_PAIR(h,'5'), HEX_PAIR(h,'6'), HEX_PAIR(h,'7'), \
22             HEX_PAIR(h,'8'), HEX_PAIR(h,'9'), HEX_PAIR(h,'a'), HEX_PAIR(h,'b'), \
23             HEX_PAIR(h,'c'), HEX_PAIR(h,'d'), HEX_PAIR(h,'e'), HEX_PAIR(h,'f')
24              
25             #define HEX_ROW_UPPER(h, H) \
26             HEX_PAIR(h,'0'), HEX_PAIR(h,'1'), HEX_PAIR(h,'2'), HEX_PAIR(h,'3'), \
27             HEX_PAIR(h,'4'), HEX_PAIR(h,'5'), HEX_PAIR(h,'6'), HEX_PAIR(h,'7'), \
28             HEX_PAIR(h,'8'), HEX_PAIR(h,'9'), HEX_PAIR(h,'A'), HEX_PAIR(h,'B'), \
29             HEX_PAIR(h,'C'), HEX_PAIR(h,'D'), HEX_PAIR(h,'E'), HEX_PAIR(h,'F')
30              
31             static const uint16_t horus_hex_lut[256] = {
32             HEX_ROW('0', '0'), HEX_ROW('1', '1'), HEX_ROW('2', '2'), HEX_ROW('3', '3'),
33             HEX_ROW('4', '4'), HEX_ROW('5', '5'), HEX_ROW('6', '6'), HEX_ROW('7', '7'),
34             HEX_ROW('8', '8'), HEX_ROW('9', '9'), HEX_ROW('a', 'a'), HEX_ROW('b', 'b'),
35             HEX_ROW('c', 'c'), HEX_ROW('d', 'd'), HEX_ROW('e', 'e'), HEX_ROW('f', 'f')
36             };
37              
38             static const uint16_t horus_hex_lut_upper[256] = {
39             HEX_ROW_UPPER('0', '0'), HEX_ROW_UPPER('1', '1'), HEX_ROW_UPPER('2', '2'), HEX_ROW_UPPER('3', '3'),
40             HEX_ROW_UPPER('4', '4'), HEX_ROW_UPPER('5', '5'), HEX_ROW_UPPER('6', '6'), HEX_ROW_UPPER('7', '7'),
41             HEX_ROW_UPPER('8', '8'), HEX_ROW_UPPER('9', '9'), HEX_ROW_UPPER('A', 'A'), HEX_ROW_UPPER('B', 'B'),
42             HEX_ROW_UPPER('C', 'C'), HEX_ROW_UPPER('D', 'D'), HEX_ROW_UPPER('E', 'E'), HEX_ROW_UPPER('F', 'F')
43             };
44              
45             /* Write two hex chars for one byte */
46             static inline void horus_hex_byte(char *dst, unsigned char b, const uint16_t *lut) {
47             memcpy(dst, &lut[b], 2);
48             }
49              
50             /* Encode 16 bytes to 32 hex chars */
51             static inline void horus_hex_encode(char *dst, const unsigned char *src, const uint16_t *lut) {
52             int i;
53             for (i = 0; i < 16; i++) {
54             horus_hex_byte(dst + i * 2, src[i], lut);
55             }
56             }
57              
58             /* ── Hex decode ─────────────────────────────────────────────────── */
59              
60             static const unsigned char horus_hex_val[256] = {
61             ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3,
62             ['4'] = 4, ['5'] = 5, ['6'] = 6, ['7'] = 7,
63             ['8'] = 8, ['9'] = 9,
64             ['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13, ['e'] = 14, ['f'] = 15,
65             ['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13, ['E'] = 14, ['F'] = 15,
66             };
67              
68             static inline int horus_is_hex(char c) {
69             return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
70             }
71              
72             static inline int horus_hex_decode(unsigned char *dst, const char *src, int nhex) {
73             int i;
74             for (i = 0; i < nhex; i += 2) {
75             if (!horus_is_hex(src[i]) || !horus_is_hex(src[i+1]))
76             return 0;
77             dst[i/2] = (horus_hex_val[(unsigned char)src[i]] << 4)
78             | horus_hex_val[(unsigned char)src[i+1]];
79             }
80             return 1;
81             }
82              
83             /* ── Base64 encoding (no padding, 22 chars for 16 bytes) ────────── */
84              
85             static const char horus_b64_alphabet[] =
86             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
87              
88             static inline void horus_base64_encode(char *dst, const unsigned char *src) {
89             int i, j = 0;
90             /* Process 3 bytes at a time -> 4 base64 chars */
91             for (i = 0; i + 2 < 16; i += 3) {
92             unsigned int v = ((unsigned int)src[i] << 16)
93             | ((unsigned int)src[i+1] << 8)
94             | (unsigned int)src[i+2];
95             dst[j++] = horus_b64_alphabet[(v >> 18) & 0x3F];
96             dst[j++] = horus_b64_alphabet[(v >> 12) & 0x3F];
97             dst[j++] = horus_b64_alphabet[(v >> 6) & 0x3F];
98             dst[j++] = horus_b64_alphabet[v & 0x3F];
99             }
100             /* Last byte (16 % 3 = 1 remaining byte) */
101             {
102             unsigned int v = (unsigned int)src[15] << 16;
103             dst[j++] = horus_b64_alphabet[(v >> 18) & 0x3F];
104             dst[j++] = horus_b64_alphabet[(v >> 12) & 0x3F];
105             }
106             /* j == 22 */
107             }
108              
109             /* ── Base64 decoding ────────────────────────────────────────────── */
110              
111             static const unsigned char horus_b64_decode_table[256] = {
112             ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5,
113             ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11,
114             ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17,
115             ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
116             ['Y'] = 24, ['Z'] = 25,
117             ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31,
118             ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37,
119             ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43,
120             ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49,
121             ['y'] = 50, ['z'] = 51,
122             ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57,
123             ['6'] = 58, ['7'] = 59, ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63,
124             };
125              
126             static inline int horus_base64_decode(unsigned char *dst, const char *src, int len) {
127             int i, j = 0;
128             /* Process 4 chars at a time -> 3 bytes */
129             for (i = 0; i + 3 < len; i += 4) {
130             unsigned int v = ((unsigned int)horus_b64_decode_table[(unsigned char)src[i]] << 18)
131             | ((unsigned int)horus_b64_decode_table[(unsigned char)src[i+1]] << 12)
132             | ((unsigned int)horus_b64_decode_table[(unsigned char)src[i+2]] << 6)
133             | (unsigned int)horus_b64_decode_table[(unsigned char)src[i+3]];
134             dst[j++] = (unsigned char)(v >> 16);
135             dst[j++] = (unsigned char)(v >> 8);
136             dst[j++] = (unsigned char)(v);
137             }
138             /* Last 2 chars -> 1 byte (22 chars: 5 groups of 4 + 2 remaining) */
139             if (i + 1 < len) {
140             unsigned int v = ((unsigned int)horus_b64_decode_table[(unsigned char)src[i]] << 18)
141             | ((unsigned int)horus_b64_decode_table[(unsigned char)src[i+1]] << 12);
142             dst[j++] = (unsigned char)(v >> 16);
143             }
144             return j;
145             }
146              
147             /* ── Base32 encoding (RFC 4648, 26 chars for 16 bytes) ──────────── */
148              
149             static const char horus_b32_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
150              
151             static inline void horus_base32_encode(char *dst, const unsigned char *src) {
152             /* 16 bytes = 128 bits. 128 / 5 = 25 full groups + 3 bits.
153             * With padding that's 26 chars (last char encodes 3 bits, padded). */
154             int i, bit_pos = 0, byte_pos = 0, j = 0;
155             for (i = 0; i < 26; i++) {
156             int val = 0;
157             int bits_needed = 5;
158             int bits_have = 0;
159              
160             while (bits_have < bits_needed && byte_pos < 16) {
161             int bits_left_in_byte = 8 - bit_pos;
162             int bits_to_take = bits_needed - bits_have;
163             if (bits_to_take > bits_left_in_byte)
164             bits_to_take = bits_left_in_byte;
165              
166             val = (val << bits_to_take) | ((src[byte_pos] >> (bits_left_in_byte - bits_to_take)) & ((1 << bits_to_take) - 1));
167             bits_have += bits_to_take;
168             bit_pos += bits_to_take;
169             if (bit_pos >= 8) {
170             bit_pos = 0;
171             byte_pos++;
172             }
173             }
174             /* Pad remaining bits with zero if we ran out of data */
175             if (bits_have < bits_needed) {
176             val <<= (bits_needed - bits_have);
177             }
178             dst[j++] = horus_b32_alphabet[val & 0x1F];
179             }
180             }
181              
182             /* ── Base32 decoding ────────────────────────────────────────────── */
183              
184             static const unsigned char horus_b32_decode_table[256] = {
185             ['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4,
186             ['F'] = 5, ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9,
187             ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14,
188             ['P'] = 15, ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19,
189             ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24,
190             ['Z'] = 25,
191             ['2'] = 26, ['3'] = 27, ['4'] = 28, ['5'] = 29, ['6'] = 30, ['7'] = 31,
192             /* lowercase aliases */
193             ['a'] = 0, ['b'] = 1, ['c'] = 2, ['d'] = 3, ['e'] = 4,
194             ['f'] = 5, ['g'] = 6, ['h'] = 7, ['i'] = 8, ['j'] = 9,
195             ['k'] = 10, ['l'] = 11, ['m'] = 12, ['n'] = 13, ['o'] = 14,
196             ['p'] = 15, ['q'] = 16, ['r'] = 17, ['s'] = 18, ['t'] = 19,
197             ['u'] = 20, ['v'] = 21, ['w'] = 22, ['x'] = 23, ['y'] = 24,
198             ['z'] = 25,
199             };
200              
201             static inline int horus_base32_decode(unsigned char *dst, const char *src, int len) {
202             int i, bit_pos = 0, byte_pos = 0;
203             memset(dst, 0, 16);
204             for (i = 0; i < len && byte_pos < 16; i++) {
205             unsigned char val = horus_b32_decode_table[(unsigned char)src[i]];
206             int bits_left = 5;
207             while (bits_left > 0 && byte_pos < 16) {
208             int space = 8 - bit_pos;
209             if (bits_left >= space) {
210             dst[byte_pos] |= (val >> (bits_left - space)) & ((1 << space) - 1);
211             bits_left -= space;
212             bit_pos = 0;
213             byte_pos++;
214             } else {
215             dst[byte_pos] |= (val & ((1 << bits_left) - 1)) << (space - bits_left);
216             bit_pos += bits_left;
217             bits_left = 0;
218             }
219             }
220             }
221             return byte_pos;
222             }
223              
224             /* ── Crockford Base32 encoding (26 chars for 16 bytes) ──────────── */
225              
226             static const char horus_crockford_alphabet[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
227              
228 2858           static inline void horus_crockford_encode(char *dst, const unsigned char *src) {
229 2858           int i, bit_pos = 0, byte_pos = 0, j = 0;
230 77166 100         for (i = 0; i < 26; i++) {
231 74308           int val = 0;
232 74308           int bits_needed = 5;
233 74308           int bits_have = 0;
234              
235 182912 100         while (bits_have < bits_needed && byte_pos < 16) {
    100          
236 108604           int bits_left_in_byte = 8 - bit_pos;
237 108604           int bits_to_take = bits_needed - bits_have;
238 108604 100         if (bits_to_take > bits_left_in_byte)
239 37154           bits_to_take = bits_left_in_byte;
240              
241 108604           val = (val << bits_to_take) | ((src[byte_pos] >> (bits_left_in_byte - bits_to_take)) & ((1 << bits_to_take) - 1));
242 108604           bits_have += bits_to_take;
243 108604           bit_pos += bits_to_take;
244 108604 100         if (bit_pos >= 8) {
245 45728           bit_pos = 0;
246 45728           byte_pos++;
247             }
248             }
249 74308 100         if (bits_have < bits_needed) {
250 2858           val <<= (bits_needed - bits_have);
251             }
252 74308           dst[j++] = horus_crockford_alphabet[val & 0x1F];
253             }
254 2858           }
255              
256             /* ── Crockford Base32 decoding ──────────────────────────────────── */
257              
258             static const unsigned char horus_crockford_decode_table[256] = {
259             ['0'] = 0, ['O'] = 0, ['o'] = 0,
260             ['1'] = 1, ['I'] = 1, ['i'] = 1, ['L'] = 1, ['l'] = 1,
261             ['2'] = 2, ['3'] = 3, ['4'] = 4, ['5'] = 5, ['6'] = 6,
262             ['7'] = 7, ['8'] = 8, ['9'] = 9,
263             ['A'] = 10, ['a'] = 10,
264             ['B'] = 11, ['b'] = 11,
265             ['C'] = 12, ['c'] = 12,
266             ['D'] = 13, ['d'] = 13,
267             ['E'] = 14, ['e'] = 14,
268             ['F'] = 15, ['f'] = 15,
269             ['G'] = 16, ['g'] = 16,
270             ['H'] = 17, ['h'] = 17,
271             ['J'] = 18, ['j'] = 18,
272             ['K'] = 19, ['k'] = 19,
273             ['M'] = 20, ['m'] = 20,
274             ['N'] = 21, ['n'] = 21,
275             ['P'] = 22, ['p'] = 22,
276             ['Q'] = 23, ['q'] = 23,
277             ['R'] = 24, ['r'] = 24,
278             ['S'] = 25, ['s'] = 25,
279             ['T'] = 26, ['t'] = 26,
280             ['V'] = 27, ['v'] = 27,
281             ['W'] = 28, ['w'] = 28,
282             ['X'] = 29, ['x'] = 29,
283             ['Y'] = 30, ['y'] = 30,
284             ['Z'] = 31, ['z'] = 31,
285             };
286              
287 107           static inline int horus_crockford_decode(unsigned char *dst, const char *src, int len) {
288 107           int i, bit_pos = 0, byte_pos = 0;
289 107           memset(dst, 0, 16);
290 2889 100         for (i = 0; i < len && byte_pos < 16; i++) {
    50          
291 2782           unsigned char val = horus_crockford_decode_table[(unsigned char)src[i]];
292 2782           int bits_left = 5;
293 6848 100         while (bits_left > 0 && byte_pos < 16) {
    100          
294 4066           int space = 8 - bit_pos;
295 4066 100         if (bits_left >= space) {
296 1712           dst[byte_pos] |= (val >> (bits_left - space)) & ((1 << space) - 1);
297 1712           bits_left -= space;
298 1712           bit_pos = 0;
299 1712           byte_pos++;
300             } else {
301 2354           dst[byte_pos] |= (val & ((1 << bits_left) - 1)) << (space - bits_left);
302 2354           bit_pos += bits_left;
303 2354           bits_left = 0;
304             }
305             }
306             }
307 107           return byte_pos;
308             }
309              
310             #endif /* HORUS_ENCODE_H */