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