codec.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. #include <stdint.h>
  2. #include <stddef.h>
  3. #include <stdlib.h>
  4. #include "libbase64.h"
  5. #include "codecs.h"
  6. BASE64_ENC_FUNCTION(plain)
  7. {
  8. // Assume that *out is large enough to contain the output.
  9. // Theoretically it should be 4/3 the length of src.
  10. const uint8_t *c = (const uint8_t *)src;
  11. uint8_t *o = (uint8_t *)out;
  12. // Use local temporaries to avoid cache thrashing:
  13. size_t outl = 0;
  14. struct base64_state st;
  15. st.bytes = state->bytes;
  16. st.carry = state->carry;
  17. // Turn three bytes into four 6-bit numbers:
  18. // in[0] = 00111111
  19. // in[1] = 00112222
  20. // in[2] = 00222233
  21. // in[3] = 00333333
  22. // Duff's device, a for() loop inside a switch() statement. Legal!
  23. switch (st.bytes)
  24. {
  25. for (;;)
  26. {
  27. case 0:
  28. // If we have 64-bit ints, pick off 6 bytes at a time for as long as we can,
  29. // but ensure that there are at least 8 bytes available to avoid segfaulting:
  30. while (srclen >= 8)
  31. {
  32. // Load string:
  33. uint64_t str = *(uint64_t *)c;
  34. // Reorder to 64-bit big-endian, if not already in that format. The
  35. // workset must be in big-endian, otherwise the shifted bits do not
  36. // carry over properly among adjacent bytes:
  37. str = cpu_to_be64(str);
  38. // Shift input by 6 bytes each round and mask in only the lower 6 bits;
  39. // look up the character in the Base64 encoding table and write it to
  40. // the output location:
  41. *o++ = base64_table_enc[(str >> 58) & 0x3F];
  42. *o++ = base64_table_enc[(str >> 52) & 0x3F];
  43. *o++ = base64_table_enc[(str >> 46) & 0x3F];
  44. *o++ = base64_table_enc[(str >> 40) & 0x3F];
  45. *o++ = base64_table_enc[(str >> 34) & 0x3F];
  46. *o++ = base64_table_enc[(str >> 28) & 0x3F];
  47. *o++ = base64_table_enc[(str >> 22) & 0x3F];
  48. *o++ = base64_table_enc[(str >> 16) & 0x3F];
  49. c += 6; // 6 bytes of input
  50. outl += 8; // 8 bytes of output
  51. srclen -= 6;
  52. }
  53. if (srclen-- == 0) {
  54. break;
  55. }
  56. *o++ = base64_table_enc[*c >> 2];
  57. st.carry = (*c++ << 4) & 0x30;
  58. st.bytes++;
  59. outl += 1;
  60. // Deliberate fallthrough:
  61. BASE64_FALLTHROUGH
  62. case 1: if (srclen-- == 0) {
  63. break;
  64. }
  65. *o++ = base64_table_enc[st.carry | (*c >> 4)];
  66. st.carry = (*c++ << 2) & 0x3C;
  67. st.bytes++;
  68. outl += 1;
  69. // Deliberate fallthrough:
  70. BASE64_FALLTHROUGH
  71. case 2: if (srclen-- == 0) {
  72. break;
  73. }
  74. *o++ = base64_table_enc[st.carry | (*c >> 6)];
  75. *o++ = base64_table_enc[*c++ & 0x3F];
  76. st.bytes = 0;
  77. outl += 2;
  78. }
  79. }
  80. state->bytes = st.bytes;
  81. state->carry = st.carry;
  82. *outlen = outl;
  83. }
  84. BASE64_DEC_FUNCTION(plain)
  85. {
  86. int ret = 0;
  87. const uint8_t *c = (const uint8_t *)src;
  88. uint8_t *o = (uint8_t *)out;
  89. uint8_t q;
  90. // Use local temporaries to avoid cache thrashing:
  91. size_t outl = 0;
  92. struct base64_state st;
  93. st.eof = state->eof;
  94. st.bytes = state->bytes;
  95. st.carry = state->carry;
  96. // If we previously saw an EOF or an invalid character, bail out:
  97. if (st.eof) {
  98. *outlen = 0;
  99. ret = 0;
  100. // If there was a trailing '=' to check, check it:
  101. if (srclen && (st.eof == BASE64_AEOF)) {
  102. state->bytes = 0;
  103. state->eof = BASE64_EOF;
  104. ret = ((base64_table_dec[*c++] == 254) && (srclen == 1)) ? 1 : 0;
  105. }
  106. return ret;
  107. }
  108. // Turn four 6-bit numbers into three bytes:
  109. // out[0] = 11111122
  110. // out[1] = 22223333
  111. // out[2] = 33444444
  112. // Duff's device again:
  113. switch (st.bytes)
  114. {
  115. for (;;)
  116. {
  117. case 0:
  118. if (srclen-- == 0) {
  119. ret = 1;
  120. break;
  121. }
  122. if ((q = base64_table_dec[*c++]) >= 254) {
  123. st.eof = BASE64_EOF;
  124. // Treat character '=' as invalid for byte 0:
  125. break;
  126. }
  127. st.carry = q << 2;
  128. st.bytes++;
  129. // Deliberate fallthrough:
  130. BASE64_FALLTHROUGH
  131. case 1: if (srclen-- == 0) {
  132. ret = 1;
  133. break;
  134. }
  135. if ((q = base64_table_dec[*c++]) >= 254) {
  136. st.eof = BASE64_EOF;
  137. // Treat character '=' as invalid for byte 1:
  138. break;
  139. }
  140. *o++ = st.carry | (q >> 4);
  141. st.carry = q << 4;
  142. st.bytes++;
  143. outl++;
  144. // Deliberate fallthrough:
  145. BASE64_FALLTHROUGH
  146. case 2: if (srclen-- == 0) {
  147. ret = 1;
  148. break;
  149. }
  150. if ((q = base64_table_dec[*c++]) >= 254) {
  151. st.bytes++;
  152. // When q == 254, the input char is '='.
  153. // Check if next byte is also '=':
  154. if (q == 254) {
  155. if (srclen-- != 0) {
  156. st.bytes = 0;
  157. // EOF:
  158. st.eof = BASE64_EOF;
  159. q = base64_table_dec[*c++];
  160. ret = ((q == 254) && (srclen == 0)) ? 1 : 0;
  161. break;
  162. }
  163. else {
  164. // Almost EOF
  165. st.eof = BASE64_AEOF;
  166. ret = 1;
  167. break;
  168. }
  169. }
  170. // If we get here, there was an error:
  171. break;
  172. }
  173. *o++ = st.carry | (q >> 2);
  174. st.carry = q << 6;
  175. st.bytes++;
  176. outl++;
  177. // Deliberate fallthrough:
  178. BASE64_FALLTHROUGH
  179. case 3: if (srclen-- == 0) {
  180. ret = 1;
  181. break;
  182. }
  183. if ((q = base64_table_dec[*c++]) >= 254) {
  184. st.bytes = 0;
  185. st.eof = BASE64_EOF;
  186. // When q == 254, the input char is '='. Return 1 and EOF.
  187. // When q == 255, the input char is invalid. Return 0 and EOF.
  188. ret = ((q == 254) && (srclen == 0)) ? 1 : 0;
  189. break;
  190. }
  191. *o++ = st.carry | q;
  192. st.carry = 0;
  193. st.bytes = 0;
  194. outl++;
  195. }
  196. }
  197. state->eof = st.eof;
  198. state->bytes = st.bytes;
  199. state->carry = st.carry;
  200. *outlen = outl;
  201. return ret;
  202. }