#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/*
Base‑36 encoding/decoding in C
----------------------------------------
Base‑36 digits: 0–9, A–Z
Encoding:
- Convert string → big integer (base‑256)
- Convert big integer → base‑36
Decoding:
- Convert base‑36 → big integer
- Convert big integer → original bytes
We store the big integer as an array of 32‑bit chunks (little‑endian).
*/
static const char BASE36_DIGITS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/* Convert Base‑36 character → numeric value */
static int base36_value(char c) {
if (c >= '0' && c <= '9') return c - '0';
return c - 'A' + 10;
}
/*
encode_to_base36:
-----------------
Converts a byte string into Base‑36 text.
Uses manual big‑integer arithmetic:
number[j] holds a 32‑bit chunk of the integer.
*/
char *encode_to_base36(const char *input) {
size_t len = strlen(input);
/* Big integer buffer */
size_t cap = len + 4;
uint32_t *number = calloc(cap, sizeof(uint32_t));
size_t nsize = 1;
/* Step 1: Convert string → big integer (base‑256) */
for (size_t i = 0; i < len; i++) {
uint32_t carry = (unsigned char)input[i];
for (size_t j = 0; j < nsize; j++) {
uint64_t value = (uint64_t)number[j] * 256 + carry;
number[j] = (uint32_t)(value & 0xFFFFFFFFu);
carry = (uint32_t)(value >> 32);
}
if (carry > 0)
number[nsize++] = carry;
}
/* Step 2: Convert big integer → Base‑36 */
char *encoded = malloc(len * 3 + 32);
size_t epos = 0;
while (!(nsize == 1 && number[0] == 0)) {
uint32_t remainder = 0;
/* Divide big integer by 36 */
for (size_t j = nsize; j-- > 0;) {
uint64_t value = ((uint64_t)remainder << 32) | number[j];
number[j] = (uint32_t)(value / 36);
remainder = (uint32_t)(value % 36);
}
/* Remove leading zero chunks */
while (nsize > 1 && number[nsize - 1] == 0)
nsize--;
encoded[epos++] = BASE36_DIGITS[remainder];
}
/* Reverse the Base‑36 output */
for (size_t i = 0; i < epos / 2; i++) {
char tmp = encoded[i];
encoded[i] = encoded[epos - 1 - i];
encoded[epos - 1 - i] = tmp;
}
encoded[epos] = '\0';
free(number);
return encoded;
}
/*
decode_from_base36:
-------------------
Converts Base‑36 text back into the original string.
Steps:
1. Convert Base‑36 → big integer
2. Convert big integer → bytes (base‑256)
*/
char *decode_from_base36(const char *encoded) {
size_t len = strlen(encoded);
/* Big integer buffer */
uint32_t *number = calloc(len + 4, sizeof(uint32_t));
size_t nsize = 1;
/* Step 1: Base‑36 → big integer */
for (size_t i = 0; i < len; i++) {
int digit = base36_value(encoded[i]);
uint32_t carry = digit;
for (size_t j = 0; j < nsize; j++) {
uint64_t value = (uint64_t)number[j] * 36 + carry;
number[j] = (uint32_t)(value & 0xFFFFFFFFu);
carry = (uint32_t)(value >> 32);
}
if (carry > 0)
number[nsize++] = carry;
}
/* Step 2: big integer → original bytes */
char *output = malloc(len + 1);
size_t opos = 0;
while (!(nsize == 1 && number[0] == 0)) {
uint32_t remainder = 0;
/* Divide big integer by 256 */
for (size_t j = nsize; j-- > 0;) {
uint64_t value = ((uint64_t)remainder << 32) | number[j];
number[j] = (uint32_t)(value / 256);
remainder = (uint32_t)(value % 256);
}
while (nsize > 1 && number[nsize - 1] == 0)
nsize--;
output[opos++] = (char)remainder;
}
/* Reverse the byte output */
for (size_t i = 0; i < opos / 2; i++) {
char tmp = output[i];
output[i] = output[opos - 1 - i];
output[opos - 1 - i] = tmp;
}
output[opos] = '\0';
free(number);
return output;
}
int main(void) {
const char *text = "Hello Universe!";
char *encoded = encode_to_base36(text);
char *decoded = decode_from_base36(encoded);
printf("Original: %s\n", text);
printf("Base-36 encoded: %s\n", encoded);
printf("Decoded: %s\n", decoded);
free(encoded);
free(decoded);
}
/*
run:
Original: Hello Universe!
Base-36 encoded: LP4N024HJ1YVBVD84Y8TYQP
Decoded: Hello Universe!
*/