Skip to content

Instantly share code, notes, and snippets.

@JackyYin
Last active October 22, 2021 08:02
Show Gist options
  • Save JackyYin/de5071a000e603426c0e553624032cc0 to your computer and use it in GitHub Desktop.
Save JackyYin/de5071a000e603426c0e553624032cc0 to your computer and use it in GitHub Desktop.
my base64 implementation
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
# define LIKELY(x) __builtin_expect(!!(x), 1)
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
static char encode_table[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
static int decode_table[128] = {
['A'] = 0,
['B'] = 1,
['C'] = 2,
['D'] = 3,
['E'] = 4,
['F'] = 5,
['G'] = 6,
['H'] = 7,
['I'] = 8,
['J'] = 9,
['K'] = 10,
['L'] = 11,
['M'] = 12,
['N'] = 13,
['O'] = 14,
['P'] = 15,
['Q'] = 16,
['R'] = 17,
['S'] = 18,
['T'] = 19,
['U'] = 20,
['V'] = 21,
['W'] = 22,
['X'] = 23,
['Y'] = 24,
['Z'] = 25,
['a'] = 26,
['b'] = 27,
['c'] = 28,
['d'] = 29,
['e'] = 30,
['f'] = 31,
['g'] = 32,
['h'] = 33,
['i'] = 34,
['j'] = 35,
['k'] = 36,
['l'] = 37,
['m'] = 38,
['n'] = 39,
['o'] = 40,
['p'] = 41,
['q'] = 42,
['r'] = 43,
['s'] = 44,
['t'] = 45,
['u'] = 46,
['v'] = 47,
['w'] = 48,
['x'] = 49,
['y'] = 50,
['z'] = 51,
['0'] = 52,
['1'] = 53,
['2'] = 54,
['3'] = 55,
['4'] = 56,
['5'] = 57,
['6'] = 58,
['7'] = 59,
['8'] = 60,
['9'] = 61,
['+'] = 62,
['/'] = 63
};
// 1 - 4
// 2 - 4
// 3 - 4
// 4 - 8
// 5 - 8
// 6 - 8
size_t get_encode_size(size_t input_len) {
return (input_len / 3) * 4 + (input_len % 3 == 0 ? 0 : 4);
}
bool encode(const uint8_t *input, size_t inlen, uint8_t *output) {
int32_t tmp = 0;
size_t i = 0, j = 0;
while (LIKELY(i + 2 < inlen)) {
tmp = input[i++] << 16;
tmp |= input[i++] << 8;
tmp |= input[i++];
output[j++] = encode_table[(tmp >> 18) & 0x3F];
output[j++] = encode_table[(tmp >> 12) & 0x3F];
output[j++] = encode_table[(tmp >> 6) & 0x3F];
output[j++] = encode_table[(tmp >> 0) & 0x3F];
}
if (i + 2 == inlen) {
tmp = input[i++] << 10;
tmp |= input[i++] << 2;
output[j++] = encode_table[(tmp >> 12) & 0x3F];
output[j++] = encode_table[(tmp >> 6) & 0x3F];
output[j++] = encode_table[(tmp >> 0) & 0x3F];
output[j++] = '=';
} else if (i + 1 == inlen) {
tmp = (input[i++] << 4);
output[j++] = encode_table[(tmp >> 6) & 0x3F];
output[j++] = encode_table[(tmp >> 0) & 0x3F];
output[j++] = '=';
output[j++] = '=';
}
output[j] = '\0';
return true;
}
size_t get_decode_size(const uint8_t *input, size_t input_len) {
size_t sz = (input_len / 4) * 3;
if (input[input_len - 1] == '=') sz--;
if (input[input_len - 2] == '=') sz--;
return sz;
}
bool decode(const uint8_t *input, uint8_t *output) {
size_t input_len = strlen((char*)input);
if (input_len % 4 != 0) {
return false;
}
int32_t tmp = 0;
size_t i = 0, j = 0;
while (LIKELY(i + 4 < input_len)) {
tmp = decode_table[input[i++]] << 18;
tmp |= decode_table[input[i++]] << 12;
tmp |= decode_table[input[i++]] << 6;
tmp |= decode_table[input[i++]] << 0;
output[j++] = (tmp >> 16) & 0xFF;
output[j++] = (tmp >> 8) & 0xFF;
output[j++] = (tmp >> 0) & 0xFF;
}
tmp = decode_table[input[i++]] << 18;
tmp |= decode_table[input[i++]] << 12;
tmp |= decode_table[input[i++]] << 6;
tmp |= decode_table[input[i++]] << 0;
if (input[input_len - 1] == '=') {
if (input[input_len - 2] == '=') {
output[j++] = (tmp >> 16) & 0xFF;
} else {
output[j++] = (tmp >> 16) & 0xFF;
output[j++] = (tmp >> 8) & 0xFF;
}
} else {
output[j++] = (tmp >> 16) & 0xFF;
output[j++] = (tmp >> 8) & 0xFF;
output[j++] = (tmp >> 0) & 0xFF;
}
output[j] = '\0';
return true;
}
void print_usage() {
printf("Usage: ./base64 [-e STRING] [-d STRING] [-f PATH]\n\n");
printf("Example: \n");
printf(" ./base64 -e apple\n\n");
printf("Options: \n");
printf(" -e get encoded string of [STRING]\n");
printf(" -d get decoded string of [STRING]\n");
printf(" -f get encoded string of buffer content in [PATH]\n");
}
int main(int argc, char **argv) {
int opt;
char *res = NULL;
size_t res_len;
while ((opt = getopt(argc, argv, "e:d:f:")) != -1) {
switch (opt) {
case 'e':
res_len = get_encode_size(strlen(optarg));
res= malloc(res_len + 1);
if (UNLIKELY(!res)) {
goto ALLOC_FAILED;
}
if (UNLIKELY(!encode((const uint8_t*)optarg, strlen(optarg), (uint8_t*)res))) {
goto FAILED;
}
goto SUCCESS;
case 'd':
res_len = get_decode_size((const uint8_t*)optarg, strlen(optarg));
res = malloc(res_len + 1);
if (UNLIKELY(!res)) {
goto ALLOC_FAILED;
}
if (UNLIKELY(!decode((const uint8_t*)optarg, (uint8_t*)res))) {
goto FAILED;
}
goto SUCCESS;
case 'f':
{
FILE *fp;
uint8_t *fbuf;
if (UNLIKELY(!(fp = fopen(optarg, "rb")))) {
goto FAILED;
}
fseek(fp, 0L, SEEK_END);
size_t flen = ftell(fp);
fseek(fp, 0L, SEEK_SET);
if (UNLIKELY(!(fbuf = malloc(flen)))) {
goto ALLOC_FAILED;
}
if (UNLIKELY(fread(fbuf, 1, flen, fp) != flen)) {
free(fbuf);
goto FREAD_FAILED;
}
fclose(fp);
res_len = get_encode_size(flen);
res= malloc(res_len + 1);
if (UNLIKELY(!res)) {
goto ALLOC_FAILED;
}
if (UNLIKELY(!encode((const uint8_t*)fbuf, flen, (uint8_t*)res))) {
free(fbuf);
goto FAILED;
}
free(fbuf);
goto SUCCESS;
}
case '?':
default:
goto ARG_ERR;
}
}
ARG_ERR:
print_usage();
return -1;
SUCCESS:
printf("%s\n", res);
free(res);
return 0;
FAILED:
printf("failed to encode/decode...\n");
free(res);
return -1;
FREAD_FAILED:
printf("failed to read from input file...\n");
return -1;
ALLOC_FAILED:
printf("failed to allocate memory...\n");
return -1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment