123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- #include <stdint.h>
- #include <stddef.h>
- #include <stdlib.h>
- #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;
- }
|