// gcc -Wall -Wextra -Wpedantic -g -fsanitize=address,undefined base32_dec.c -o base32_dec && ./base32_dec

/// Includes

#include <stdint.h>

/// Types

typedef struct
{
    char beg;
    char end;
} Range;

typedef struct
{
    int     bits;
    Range * ranges;
} Encoding;

/// Encodings

// https://datatracker.ietf.org/doc/html/rfc4648#section-6
Encoding base32 = {5, (Range[]){
    {'A', 'Z'},
    {'2', '7'},
    {0},
}};

// https://datatracker.ietf.org/doc/html/rfc4648#section-4
Encoding base64 = {6, (Range[]){
    {'A', 'Z'},
    {'a', 'z'},
    {'0', '9'},
    {'+', '+'},
    {'/', '/'},
    // We need some other sentinel. If the input byte is an (invalid) '\0' this
    // gets treated as any other range and bad things happen. Just switch to
    // C++ and use something like `std::array`?
    {0},
}};

/// Decode

int decode(Encoding const * enc, char const * i_buf, int i_len, char * o_buf)
{
    for (int i_ind = 0; i_ind < i_len; ++i_ind)
    {
        // Input byte.
        // Decode into 5 most significant bits, using the "standard" RFC 4648
        // section 6 alphabet.
        // i = (
        //     ('A' <= i && i <= 'Z') ? i - 'A' + 0:
        //     ('2' <= i && i <= '7') ? i - '2' + ('Z'-'A'+1) :
        //     0
        // ) << 3;
        uint8_t i = i_buf[i_ind];
        for (
            Range * r = enc->ranges;
            i -= r->beg, !(r->beg <= i_buf[i_ind] && i_buf[i_ind] <= r->end);
            i += r->end + 1, ++r
        )
            if (!r->beg)
                return 0;
        i <<= 8 - enc->bits;
        // Number of input bits left to pack.
        int i_bits = enc->bits;
        // Next free output byte and bit.
        int o_ind = i_ind * enc->bits / 8;
        int o_bit = i_ind * enc->bits % 8;
        while (i_bits > 0)
        {
            // Number of free bits in the current output byte.
            int o_bits = 8 - o_bit;
            int m_bits = i_bits < o_bits ? i_bits : o_bits;
            // Clear output bits and set them to the input bits.
            // o_buf[o_ind] &= ~((1 << o_bits) - 1);
            o_buf[o_ind] |= i >> o_bit;
            // Discard the processed bits from the input.
            i_bits  -= m_bits;
            i      <<= m_bits;
            // Proceed to the next output byte.
            o_ind += 1;
            o_bit  = 0;
        }
    }
    return 1;
}

/// Main

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // ||01101 000|01 10010 1|0110 1100|0 11011 00|011 01111||00100 000|01 11011 1|0110 1111|0 11100 10|011 01100||01100 100
    // ||01101|000 01|10010|1 0110|1100 0|11011|00 011|01111||00100|000 01|11011|1 0110|1111 0|11100|10 011|01100||01100|100
    {
        char const i_buf[18] = "NBSWY3DPEB3W64TMMQ"; // "hello world"
        char       o_buf[12] = {0}; // strlen(i_buf) * 5 / 8 + 1
        decode(&base32, i_buf, 18, o_buf);
        puts(o_buf);
    }
    {
        char const i_buf[15] = "\0aGVsbG8gd29ybGQ"; // "hello world"
        char       o_buf[12] = {0}; // strlen(i_buf) * 6 / 8 + 1
        decode(&base64, i_buf, 15, o_buf);
        puts(o_buf);
    }
}