chiark / gitweb /
b3ea05ce53e6d1b315edd429ee62027242d5aa6d
[elogind.git] / src / basic / hexdecoct.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6 ***/
7
8 #include <ctype.h>
9 #include <errno.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12
13 #include "alloc-util.h"
14 #include "hexdecoct.h"
15 #include "macro.h"
16 #include "string-util.h"
17 #include "util.h"
18
19 char octchar(int x) {
20         return '0' + (x & 7);
21 }
22
23 int unoctchar(char c) {
24
25         if (c >= '0' && c <= '7')
26                 return c - '0';
27
28         return -EINVAL;
29 }
30
31 char decchar(int x) {
32         return '0' + (x % 10);
33 }
34
35 int undecchar(char c) {
36
37         if (c >= '0' && c <= '9')
38                 return c - '0';
39
40         return -EINVAL;
41 }
42
43 char hexchar(int x) {
44         static const char table[16] = "0123456789abcdef";
45
46         return table[x & 15];
47 }
48
49 int unhexchar(char c) {
50
51         if (c >= '0' && c <= '9')
52                 return c - '0';
53
54         if (c >= 'a' && c <= 'f')
55                 return c - 'a' + 10;
56
57         if (c >= 'A' && c <= 'F')
58                 return c - 'A' + 10;
59
60         return -EINVAL;
61 }
62
63 char *hexmem(const void *p, size_t l) {
64         const uint8_t *x;
65         char *r, *z;
66
67         z = r = new(char, l * 2 + 1);
68         if (!r)
69                 return NULL;
70
71         for (x = p; x < (const uint8_t*) p + l; x++) {
72                 *(z++) = hexchar(*x >> 4);
73                 *(z++) = hexchar(*x & 15);
74         }
75
76         *z = 0;
77         return r;
78 }
79
80 static int unhex_next(const char **p, size_t *l) {
81         int r;
82
83         assert(p);
84         assert(l);
85
86         /* Find the next non-whitespace character, and decode it. We
87          * greedily skip all preceeding and all following whitespace. */
88
89         for (;;) {
90                 if (*l == 0)
91                         return -EPIPE;
92
93                 if (!strchr(WHITESPACE, **p))
94                         break;
95
96                 /* Skip leading whitespace */
97                 (*p)++, (*l)--;
98         }
99
100         r = unhexchar(**p);
101         if (r < 0)
102                 return r;
103
104         for (;;) {
105                 (*p)++, (*l)--;
106
107                 if (*l == 0 || !strchr(WHITESPACE, **p))
108                         break;
109
110                 /* Skip following whitespace */
111         }
112
113         return r;
114 }
115
116 int unhexmem(const char *p, size_t l, void **ret, size_t *ret_len) {
117         _cleanup_free_ uint8_t *buf = NULL;
118         const char *x;
119         uint8_t *z;
120
121         assert(ret);
122         assert(ret_len);
123         assert(p || l == 0);
124
125         if (l == (size_t) -1)
126                 l = strlen(p);
127
128         /* Note that the calculation of memory size is an upper boundary, as we ignore whitespace while decoding */
129         buf = malloc((l + 1) / 2 + 1);
130         if (!buf)
131                 return -ENOMEM;
132
133         for (x = p, z = buf;;) {
134                 int a, b;
135
136                 a = unhex_next(&x, &l);
137                 if (a == -EPIPE) /* End of string */
138                         break;
139                 if (a < 0)
140                         return a;
141
142                 b = unhex_next(&x, &l);
143                 if (b < 0)
144                         return b;
145
146                 *(z++) = (uint8_t) a << 4 | (uint8_t) b;
147         }
148
149         *z = 0;
150
151         *ret_len = (size_t) (z - buf);
152         *ret = TAKE_PTR(buf);
153
154         return 0;
155 }
156
157 /* https://tools.ietf.org/html/rfc4648#section-6
158  * Notice that base32hex differs from base32 in the alphabet it uses.
159  * The distinction is that the base32hex representation preserves the
160  * order of the underlying data when compared as bytestrings, this is
161  * useful when representing NSEC3 hashes, as one can then verify the
162  * order of hashes directly from their representation. */
163 char base32hexchar(int x) {
164         static const char table[32] = "0123456789"
165                                       "ABCDEFGHIJKLMNOPQRSTUV";
166
167         return table[x & 31];
168 }
169
170 int unbase32hexchar(char c) {
171         unsigned offset;
172
173         if (c >= '0' && c <= '9')
174                 return c - '0';
175
176         offset = '9' - '0' + 1;
177
178         if (c >= 'A' && c <= 'V')
179                 return c - 'A' + offset;
180
181         return -EINVAL;
182 }
183
184 char *base32hexmem(const void *p, size_t l, bool padding) {
185         char *r, *z;
186         const uint8_t *x;
187         size_t len;
188
189         assert(p || l == 0);
190
191         if (padding)
192                 /* five input bytes makes eight output bytes, padding is added so we must round up */
193                 len = 8 * (l + 4) / 5;
194         else {
195                 /* same, but round down as there is no padding */
196                 len = 8 * l / 5;
197
198                 switch (l % 5) {
199                 case 4:
200                         len += 7;
201                         break;
202                 case 3:
203                         len += 5;
204                         break;
205                 case 2:
206                         len += 4;
207                         break;
208                 case 1:
209                         len += 2;
210                         break;
211                 }
212         }
213
214         z = r = malloc(len + 1);
215         if (!r)
216                 return NULL;
217
218         for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) {
219                 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ
220                  * x[3] == QQQQQQQQ; x[4] == WWWWWWWW */
221                 *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
222                 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
223                 *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
224                 *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);  /* 000YZZZZ */
225                 *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
226                 *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
227                 *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5);  /* 000QQWWW */
228                 *(z++) = base32hexchar((x[4] & 31));                  /* 000WWWWW */
229         }
230
231         switch (l % 5) {
232         case 4:
233                 *(z++) = base32hexchar(x[0] >> 3);                    /* 000XXXXX */
234                 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6);  /* 000XXXYY */
235                 *(z++) = base32hexchar((x[1] & 63) >> 1);             /* 000YYYYY */
236                 *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4);   /* 000YZZZZ */
237                 *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */
238                 *(z++) = base32hexchar((x[3] & 127) >> 2);            /* 000QQQQQ */
239                 *(z++) = base32hexchar((x[3] & 3) << 3);              /* 000QQ000 */
240                 if (padding)
241                         *(z++) = '=';
242
243                 break;
244
245         case 3:
246                 *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
247                 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
248                 *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
249                 *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */
250                 *(z++) = base32hexchar((x[2] & 15) << 1);            /* 000ZZZZ0 */
251                 if (padding) {
252                         *(z++) = '=';
253                         *(z++) = '=';
254                         *(z++) = '=';
255                 }
256
257                 break;
258
259         case 2:
260                 *(z++) = base32hexchar(x[0] >> 3);                   /* 000XXXXX */
261                 *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */
262                 *(z++) = base32hexchar((x[1] & 63) >> 1);            /* 000YYYYY */
263                 *(z++) = base32hexchar((x[1] & 1) << 4);             /* 000Y0000 */
264                 if (padding) {
265                         *(z++) = '=';
266                         *(z++) = '=';
267                         *(z++) = '=';
268                         *(z++) = '=';
269                 }
270
271                 break;
272
273         case 1:
274                 *(z++) = base32hexchar(x[0] >> 3);       /* 000XXXXX */
275                 *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */
276                 if (padding) {
277                         *(z++) = '=';
278                         *(z++) = '=';
279                         *(z++) = '=';
280                         *(z++) = '=';
281                         *(z++) = '=';
282                         *(z++) = '=';
283                 }
284
285                 break;
286         }
287
288         *z = 0;
289         return r;
290 }
291
292 int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) {
293         _cleanup_free_ uint8_t *r = NULL;
294         int a, b, c, d, e, f, g, h;
295         uint8_t *z;
296         const char *x;
297         size_t len;
298         unsigned pad = 0;
299
300         assert(p || l == 0);
301         assert(mem);
302         assert(_len);
303
304         if (l == (size_t) -1)
305                 l = strlen(p);
306
307         /* padding ensures any base32hex input has input divisible by 8 */
308         if (padding && l % 8 != 0)
309                 return -EINVAL;
310
311         if (padding) {
312                 /* strip the padding */
313                 while (l > 0 && p[l - 1] == '=' && pad < 7) {
314                         pad++;
315                         l--;
316                 }
317         }
318
319         /* a group of eight input bytes needs five output bytes, in case of
320          * padding we need to add some extra bytes */
321         len = (l / 8) * 5;
322
323         switch (l % 8) {
324         case 7:
325                 len += 4;
326                 break;
327         case 5:
328                 len += 3;
329                 break;
330         case 4:
331                 len += 2;
332                 break;
333         case 2:
334                 len += 1;
335                 break;
336         case 0:
337                 break;
338         default:
339                 return -EINVAL;
340         }
341
342         z = r = malloc(len + 1);
343         if (!r)
344                 return -ENOMEM;
345
346         for (x = p; x < p + (l / 8) * 8; x += 8) {
347                 /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW
348                  * e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */
349                 a = unbase32hexchar(x[0]);
350                 if (a < 0)
351                         return -EINVAL;
352
353                 b = unbase32hexchar(x[1]);
354                 if (b < 0)
355                         return -EINVAL;
356
357                 c = unbase32hexchar(x[2]);
358                 if (c < 0)
359                         return -EINVAL;
360
361                 d = unbase32hexchar(x[3]);
362                 if (d < 0)
363                         return -EINVAL;
364
365                 e = unbase32hexchar(x[4]);
366                 if (e < 0)
367                         return -EINVAL;
368
369                 f = unbase32hexchar(x[5]);
370                 if (f < 0)
371                         return -EINVAL;
372
373                 g = unbase32hexchar(x[6]);
374                 if (g < 0)
375                         return -EINVAL;
376
377                 h = unbase32hexchar(x[7]);
378                 if (h < 0)
379                         return -EINVAL;
380
381                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
382                 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
383                 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
384                 *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
385                 *(z++) = (uint8_t) g << 5 | (uint8_t) h;                         /* VVVRRRRR */
386         }
387
388         switch (l % 8) {
389         case 7:
390                 a = unbase32hexchar(x[0]);
391                 if (a < 0)
392                         return -EINVAL;
393
394                 b = unbase32hexchar(x[1]);
395                 if (b < 0)
396                         return -EINVAL;
397
398                 c = unbase32hexchar(x[2]);
399                 if (c < 0)
400                         return -EINVAL;
401
402                 d = unbase32hexchar(x[3]);
403                 if (d < 0)
404                         return -EINVAL;
405
406                 e = unbase32hexchar(x[4]);
407                 if (e < 0)
408                         return -EINVAL;
409
410                 f = unbase32hexchar(x[5]);
411                 if (f < 0)
412                         return -EINVAL;
413
414                 g = unbase32hexchar(x[6]);
415                 if (g < 0)
416                         return -EINVAL;
417
418                 /* g == 000VV000 */
419                 if (g & 7)
420                         return -EINVAL;
421
422                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
423                 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
424                 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
425                 *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */
426
427                 break;
428         case 5:
429                 a = unbase32hexchar(x[0]);
430                 if (a < 0)
431                         return -EINVAL;
432
433                 b = unbase32hexchar(x[1]);
434                 if (b < 0)
435                         return -EINVAL;
436
437                 c = unbase32hexchar(x[2]);
438                 if (c < 0)
439                         return -EINVAL;
440
441                 d = unbase32hexchar(x[3]);
442                 if (d < 0)
443                         return -EINVAL;
444
445                 e = unbase32hexchar(x[4]);
446                 if (e < 0)
447                         return -EINVAL;
448
449                 /* e == 000SSSS0 */
450                 if (e & 1)
451                         return -EINVAL;
452
453                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
454                 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
455                 *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1;                    /* WWWWSSSS */
456
457                 break;
458         case 4:
459                 a = unbase32hexchar(x[0]);
460                 if (a < 0)
461                         return -EINVAL;
462
463                 b = unbase32hexchar(x[1]);
464                 if (b < 0)
465                         return -EINVAL;
466
467                 c = unbase32hexchar(x[2]);
468                 if (c < 0)
469                         return -EINVAL;
470
471                 d = unbase32hexchar(x[3]);
472                 if (d < 0)
473                         return -EINVAL;
474
475                 /* d == 000W0000 */
476                 if (d & 15)
477                         return -EINVAL;
478
479                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2;                    /* XXXXXYYY */
480                 *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */
481
482                 break;
483         case 2:
484                 a = unbase32hexchar(x[0]);
485                 if (a < 0)
486                         return -EINVAL;
487
488                 b = unbase32hexchar(x[1]);
489                 if (b < 0)
490                         return -EINVAL;
491
492                 /* b == 000YYY00 */
493                 if (b & 3)
494                         return -EINVAL;
495
496                 *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */
497
498                 break;
499         case 0:
500                 break;
501         default:
502                 return -EINVAL;
503         }
504
505         *z = 0;
506
507         *mem = TAKE_PTR(r);
508         *_len = len;
509
510         return 0;
511 }
512
513 /* https://tools.ietf.org/html/rfc4648#section-4 */
514 char base64char(int x) {
515         static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
516                                       "abcdefghijklmnopqrstuvwxyz"
517                                       "0123456789+/";
518         return table[x & 63];
519 }
520
521 int unbase64char(char c) {
522         unsigned offset;
523
524         if (c >= 'A' && c <= 'Z')
525                 return c - 'A';
526
527         offset = 'Z' - 'A' + 1;
528
529         if (c >= 'a' && c <= 'z')
530                 return c - 'a' + offset;
531
532         offset += 'z' - 'a' + 1;
533
534         if (c >= '0' && c <= '9')
535                 return c - '0' + offset;
536
537         offset += '9' - '0' + 1;
538
539         if (c == '+')
540                 return offset;
541
542         offset++;
543
544         if (c == '/')
545                 return offset;
546
547         return -EINVAL;
548 }
549
550 ssize_t base64mem(const void *p, size_t l, char **out) {
551         char *r, *z;
552         const uint8_t *x;
553
554         assert(p || l == 0);
555         assert(out);
556
557         /* three input bytes makes four output bytes, padding is added so we must round up */
558         z = r = malloc(4 * (l + 2) / 3 + 1);
559         if (!r)
560                 return -ENOMEM;
561
562         for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) {
563                 /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */
564                 *(z++) = base64char(x[0] >> 2);                    /* 00XXXXXX */
565                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4);  /* 00XXYYYY */
566                 *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */
567                 *(z++) = base64char(x[2] & 63);                    /* 00ZZZZZZ */
568         }
569
570         switch (l % 3) {
571         case 2:
572                 *(z++) = base64char(x[0] >> 2);                   /* 00XXXXXX */
573                 *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */
574                 *(z++) = base64char((x[1] & 15) << 2);            /* 00YYYY00 */
575                 *(z++) = '=';
576
577                 break;
578         case 1:
579                 *(z++) = base64char(x[0] >> 2);        /* 00XXXXXX */
580                 *(z++) = base64char((x[0] & 3) << 4);  /* 00XX0000 */
581                 *(z++) = '=';
582                 *(z++) = '=';
583
584                 break;
585         }
586
587         *z = 0;
588         *out = r;
589         return z - r;
590 }
591
592 static int base64_append_width(
593                 char **prefix, int plen,
594                 const char *sep, int indent,
595                 const void *p, size_t l,
596                 int width) {
597
598         _cleanup_free_ char *x = NULL;
599         char *t, *s;
600         ssize_t slen, len, avail;
601         int line, lines;
602
603         len = base64mem(p, l, &x);
604         if (len <= 0)
605                 return len;
606
607         lines = DIV_ROUND_UP(len, width);
608
609         slen = strlen_ptr(sep);
610         t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines);
611         if (!t)
612                 return -ENOMEM;
613
614         memcpy_safe(t + plen, sep, slen);
615
616         for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) {
617                 int act = MIN(width, avail);
618
619                 if (line > 0 || sep) {
620                         memset(s, ' ', indent);
621                         s += indent;
622                 }
623
624                 memcpy(s, x + width * line, act);
625                 s += act;
626                 *(s++) = line < lines - 1 ? '\n' : '\0';
627                 avail -= act;
628         }
629         assert(avail == 0);
630
631         *prefix = t;
632         return 0;
633 }
634
635 int base64_append(
636                 char **prefix, int plen,
637                 const void *p, size_t l,
638                 int indent, int width) {
639
640         if (plen > width / 2 || plen + indent > width)
641                 /* leave indent on the left, keep last column free */
642                 return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
643         else
644                 /* leave plen on the left, keep last column free */
645                 return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
646 }
647
648 static int unbase64_next(const char **p, size_t *l) {
649         int ret;
650
651         assert(p);
652         assert(l);
653
654         /* Find the next non-whitespace character, and decode it. If we find padding, we return it as INT_MAX. We
655          * greedily skip all preceeding and all following whitespace. */
656
657         for (;;) {
658                 if (*l == 0)
659                         return -EPIPE;
660
661                 if (!strchr(WHITESPACE, **p))
662                         break;
663
664                 /* Skip leading whitespace */
665                 (*p)++, (*l)--;
666         }
667
668         if (**p == '=')
669                 ret = INT_MAX; /* return padding as INT_MAX */
670         else {
671                 ret = unbase64char(**p);
672                 if (ret < 0)
673                         return ret;
674         }
675
676         for (;;) {
677                 (*p)++, (*l)--;
678
679                 if (*l == 0)
680                         break;
681                 if (!strchr(WHITESPACE, **p))
682                         break;
683
684                 /* Skip following whitespace */
685         }
686
687         return ret;
688 }
689
690 int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
691         _cleanup_free_ uint8_t *buf = NULL;
692         const char *x;
693         uint8_t *z;
694         size_t len;
695
696         assert(p || l == 0);
697         assert(ret);
698         assert(ret_size);
699
700         if (l == (size_t) -1)
701                 l = strlen(p);
702
703         /* A group of four input bytes needs three output bytes, in case of padding we need to add two or three extra
704          * bytes. Note that this calculation is an upper boundary, as we ignore whitespace while decoding */
705         len = (l / 4) * 3 + (l % 4 != 0 ? (l % 4) - 1 : 0);
706
707         buf = malloc(len + 1);
708         if (!buf)
709                 return -ENOMEM;
710
711         for (x = p, z = buf;;) {
712                 int a, b, c, d; /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */
713
714                 a = unbase64_next(&x, &l);
715                 if (a == -EPIPE) /* End of string */
716                         break;
717                 if (a < 0)
718                         return a;
719                 if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */
720                         return -EINVAL;
721
722                 b = unbase64_next(&x, &l);
723                 if (b < 0)
724                         return b;
725                 if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */
726                         return -EINVAL;
727
728                 c = unbase64_next(&x, &l);
729                 if (c < 0)
730                         return c;
731
732                 d = unbase64_next(&x, &l);
733                 if (d < 0)
734                         return d;
735
736                 if (c == INT_MAX) { /* Padding at the third character */
737
738                         if (d != INT_MAX) /* If the third character is padding, the fourth must be too */
739                                 return -EINVAL;
740
741                         /* b == 00YY0000 */
742                         if (b & 15)
743                                 return -EINVAL;
744
745                         if (l > 0) /* Trailing rubbish? */
746                                 return -ENAMETOOLONG;
747
748                         *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */
749                         break;
750                 }
751
752                 if (d == INT_MAX) {
753                         /* c == 00ZZZZ00 */
754                         if (c & 3)
755                                 return -EINVAL;
756
757                         if (l > 0) /* Trailing rubbish? */
758                                 return -ENAMETOOLONG;
759
760                         *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
761                         *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
762                         break;
763                 }
764
765                 *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */
766                 *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */
767                 *(z++) = (uint8_t) c << 6 | (uint8_t) d;      /* ZZWWWWWW */
768         }
769
770         *z = 0;
771
772         *ret_size = (size_t) (z - buf);
773         *ret = TAKE_PTR(buf);
774
775         return 0;
776 }
777
778 void hexdump(FILE *f, const void *p, size_t s) {
779         const uint8_t *b = p;
780         unsigned n = 0;
781
782         assert(b || s == 0);
783
784         if (!f)
785                 f = stdout;
786
787         while (s > 0) {
788                 size_t i;
789
790                 fprintf(f, "%04x  ", n);
791
792                 for (i = 0; i < 16; i++) {
793
794                         if (i >= s)
795                                 fputs("   ", f);
796                         else
797                                 fprintf(f, "%02x ", b[i]);
798
799                         if (i == 7)
800                                 fputc(' ', f);
801                 }
802
803                 fputc(' ', f);
804
805                 for (i = 0; i < 16; i++) {
806
807                         if (i >= s)
808                                 fputc(' ', f);
809                         else
810                                 fputc(isprint(b[i]) ? (char) b[i] : '.', f);
811                 }
812
813                 fputc('\n', f);
814
815                 if (s < 16)
816                         break;
817
818                 n += 16;
819                 b += 16;
820                 s -= 16;
821         }
822 }