4.5.1 Problem
You want to represent binary data in as
compact a textual representation as is reasonable, but the data must
be easy to encode and decode, and it must use printable text
characters.
4.5.2 Solution
Base64 encoding encodes six bits of
data at a time, meaning that every six bits of input map to one
character of output. The characters in the output will be a numeric
digit, a letter (uppercase or lowercase), a forward slash, a plus, or
the equal sign (which is a special padding character).
Note that four output characters map exactly to three input
characters. As a result, if the input string isn't a
multiple of three characters, you'll need to do some
padding (explained in
Section 4.5.3).
4.5.3 Discussion
The base64 alphabet takes 6-bit binary values representing numbers
from 0 to 63 and maps them to a set of printable
ASCII characters. The values 0 through 25
map to the uppercase letters in order. The values 26 through 51 map
to the lowercase letters. Then come the decimal digits from 0 to 9,
and finally + and /.
If the length of the input string isn't a multiple
of three bytes, the leftover bits are padded to a multiple of six
with zeros; then the last character is encoded. If only one byte
would have been needed in the input to make it a multiple of three,
the pad character (=) is added to the end of the string.
Otherwise, two pad characters are added.
#include <stdlib.h>
static char b64table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* Accepts a binary buffer with an associated size.
* Returns a base64 encoded, NULL-terminated string.
*/
unsigned char *spc_base64_encode(unsigned char *input, size_t len, int wrap) {
unsigned char *output, *p;
size_t i = 0, mod = len % 3, toalloc;
toalloc = (len / 3) * 4 + (3 - mod) % 3 + 1;
if (wrap) {
toalloc += len / 57;
if (len % 57) toalloc++;
}
p = output = (unsigned char *)malloc(((len / 3) + (mod ? 1 : 0)) * 4 + 1);
if (!p) return 0;
while (i < len - mod) {
*p++ = b64table[input[i++] >> 2];
*p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];
*p++ = b64table[((input[i] << 2) | (input[i + 1] >> 6)) & 0x3f];
*p++ = b64table[input[i + 1] & 0x3f];
i += 2;
if (wrap && !(i % 57)) *p++ = '\n';
}
if (!mod) {
if (wrap && i % 57) *p++ = '\n';
*p = 0;
return output;
} else {
*p++ = b64table[input[i++] >> 2];
*p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];
if (mod = = 1) {
*p++ = '=';
*p++ = '=';
if (wrap) *p++ = '\n';
*p = 0;
return output;
} else {
*p++ = b64table[(input[i] << 2) & 0x3f];
*p++ = '=';
if (wrap) *p++ = '\n';
*p = 0;
return output;
}
}
}
The
public interface to
the above code is the following:
unsigned char *spc base64_encode(unsigned char *input, size_t len, int wrap);
The result is a NULL-terminated string allocated
internally via malloc( ). Some protocols may
expect you to "wrap" base64-encoded
data so that, when printed, it takes up less than 80 columns. If such
behavior is necessary, you can pass in a non-zero value for the final
parameter, which will cause this code to insert newlines once every
76 characters. In that case, the string will always end with a
newline (followed by the expected
NULL-terminator).
If the call to malloc( ) fails because there is no
memory, this function returns 0.
4.5.4 See Also
Recipe 4.6
