Skip to content

Commit 706e699

Browse files
Eugene Ostroukhovofrobots
authored andcommitted
string_bytes: Make base64 encode/decode reusable
Node already has support for base64 encoding and decoding that is not visible outside the string_bytes.cc file. Our work on providing a support for the Chrome inspector protocol (#6792) requires base64 encoding support. Rather then introducing a second copy of the base64 encoder, we suggest moving this code into a separate header. PR-URL: #6910 Reviewed-By: bnoordhuis - Ben Noordhuis <[email protected]> Reviewed-By: indutny - Fedor Indutny <[email protected]> Reviewed-By: ofrobots - Ali Ijaz Sheikh <[email protected]>
1 parent c111cf2 commit 706e699

File tree

2 files changed

+196
-178
lines changed

2 files changed

+196
-178
lines changed

src/base64.h

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
#ifndef SRC_BASE64_H_
2+
#define SRC_BASE64_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include "util.h"
7+
8+
#include <stddef.h>
9+
10+
namespace node {
11+
//// Base 64 ////
12+
#define base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4)
13+
14+
15+
// Doesn't check for padding at the end. Can be 1-2 bytes over.
16+
static inline size_t base64_decoded_size_fast(size_t size) {
17+
size_t remainder = size % 4;
18+
19+
size = (size / 4) * 3;
20+
if (remainder) {
21+
if (size == 0 && remainder == 1) {
22+
// special case: 1-byte input cannot be decoded
23+
size = 0;
24+
} else {
25+
// non-padded input, add 1 or 2 extra bytes
26+
size += 1 + (remainder == 3);
27+
}
28+
}
29+
30+
return size;
31+
}
32+
33+
template <typename TypeName>
34+
size_t base64_decoded_size(const TypeName* src, size_t size) {
35+
if (size == 0)
36+
return 0;
37+
38+
if (src[size - 1] == '=')
39+
size--;
40+
if (size > 0 && src[size - 1] == '=')
41+
size--;
42+
43+
return base64_decoded_size_fast(size);
44+
}
45+
46+
47+
extern const int8_t unbase64_table[256];
48+
49+
50+
#define unbase64(x) \
51+
static_cast<uint8_t>(unbase64_table[static_cast<uint8_t>(x)])
52+
53+
54+
template <typename TypeName>
55+
size_t base64_decode_slow(char* dst, size_t dstlen,
56+
const TypeName* src, size_t srclen) {
57+
uint8_t hi;
58+
uint8_t lo;
59+
size_t i = 0;
60+
size_t k = 0;
61+
for (;;) {
62+
#define V(expr) \
63+
while (i < srclen) { \
64+
const uint8_t c = src[i]; \
65+
lo = unbase64(c); \
66+
i += 1; \
67+
if (lo < 64) \
68+
break; /* Legal character. */ \
69+
if (c == '=') \
70+
return k; \
71+
} \
72+
expr; \
73+
if (i >= srclen) \
74+
return k; \
75+
if (k >= dstlen) \
76+
return k; \
77+
hi = lo;
78+
V(/* Nothing. */);
79+
V(dst[k++] = ((hi & 0x3F) << 2) | ((lo & 0x30) >> 4));
80+
V(dst[k++] = ((hi & 0x0F) << 4) | ((lo & 0x3C) >> 2));
81+
V(dst[k++] = ((hi & 0x03) << 6) | ((lo & 0x3F) >> 0));
82+
#undef V
83+
}
84+
UNREACHABLE();
85+
}
86+
87+
88+
template <typename TypeName>
89+
size_t base64_decode_fast(char* const dst, const size_t dstlen,
90+
const TypeName* const src, const size_t srclen,
91+
const size_t decoded_size) {
92+
const size_t available = dstlen < decoded_size ? dstlen : decoded_size;
93+
const size_t max_i = srclen / 4 * 4;
94+
const size_t max_k = available / 3 * 3;
95+
size_t i = 0;
96+
size_t k = 0;
97+
while (i < max_i && k < max_k) {
98+
const uint32_t v =
99+
unbase64(src[i + 0]) << 24 |
100+
unbase64(src[i + 1]) << 16 |
101+
unbase64(src[i + 2]) << 8 |
102+
unbase64(src[i + 3]);
103+
// If MSB is set, input contains whitespace or is not valid base64.
104+
if (v & 0x80808080) {
105+
break;
106+
}
107+
dst[k + 0] = ((v >> 22) & 0xFC) | ((v >> 20) & 0x03);
108+
dst[k + 1] = ((v >> 12) & 0xF0) | ((v >> 10) & 0x0F);
109+
dst[k + 2] = ((v >> 2) & 0xC0) | ((v >> 0) & 0x3F);
110+
i += 4;
111+
k += 3;
112+
}
113+
if (i < srclen && k < dstlen) {
114+
return k + base64_decode_slow(dst + k, dstlen - k, src + i, srclen - i);
115+
}
116+
return k;
117+
}
118+
119+
120+
template <typename TypeName>
121+
size_t base64_decode(char* const dst, const size_t dstlen,
122+
const TypeName* const src, const size_t srclen) {
123+
const size_t decoded_size = base64_decoded_size(src, srclen);
124+
return base64_decode_fast(dst, dstlen, src, srclen, decoded_size);
125+
}
126+
127+
static size_t base64_encode(const char* src,
128+
size_t slen,
129+
char* dst,
130+
size_t dlen) {
131+
// We know how much we'll write, just make sure that there's space.
132+
CHECK(dlen >= base64_encoded_size(slen) &&
133+
"not enough space provided for base64 encode");
134+
135+
dlen = base64_encoded_size(slen);
136+
137+
unsigned a;
138+
unsigned b;
139+
unsigned c;
140+
unsigned i;
141+
unsigned k;
142+
unsigned n;
143+
144+
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
145+
"abcdefghijklmnopqrstuvwxyz"
146+
"0123456789+/";
147+
148+
i = 0;
149+
k = 0;
150+
n = slen / 3 * 3;
151+
152+
while (i < n) {
153+
a = src[i + 0] & 0xff;
154+
b = src[i + 1] & 0xff;
155+
c = src[i + 2] & 0xff;
156+
157+
dst[k + 0] = table[a >> 2];
158+
dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
159+
dst[k + 2] = table[((b & 0x0f) << 2) | (c >> 6)];
160+
dst[k + 3] = table[c & 0x3f];
161+
162+
i += 3;
163+
k += 4;
164+
}
165+
166+
if (n != slen) {
167+
switch (slen - n) {
168+
case 1:
169+
a = src[i + 0] & 0xff;
170+
dst[k + 0] = table[a >> 2];
171+
dst[k + 1] = table[(a & 3) << 4];
172+
dst[k + 2] = '=';
173+
dst[k + 3] = '=';
174+
break;
175+
176+
case 2:
177+
a = src[i + 0] & 0xff;
178+
b = src[i + 1] & 0xff;
179+
dst[k + 0] = table[a >> 2];
180+
dst[k + 1] = table[((a & 3) << 4) | (b >> 4)];
181+
dst[k + 2] = table[(b & 0x0f) << 2];
182+
dst[k + 3] = '=';
183+
break;
184+
}
185+
}
186+
187+
return dlen;
188+
}
189+
} // namespace node
190+
191+
192+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
193+
194+
#endif // SRC_BASE64_H_

0 commit comments

Comments
 (0)