title: src/crelude/base64.c


src/crelude/base64.c

Source code

#include "base64.h"
#include "log.h"

#ifndef IMPLEMENTATION

usize base64_encoded_size(usize original)
{
    usize size = original;
    unless (original % 3 == 0)
        size += 3 - (original % 3);
    size /= 3;
    size *= 4;

    return size;
}

usize base64_decoded_size(MemSlice encoded)
{
    usize size = encoded.len / 4 * 3;

    for (usize i = encoded.len; i-- > 0;)
        if (NTH(encoded, i) == '=') --size;
        else break;

    return size;
}

MemSlice base64_encode(MemSlice data)
{
    if (data.len == 0 || PTR(data) == nil)
        return EMPTY(MemSlice);

    usize len = base64_encoded_size(data.len);
    MemSlice out = SMAKE(umin, len + 1);
    NTH(out, len) = NUL;  //< NUL-terminate the bytes.
    --out.len;  //< Don't *count* NUL.

    for (usize i = 0, j = 0; i < data.len; i += 3, j += 4) {
        usize v = NTH(data, i);
        bool not_two_from_end = i + 2 < data.len;
        bool not_one_from_end = i + 1 < data.len;

        v = v << 8 | NTH(data, i + 1) * not_one_from_end;
        v = v << 8 | NTH(data, i + 2) * not_two_from_end;

        NTH(out, j)     = NTH(BASE64_DIGITS, (v >> 18) & 0x3F);
        NTH(out, j + 1) = NTH(BASE64_DIGITS, (v >> 12) & 0x3F);

        NTH(out, j + 2) = not_one_from_end
            ? NTH(BASE64_DIGITS, (v >> 6) & 0x3F) : '=';
        NTH(out, j + 3) = not_two_from_end
            ? NTH(BASE64_DIGITS,        v & 0x3F) : '=';
    }

    return out;
}

MemSlice base64_decode(MemSlice data)
{
    if (data.len == 0 || PTR(data) == nil)
        return EMPTY(MemSlice);

    if (LAST(data) == NUL)
        --data.len;

    foreach (digit, data)
        unless (is_base64_digit(digit)) {
            LOG(ERROR, "invalid base64 digit: %hhX (%c)", digit, digit);
            return EMPTY(MemSlice);
        }

    usize len = base64_decoded_size(data);
    MemSlice out = SMAKE(umin, len + 1);
    NTH(out, len) = NUL;  //< NUL-terminate the bytes.
    --out.len;  //< Don't *count* NUL.

    for (usize i = 0, j = 0; i < data.len; i += 4, j += 3) {
        // `v` is 4 bytes.  We decode sets of 4 base-64 digits into 3 bytes.
        u32 v = NTH(BASE64_INVERSES, NTH(data, i) - '+');
        v = (v << 6) | NTH(BASE64_INVERSES, NTH(data, i + 1) - '+');

        bool not_2_from_padding = NTH(data, i + 2) != '=';
        bool not_3_from_padding = NTH(data, i + 3) != '=';

        v = v << 6 | NTH(BASE64_INVERSES, NTH(data, i + 2) - '+')
                   * not_2_from_padding;
        v = v << 6 | NTH(BASE64_INVERSES, NTH(data, i + 3) - '+')
                   * not_3_from_padding;

        NTH(out, j) = (v >> 16) & 0xFF;
        if (not_2_from_padding)
            NTH(out, j + 1) = (v >> 8) & 0xFF;
        if (not_3_from_padding)
            NTH(out, j + 2) = v & 0xFF;
    }

    return out;
}

#endif

Updated on 23 August 2022 at 00:54:19 UTC