master ohmu/fastfetch-openbsd / src / common / impl / base64.c
ohmu  ·  2026-10-15
  1#include "common/base64.h"
  2#include "common/endian.h"
  3
  4// https://github.com/kostya/benchmarks/blob/master/base64/test-nolib.c#L145
  5void ffBase64EncodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) {
  6    static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  7    char* out = output;
  8    const char* ends = str + (size - size % 3);
  9    while (str != ends) {
 10        uint32_t n = *(uint32_t*) str;
 11        // The 3 input bytes must be laid out big-endian (str[0] in the most significant position).
 12        n = FF_READ_BE(n);
 13        *out++ = chars[(n >> 26) & 63];
 14        *out++ = chars[(n >> 20) & 63];
 15        *out++ = chars[(n >> 14) & 63];
 16        *out++ = chars[(n >> 8) & 63];
 17        str += 3;
 18    }
 19
 20    // The bytes have to be widened through `uint8_t`: `char` is signed here, so widening a byte
 21    // with the high bit set would sign extend it and the `& 63` below would then read the extension
 22    // instead of the byte's own bits. The whole groups above are immune because they read through a
 23    // `uint32_t` instead.
 24    if (size % 3 == 1) {
 25        uint64_t n = (uint64_t) (uint8_t) *str << 16;
 26        *out++ = chars[(n >> 18) & 63];
 27        *out++ = chars[(n >> 12) & 63];
 28        *out++ = '=';
 29        *out++ = '=';
 30    } else if (size % 3 == 2) {
 31        uint64_t n = (uint64_t) (uint8_t) *str++ << 16;
 32        n |= (uint64_t) (uint8_t) *str << 8;
 33        *out++ = chars[(n >> 18) & 63];
 34        *out++ = chars[(n >> 12) & 63];
 35        *out++ = chars[(n >> 6) & 63];
 36        *out++ = '=';
 37    }
 38    *out = '\0';
 39    *out_size = (uint32_t) (out - output);
 40}
 41
 42static uint8_t decode_table[256];
 43
 44static void init_decode_table() {
 45    uint8_t ch = 0;
 46    do {
 47        int32_t code = -1;
 48        if (ch >= 'A' && ch <= 'Z') {
 49            code = ch - 0x41;
 50        }
 51        if (ch >= 'a' && ch <= 'z') {
 52            code = ch - 0x47;
 53        }
 54        if (ch >= '0' && ch <= '9') {
 55            code = ch + 0x04;
 56        }
 57        if (ch == '+' || ch == '-') {
 58            code = 0x3E;
 59        }
 60        if (ch == '/' || ch == '_') {
 61            code = 0x3F;
 62        }
 63        decode_table[ch] = (uint8_t) code;
 64    } while (ch++ < 0xFF);
 65}
 66
 67#define next_char(x) uint8_t x = decode_table[(uint8_t) *str++];
 68
 69void ffBase64DecodeRaw(uint32_t size, const char* str, uint32_t* out_size, char* output) {
 70    if (*(uint64_t*) decode_table == 0) {
 71        init_decode_table();
 72    }
 73
 74    char* out = output;
 75    while (size > 0 && (str[size - 1] == '\n' || str[size - 1] == '\r' || str[size - 1] == '=')) {
 76        size--;
 77    }
 78
 79    const char* ends = str + size - 4;
 80    while (true) {
 81        if (str > ends) {
 82            break;
 83        }
 84        while (*str == '\n' || *str == '\r') {
 85            str++;
 86        }
 87
 88        if (str > ends) {
 89            break;
 90        }
 91        next_char(a);
 92        next_char(b);
 93        next_char(c);
 94        next_char(d);
 95
 96        *out++ = (char) (a << 2 | b >> 4);
 97        *out++ = (char) (b << 4 | c >> 2);
 98        *out++ = (char) (c << 6 | d >> 0);
 99    }
100
101    uint8_t mod = (uint8_t) (ends - str + 4) % 4;
102    if (mod == 2) {
103        next_char(a);
104        next_char(b);
105        *out++ = (char) (a << 2 | b >> 4);
106    } else if (mod == 3) {
107        next_char(a);
108        next_char(b);
109        next_char(c);
110        *out++ = (char) (a << 2 | b >> 4);
111        *out++ = (char) (b << 4 | c >> 2);
112    }
113
114    *out = '\0';
115    *out_size = (uint32_t) (out - output);
116}