Skip to content

Instantly share code, notes, and snippets.

@marceloalcocer
Last active June 19, 2023 19:06
Show Gist options
  • Save marceloalcocer/1dac66e0893e7d6c3121c294e9f66d9e to your computer and use it in GitHub Desktop.
Save marceloalcocer/1dac66e0893e7d6c3121c294e9f66d9e to your computer and use it in GitHub Desktop.
Lightweight base64 encoder, implemented in C
/* 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