Last active
October 22, 2021 08:02
-
-
Save JackyYin/de5071a000e603426c0e553624032cc0 to your computer and use it in GitHub Desktop.
my base64 implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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