Check out IETF RFC 4648 for โBase16, Base32, and Base64 Data Codes.โ
Partial Code Criticism:
size_t dl = sizeof(data); printf("sizeof(data):%d\n",dl); float fpl = ((float)dl / 4.0f) * 3.0f; size_t pl = (size_t)(fpl > (float)((int)fpl) ? fpl + 1 : fpl); printf("length of packed data:%d\n",pl);
Do not use floating point things - just use integers. And use "% z" to print the "size_t" values โโ- if you have a C99 library.
size_t pl = ((dl + 3) / 4) * 3;
I think your loop could be simplified by dealing with 3-byte input modules until you have a partial unit left, and then deal with the remainder of 1 or 2 bytes as special cases. I note that the standard mention says that you use one or two "=" signs to fill in at the end.
I have a Base64 encoder and decoding that does some of them. You describe the โdecodedโ part of Base64, where the Base64 code has 4 bytes of data, which should be stored in only 3 - as a packaging code. The Base64 encoder matches the unpacker you need.
Base-64 Decoder
Note: base_64_inv is an array of 256 values, one for each possible input byte value; it determines the correct decoded value for each encoded byte. Base64 encoded is a sparse array - 3/4 zeros. Similarly, base_64_map is a mapping between the value 0..63 and the corresponding storage value.
enum { DC_PAD = -1, DC_ERR = -2 }; static int decode_b64(int c) { int b64 = base_64_inv[c]; if (c == base64_pad) b64 = DC_PAD; else if (b64 == 0 && c != base_64_map[0]) b64 = DC_ERR; return(b64); } static int decode_quad(const char *b64_data, char *bin_data) { int b0 = decode_b64(b64_data[0]); int b1 = decode_b64(b64_data[1]); int b2 = decode_b64(b64_data[2]); int b3 = decode_b64(b64_data[3]); int bytes; if (b0 < 0 || b1 < 0 || b2 == DC_ERR || b3 == DC_ERR || (b2 == DC_PAD && b3 != DC_PAD)) return(B64_ERR_INVALID_ENCODED_DATA); if (b2 == DC_PAD && (b1 & 0x0F) != 0) return(B64_ERR_INVALID_TRAILING_BYTE); if (b2 >= 0 && b3 == DC_PAD && (b2 & 0x03) != 0) return(B64_ERR_INVALID_TRAILING_BYTE); bin_data[0] = (b0 << 2) | (b1 >> 4); bytes = 1; if (b2 >= 0) { bin_data[1] = ((b1 & 0x0F) << 4) | (b2 >> 2); bytes = 2; } if (b3 >= 0) { bin_data[2] = ((b2 & 0x03) << 6) | (b3); bytes = 3; } return(bytes); } int base64_decode(const char *data, size_t datalen, char *buffer, size_t buflen) { size_t outlen = 0; if (datalen % 4 != 0) return(B64_ERR_INVALID_ENCODED_LENGTH); if (BASE64_DECLENGTH(datalen) > buflen) return(B64_ERR_OUTPUT_BUFFER_TOO_SMALL); while (datalen >= 4) { int nbytes = decode_quad(data, buffer + outlen); if (nbytes < 0) return(nbytes); outlen += nbytes; data += 4; datalen -= 4; } assert(datalen == 0); return(outlen); }
Base-64 Encoder
static void encode_triplet(const char *triplet, char *quad) { quad[0] = base_64_map[(triplet[0] >> 2) & 0x3F]; quad[1] = base_64_map[((triplet[0] & 0x03) << 4) | ((triplet[1] >> 4) & 0x0F)]; quad[2] = base_64_map[((triplet[1] & 0x0F) << 2) | ((triplet[2] >> 6) & 0x03)]; quad[3] = base_64_map[triplet[2] & 0x3F]; } static void encode_doublet(const char *doublet, char *quad, char pad) { quad[0] = base_64_map[(doublet[0] >> 2) & 0x3F]; quad[1] = base_64_map[((doublet[0] & 0x03) << 4) | ((doublet[1] >> 4) & 0x0F)]; quad[2] = base_64_map[((doublet[1] & 0x0F) << 2)]; quad[3] = pad; } static void encode_singlet(const char *singlet, char *quad, char pad) { quad[0] = base_64_map[(singlet[0] >> 2) & 0x3F]; quad[1] = base_64_map[((singlet[0] & 0x03) << 4)]; quad[2] = pad; quad[3] = pad; } static int base64_encode_internal(const char *data, size_t datalen, char *buffer, size_t buflen, char pad) { size_t outlen = BASE64_ENCLENGTH(datalen); const char *bin_data = (const void *)data; char *b64_data = (void *)buffer; if (outlen > buflen) return(B64_ERR_OUTPUT_BUFFER_TOO_SMALL); while (datalen >= 3) { encode_triplet(bin_data, b64_data); bin_data += 3; b64_data += 4; datalen -= 3; } b64_data[0] = '\0'; if (datalen == 2) encode_doublet(bin_data, b64_data, pad); else if (datalen == 1) encode_singlet(bin_data, b64_data, pad); b64_data[4] = '\0'; return((b64_data - buffer) + strlen(b64_data)); }
I complicate my life by dealing with a product that uses the alphabet variant for Base64 encoding, and also does not allow processing data - therefore, the argument is "pad" (which can be zero for "zero fill" or "= 'for the standard complement. Array base_64_map contains the alphabet to use for 6-bit values โโin the range 0..63.