Line data Source code
1 : #include "fd_base64.h"
2 :
3 : static const char base64_alphabet[] =
4 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
5 :
6 : /* Inverse lookup table of ASCII byte => Base64 code point.
7 :
8 : Simple Base64 decode. Could do better here, but it's good
9 : enough for now. One obvious optimization is to provide
10 : multiple LUTs for each limb of the encoded word, then AND
11 : them together. */
12 :
13 : static uchar const invlut[ 0x100 ] = {
14 : /* 0x00 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
15 : /* 0x08 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
16 : /* 0x10 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
17 : /* 0x18 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
18 : /* 0x20 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
19 : /* 0x28 */ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
20 : /* 0x30 */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
21 : /* 0x38 */ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
22 : /* 0x40 */ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
23 : /* 0x48 */ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
24 : /* 0x50 */ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
25 : /* 0x58 */ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
26 : /* 0x60 */ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
27 : /* 0x68 */ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
28 : /* 0x70 */ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
29 : /* 0x78 */ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
30 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
31 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
32 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
33 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
34 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
35 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
36 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
37 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
38 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
39 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
40 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
41 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
42 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
43 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
44 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
45 : 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
46 : };
47 :
48 : long
49 : fd_base64_decode( uchar * out,
50 : char const * in,
51 0 : ulong in_len ) {
52 :
53 0 : uchar * const out_orig = out;
54 :
55 0 : if( in_len==0UL ) return 0UL;
56 :
57 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( in_len, 4UL ) ) ) return -1L;
58 :
59 0 : ulong pad_cnt = 0UL;
60 0 : if( in[ in_len-2UL ]=='=' ) pad_cnt++;
61 0 : if( in[ in_len-1UL ]=='=' ) pad_cnt++;
62 0 : in_len -= pad_cnt;
63 :
64 : /* 3 char padding is invalid */
65 0 : if( FD_UNLIKELY( (in_len%4UL)==1UL ) ) return -1L;
66 :
67 : /* "Fast" decode */
68 :
69 0 : while( in_len>=4UL ) {
70 0 : int a = (schar)invlut[ (ulong)(uchar)in[ 0 ] ];
71 0 : int b = (schar)invlut[ (ulong)(uchar)in[ 1 ] ];
72 0 : int c = (schar)invlut[ (ulong)(uchar)in[ 2 ] ];
73 0 : int d = (schar)invlut[ (ulong)(uchar)in[ 3 ] ];
74 :
75 0 : int err = (a<0) | (b<0) | (c<0) | (d<0);
76 0 : if( FD_UNLIKELY( err ) ) return -1L;
77 :
78 0 : ulong triple = ((ulong)a<<18UL) | ((ulong)b<<12UL) | ((ulong)c<<6UL) | ((ulong)d);
79 :
80 0 : out[ 0 ] = (uchar)(triple>>16UL);
81 0 : out[ 1 ] = (uchar)(triple>> 8UL);
82 0 : out[ 2 ] = (uchar)(triple>> 0UL);
83 :
84 0 : in += 4L;
85 0 : in_len -= 4L;
86 0 : out += 3L;
87 0 : }
88 :
89 : /* Decode last chunk */
90 :
91 0 : if( in_len>0UL ) {
92 :
93 0 : int a = (schar)invlut[ (ulong)(uchar)in[ 0 ] ];
94 0 : int b = (schar)invlut[ (ulong)(uchar)in[ 1 ] ];
95 0 : int c = 0;
96 0 : if( in_len==3 )
97 0 : c = (schar)invlut[ (ulong)(uchar)in[ 2 ] ];
98 :
99 0 : int err = (a<0) | (b<0) | (c<0);
100 0 : if( FD_UNLIKELY( err ) ) return -1L;
101 :
102 0 : ulong triple = ((ulong)a<<18UL) | ((ulong)b<<12UL) | ((ulong)c<<6UL);
103 0 : triple = fd_ulong_bswap( triple );
104 0 : triple >>= 40UL;
105 :
106 0 : switch( in_len ) {
107 0 : case 3: *out = (uchar)triple;
108 0 : out++;
109 0 : triple>>=8UL;
110 0 : __attribute__((fallthrough));
111 0 : case 2: *out = (uchar)triple;
112 0 : out++;
113 0 : }
114 :
115 0 : } while(0);
116 :
117 0 : return out - out_orig;
118 0 : }
119 :
120 : ulong
121 : fd_base64_encode( char * encoded,
122 : void const * _data,
123 3084 : ulong data_len ) {
124 :
125 3084 : uchar const * data = fd_type_pun_const( _data );
126 :
127 3084 : uint encoded_len = 0;
128 3084 : uint accumulator = 0;
129 3084 : int bits_collected = 0;
130 :
131 69684 : while( data_len-- ) {
132 66600 : accumulator = ( accumulator << 8 ) | *data++;
133 66600 : bits_collected += 8;
134 :
135 153369 : while( bits_collected >= 6 ) {
136 86769 : encoded[ encoded_len++ ] = base64_alphabet[ ( accumulator >> ( bits_collected - 6) ) & 0x3F ];
137 86769 : bits_collected -= 6;
138 86769 : }
139 66600 : }
140 :
141 3084 : if( bits_collected > 0 ) {
142 : // If there are remaining bits, pad the last Base64 character with zeroes
143 3057 : accumulator <<= 6 - bits_collected;
144 3057 : encoded[ encoded_len++ ] = base64_alphabet[accumulator & 0x3F ];
145 3057 : }
146 :
147 : // Add padding characters if necessary
148 6162 : while( encoded_len % 4 != 0 ) {
149 3078 : encoded[ encoded_len++ ] = '=';
150 3078 : }
151 :
152 3084 : return encoded_len;
153 3084 : }
|