// 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); } }