#include #include #include #include "libbase64.h" #include "codecs.h" BASE64_ENC_FUNCTION(plain) { // Assume that *out is large enough to contain the output. // Theoretically it should be 4/3 the length of src. const uint8_t *c = (const uint8_t *)src; uint8_t *o = (uint8_t *)out; // Use local temporaries to avoid cache thrashing: size_t outl = 0; struct base64_state st; st.bytes = state->bytes; st.carry = state->carry; // Turn three bytes into four 6-bit numbers: // in[0] = 00111111 // in[1] = 00112222 // in[2] = 00222233 // in[3] = 00333333 // Duff's device, a for() loop inside a switch() statement. Legal! switch (st.bytes) { for (;;) { case 0: // If we have 64-bit ints, pick off 6 bytes at a time for as long as we can, // but ensure that there are at least 8 bytes available to avoid segfaulting: while (srclen >= 8) { // Load string: uint64_t str = *(uint64_t *)c; // Reorder to 64-bit big-endian, if not already in that format. The // workset must be in big-endian, otherwise the shifted bits do not // carry over properly among adjacent bytes: str = cpu_to_be64(str); // Shift input by 6 bytes each round and mask in only the lower 6 bits; // look up the character in the Base64 encoding table and write it to // the output location: *o++ = base64_table_enc[(str >> 58) & 0x3F]; *o++ = base64_table_enc[(str >> 52) & 0x3F]; *o++ = base64_table_enc[(str >> 46) & 0x3F]; *o++ = base64_table_enc[(str >> 40) & 0x3F]; *o++ = base64_table_enc[(str >> 34) & 0x3F]; *o++ = base64_table_enc[(str >> 28) & 0x3F]; *o++ = base64_table_enc[(str >> 22) & 0x3F]; *o++ = base64_table_enc[(str >> 16) & 0x3F]; c += 6; // 6 bytes of input outl += 8; // 8 bytes of output srclen -= 6; } if (srclen-- == 0) { break; } *o++ = base64_table_enc[*c >> 2]; st.carry = (*c++ << 4) & 0x30; st.bytes++; outl += 1; // Deliberate fallthrough: BASE64_FALLTHROUGH case 1: if (srclen-- == 0) { break; } *o++ = base64_table_enc[st.carry | (*c >> 4)]; st.carry = (*c++ << 2) & 0x3C; st.bytes++; outl += 1; // Deliberate fallthrough: BASE64_FALLTHROUGH case 2: if (srclen-- == 0) { break; } *o++ = base64_table_enc[st.carry | (*c >> 6)]; *o++ = base64_table_enc[*c++ & 0x3F]; st.bytes = 0; outl += 2; } } state->bytes = st.bytes; state->carry = st.carry; *outlen = outl; } BASE64_DEC_FUNCTION(plain) { int ret = 0; const uint8_t *c = (const uint8_t *)src; uint8_t *o = (uint8_t *)out; uint8_t q; // Use local temporaries to avoid cache thrashing: size_t outl = 0; struct base64_state st; st.eof = state->eof; st.bytes = state->bytes; st.carry = state->carry; // If we previously saw an EOF or an invalid character, bail out: if (st.eof) { *outlen = 0; ret = 0; // If there was a trailing '=' to check, check it: if (srclen && (st.eof == BASE64_AEOF)) { state->bytes = 0; state->eof = BASE64_EOF; ret = ((base64_table_dec[*c++] == 254) && (srclen == 1)) ? 1 : 0; } return ret; } // Turn four 6-bit numbers into three bytes: // out[0] = 11111122 // out[1] = 22223333 // out[2] = 33444444 // Duff's device again: switch (st.bytes) { for (;;) { case 0: if (srclen-- == 0) { ret = 1; break; } if ((q = base64_table_dec[*c++]) >= 254) { st.eof = BASE64_EOF; // Treat character '=' as invalid for byte 0: break; } st.carry = q << 2; st.bytes++; // Deliberate fallthrough: BASE64_FALLTHROUGH case 1: if (srclen-- == 0) { ret = 1; break; } if ((q = base64_table_dec[*c++]) >= 254) { st.eof = BASE64_EOF; // Treat character '=' as invalid for byte 1: break; } *o++ = st.carry | (q >> 4); st.carry = q << 4; st.bytes++; outl++; // Deliberate fallthrough: BASE64_FALLTHROUGH case 2: if (srclen-- == 0) { ret = 1; break; } if ((q = base64_table_dec[*c++]) >= 254) { st.bytes++; // When q == 254, the input char is '='. // Check if next byte is also '=': if (q == 254) { if (srclen-- != 0) { st.bytes = 0; // EOF: st.eof = BASE64_EOF; q = base64_table_dec[*c++]; ret = ((q == 254) && (srclen == 0)) ? 1 : 0; break; } else { // Almost EOF st.eof = BASE64_AEOF; ret = 1; break; } } // If we get here, there was an error: break; } *o++ = st.carry | (q >> 2); st.carry = q << 6; st.bytes++; outl++; // Deliberate fallthrough: BASE64_FALLTHROUGH case 3: if (srclen-- == 0) { ret = 1; break; } if ((q = base64_table_dec[*c++]) >= 254) { st.bytes = 0; st.eof = BASE64_EOF; // When q == 254, the input char is '='. Return 1 and EOF. // When q == 255, the input char is invalid. Return 0 and EOF. ret = ((q == 254) && (srclen == 0)) ? 1 : 0; break; } *o++ = st.carry | q; st.carry = 0; st.bytes = 0; outl++; } } state->eof = st.eof; state->bytes = st.bytes; state->carry = st.carry; *outlen = outl; return ret; }