Last active
June 19, 2023 19:06
-
-
Save marceloalcocer/1dac66e0893e7d6c3121c294e9f66d9e to your computer and use it in GitHub Desktop.
Lightweight base64 encoder, implemented in C
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
/* Base64 encoder ************************************************************* | |
* * | |
* A lightweight base64 encoder, implemented in C. * | |
* * | |
* * | |
* Example usage; * | |
* * | |
* ``` * | |
* void main(void){ * | |
* unsigned char octets[] = { * | |
* 0x4d, 0x61, 0x6e, * | |
* 0x79, 0x20, 0x68, * | |
* 0x61, 0x6e, 0x64, * | |
* 0x73, 0x20 * | |
* }; * | |
* unsigned int n_octets = (sizeof octets)/(sizeof octets[0]); * | |
* unsigned char* sextets = base64((unsigned char*)octets, n_octets); * | |
* printf("%s\n", sextets); * | |
* free(sextets); * | |
* } * | |
* ``` * | |
* * | |
* Output; * | |
* * | |
* ``` * | |
* TWFueSBoYW5kcyA= * | |
* ``` * | |
* * | |
******************************************************************************/ | |
/* Includes *******************************************************************/ | |
#include <stdlib.h> | |
#include <stdio.h> // printf | |
#include <limits.h> // CHAR_BIT | |
/* Macros *********************************************************************/ | |
// Sextet alignment | |
// | |
// Three possible sextet alignments in octets; | |
// | |
// A | B | C Octets | |
// 010011·01 0110·0001 01·101110 | |
// 010011 01·0110 0001·01 101110 | |
// 1 | 2 | 3 | 4 Sextets | |
// | |
#define SEXTET_ALIGNMENT(octet) octet % 3 | |
#define SEXTET_ALIGNMENT_A 0 | |
#define SEXTET_ALIGNMENT_B 1 | |
#define SEXTET_ALIGNMENT_C 2 | |
// Sextet extraction | |
// | |
// To avoid octet array overflow, only ever access current and previous | |
// octet, never next octet. | |
// | |
#define SEXTET_BIT 6 | |
#define SEXTET_MASK 0x3f | |
#define SEXTET_ONE(octet) (*(octet) >> 2) & SEXTET_MASK | |
#define SEXTET_TWO_HI(octet) ((*(octet - 1) << 4) & SEXTET_MASK) | |
#define SEXTET_TWO_LO(octet) ((*(octet) >> 4) & SEXTET_MASK) | |
#define SEXTET_TWO(octet) SEXTET_TWO_HI(octet) | SEXTET_TWO_LO(octet) | |
#define SEXTET_THREE_HI(octet) ((*((octet) - 1) << 2) & SEXTET_MASK) | |
#define SEXTET_THREE_LO(octet) ((*(octet) >> SEXTET_BIT) & SEXTET_MASK) | |
#define SEXTET_THREE(octet) SEXTET_THREE_HI(octet) | SEXTET_THREE_LO(octet) | |
#define SEXTET_FOUR(octet) *(octet) & SEXTET_MASK | |
// Encoding | |
#define BASE64_LUT { \ | |
/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ \ | |
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 0x00 */ \ | |
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', /* 0x10 */ \ | |
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 0x20 */ \ | |
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', /* 0x30 */ \ | |
}; | |
#define BASE64_PADDING '=' | |
/* Functions ******************************************************************/ | |
// Base64 encode | |
// | |
// @param octets Array to be encoded | |
// @param n_octets Number of elements in array | |
// | |
// @return Base64 encoded array | |
// | |
unsigned char* base64(unsigned char* octets, unsigned int n_octets){ | |
// Allocate sextet array | |
unsigned int n_bits = CHAR_BIT * n_octets; | |
unsigned int n_sextets = n_bits / SEXTET_BIT; | |
unsigned char n_padding; | |
switch(n_bits % SEXTET_BIT){ | |
case 0: | |
n_padding = 0; | |
break; | |
case 2: | |
n_padding = 2; | |
n_sextets++; | |
break; | |
case 4: | |
n_padding = 1; | |
n_sextets++; | |
break; | |
default: | |
printf("Unexpected sextet alignment\n"); | |
return NULL; | |
} | |
unsigned char* sextets = malloc( | |
n_sextets | |
+ n_padding | |
+ 1 // String termination | |
); | |
if(!sextets){ | |
printf("Memory allocation failed\n"); | |
return NULL; | |
} | |
// Encode sextets | |
unsigned char lut[] = BASE64_LUT; | |
unsigned int i_octets; | |
for(i_octets = 0; i_octets < n_octets; i_octets++){ | |
switch(SEXTET_ALIGNMENT(i_octets)){ | |
case SEXTET_ALIGNMENT_A: | |
*sextets++ = lut[SEXTET_ONE(octets + i_octets)]; | |
break; | |
case SEXTET_ALIGNMENT_B: | |
*sextets++ = lut[SEXTET_TWO(octets + i_octets)]; | |
break; | |
case SEXTET_ALIGNMENT_C: | |
*sextets++ = lut[SEXTET_THREE(octets + i_octets)]; | |
*sextets++ = lut[SEXTET_FOUR(octets + i_octets)]; | |
break; | |
} | |
} | |
// Encode final sextet | |
switch(SEXTET_ALIGNMENT(i_octets)){ // N.b. incremented i_octets | |
case SEXTET_ALIGNMENT_A: | |
break; | |
case SEXTET_ALIGNMENT_B: | |
*sextets++ = lut[SEXTET_TWO_HI(octets + i_octets)]; | |
*sextets++ = BASE64_PADDING; | |
*sextets++ = BASE64_PADDING; | |
break; | |
case SEXTET_ALIGNMENT_C: | |
*sextets++ = lut[SEXTET_THREE_HI(octets + i_octets)]; | |
*sextets++ = BASE64_PADDING; | |
break; | |
} | |
// String termination | |
*sextets = '\0'; | |
// Return | |
return ( | |
sextets | |
- (n_sextets + n_padding) // Decrement to array start | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment