Skip to content

Commit da9a2f5

Browse files
committed
Merge #11630: Simplify Base32 and Base64 conversions
b3ea8cc Simplify Base32 and Base64 conversions (Pieter Wuille) 3296a3b Generalize ConvertBits (Pieter Wuille) Pull request description: Generalize `ConvertBits` a bit to also be usable for the existing Base32 and Base64 convertions (rather than just for Bech32). Tree-SHA512: 3858247f9b14ca4766c08ea040a09b1d6d70caaccc75c2436a54102d6d526f499ec07f5bdfcbbe16cbde5aae521cd16e9aa693e688a97e6c5e74b8e58ee55a13
2 parents a312e20 + b3ea8cc commit da9a2f5

File tree

5 files changed

+69
-254
lines changed

5 files changed

+69
-254
lines changed

src/key_io.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,16 @@ class DestinationEncoder : public boost::static_visitor<std::string>
4343
std::string operator()(const WitnessV0KeyHash& id) const
4444
{
4545
std::vector<unsigned char> data = {0};
46-
ConvertBits<8, 5, true>(data, id.begin(), id.end());
46+
data.reserve(33);
47+
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
4748
return bech32::Encode(m_params.Bech32HRP(), data);
4849
}
4950

5051
std::string operator()(const WitnessV0ScriptHash& id) const
5152
{
5253
std::vector<unsigned char> data = {0};
53-
ConvertBits<8, 5, true>(data, id.begin(), id.end());
54+
data.reserve(53);
55+
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
5456
return bech32::Encode(m_params.Bech32HRP(), data);
5557
}
5658

@@ -60,7 +62,8 @@ class DestinationEncoder : public boost::static_visitor<std::string>
6062
return {};
6163
}
6264
std::vector<unsigned char> data = {(unsigned char)id.version};
63-
ConvertBits<8, 5, true>(data, id.program, id.program + id.length);
65+
data.reserve(1 + (id.length * 8 + 4) / 5);
66+
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.program, id.program + id.length);
6467
return bech32::Encode(m_params.Bech32HRP(), data);
6568
}
6669

@@ -94,7 +97,8 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
9497
// Bech32 decoding
9598
int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
9699
// The rest of the symbols are converted witness program bytes.
97-
if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) {
100+
data.reserve(((bech.second.size() - 1) * 5) / 8);
101+
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin() + 1, bech.second.end())) {
98102
if (version == 0) {
99103
{
100104
WitnessV0KeyHash keyid;

src/test/base32_tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(base32_testvectors)
1616
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
1717
{
1818
std::string strEnc = EncodeBase32(vstrIn[i]);
19-
BOOST_CHECK(strEnc == vstrOut[i]);
19+
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
2020
std::string strDec = DecodeBase32(vstrOut[i]);
21-
BOOST_CHECK(strDec == vstrIn[i]);
21+
BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
2222
}
2323
}
2424

src/test/base64_tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(base64_testvectors)
1616
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
1717
{
1818
std::string strEnc = EncodeBase64(vstrIn[i]);
19-
BOOST_CHECK(strEnc == vstrOut[i]);
19+
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
2020
std::string strDec = DecodeBase64(strEnc);
21-
BOOST_CHECK(strDec == vstrIn[i]);
21+
BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
2222
}
2323
}
2424

src/utilstrencodings.cpp

Lines changed: 54 additions & 243 deletions
Original file line numberDiff line numberDiff line change
@@ -127,46 +127,11 @@ std::string EncodeBase64(const unsigned char* pch, size_t len)
127127
{
128128
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
129129

130-
std::string strRet;
131-
strRet.reserve((len+2)/3*4);
132-
133-
int mode=0, left=0;
134-
const unsigned char *pchEnd = pch+len;
135-
136-
while (pch<pchEnd)
137-
{
138-
int enc = *(pch++);
139-
switch (mode)
140-
{
141-
case 0: // we have no bits
142-
strRet += pbase64[enc >> 2];
143-
left = (enc & 3) << 4;
144-
mode = 1;
145-
break;
146-
147-
case 1: // we have two bits
148-
strRet += pbase64[left | (enc >> 4)];
149-
left = (enc & 15) << 2;
150-
mode = 2;
151-
break;
152-
153-
case 2: // we have four bits
154-
strRet += pbase64[left | (enc >> 6)];
155-
strRet += pbase64[enc & 63];
156-
mode = 0;
157-
break;
158-
}
159-
}
160-
161-
if (mode)
162-
{
163-
strRet += pbase64[left];
164-
strRet += '=';
165-
if (mode == 1)
166-
strRet += '=';
167-
}
168-
169-
return strRet;
130+
std::string str;
131+
str.reserve(((len + 2) / 3) * 4);
132+
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len);
133+
while (str.size() % 4) str += '=';
134+
return str;
170135
}
171136

172137
std::string EncodeBase64(const std::string& str)
@@ -193,68 +158,32 @@ std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
193158
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
194159
};
195160

196-
if (pfInvalid)
197-
*pfInvalid = false;
198-
199-
std::vector<unsigned char> vchRet;
200-
vchRet.reserve(strlen(p)*3/4);
201-
202-
int mode = 0;
203-
int left = 0;
204-
205-
while (1)
206-
{
207-
int dec = decode64_table[(unsigned char)*p];
208-
if (dec == -1) break;
209-
p++;
210-
switch (mode)
211-
{
212-
case 0: // we have no bits and get 6
213-
left = dec;
214-
mode = 1;
215-
break;
216-
217-
case 1: // we have 6 bits and keep 4
218-
vchRet.push_back((left<<2) | (dec>>4));
219-
left = dec & 15;
220-
mode = 2;
221-
break;
222-
223-
case 2: // we have 4 bits and get 6, we keep 2
224-
vchRet.push_back((left<<4) | (dec>>2));
225-
left = dec & 3;
226-
mode = 3;
227-
break;
228-
229-
case 3: // we have 2 bits and get 6
230-
vchRet.push_back((left<<6) | dec);
231-
mode = 0;
232-
break;
233-
}
161+
const char* e = p;
162+
std::vector<uint8_t> val;
163+
val.reserve(strlen(p));
164+
while (*p != 0) {
165+
int x = decode64_table[(unsigned char)*p];
166+
if (x == -1) break;
167+
val.push_back(x);
168+
++p;
234169
}
235170

236-
if (pfInvalid)
237-
switch (mode)
238-
{
239-
case 0: // 4n base64 characters processed: ok
240-
break;
241-
242-
case 1: // 4n+1 base64 character processed: impossible
243-
*pfInvalid = true;
244-
break;
245-
246-
case 2: // 4n+2 base64 characters processed: require '=='
247-
if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1)
248-
*pfInvalid = true;
249-
break;
250-
251-
case 3: // 4n+3 base64 characters processed: require '='
252-
if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1)
253-
*pfInvalid = true;
254-
break;
171+
std::vector<unsigned char> ret;
172+
ret.reserve((val.size() * 3) / 4);
173+
bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
174+
175+
const char* q = p;
176+
while (valid && *p != 0) {
177+
if (*p != '=') {
178+
valid = false;
179+
break;
255180
}
181+
++p;
182+
}
183+
valid = valid && (p - e) % 4 == 0 && p - q < 4;
184+
if (pfInvalid) *pfInvalid = !valid;
256185

257-
return vchRet;
186+
return ret;
258187
}
259188

260189
std::string DecodeBase64(const std::string& str)
@@ -267,59 +196,11 @@ std::string EncodeBase32(const unsigned char* pch, size_t len)
267196
{
268197
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
269198

270-
std::string strRet;
271-
strRet.reserve((len+4)/5*8);
272-
273-
int mode=0, left=0;
274-
const unsigned char *pchEnd = pch+len;
275-
276-
while (pch<pchEnd)
277-
{
278-
int enc = *(pch++);
279-
switch (mode)
280-
{
281-
case 0: // we have no bits
282-
strRet += pbase32[enc >> 3];
283-
left = (enc & 7) << 2;
284-
mode = 1;
285-
break;
286-
287-
case 1: // we have three bits
288-
strRet += pbase32[left | (enc >> 6)];
289-
strRet += pbase32[(enc >> 1) & 31];
290-
left = (enc & 1) << 4;
291-
mode = 2;
292-
break;
293-
294-
case 2: // we have one bit
295-
strRet += pbase32[left | (enc >> 4)];
296-
left = (enc & 15) << 1;
297-
mode = 3;
298-
break;
299-
300-
case 3: // we have four bits
301-
strRet += pbase32[left | (enc >> 7)];
302-
strRet += pbase32[(enc >> 2) & 31];
303-
left = (enc & 3) << 3;
304-
mode = 4;
305-
break;
306-
307-
case 4: // we have two bits
308-
strRet += pbase32[left | (enc >> 5)];
309-
strRet += pbase32[enc & 31];
310-
mode = 0;
311-
}
312-
}
313-
314-
static const int nPadding[5] = {0, 6, 4, 3, 1};
315-
if (mode)
316-
{
317-
strRet += pbase32[left];
318-
for (int n=0; n<nPadding[mode]; n++)
319-
strRet += '=';
320-
}
321-
322-
return strRet;
199+
std::string str;
200+
str.reserve(((len + 4) / 5) * 8);
201+
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, pch, pch + len);
202+
while (str.size() % 8) str += '=';
203+
return str;
323204
}
324205

325206
std::string EncodeBase32(const std::string& str)
@@ -346,102 +227,32 @@ std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid)
346227
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
347228
};
348229

349-
if (pfInvalid)
350-
*pfInvalid = false;
351-
352-
std::vector<unsigned char> vchRet;
353-
vchRet.reserve((strlen(p))*5/8);
354-
355-
int mode = 0;
356-
int left = 0;
357-
358-
while (1)
359-
{
360-
int dec = decode32_table[(unsigned char)*p];
361-
if (dec == -1) break;
362-
p++;
363-
switch (mode)
364-
{
365-
case 0: // we have no bits and get 5
366-
left = dec;
367-
mode = 1;
368-
break;
369-
370-
case 1: // we have 5 bits and keep 2
371-
vchRet.push_back((left<<3) | (dec>>2));
372-
left = dec & 3;
373-
mode = 2;
374-
break;
375-
376-
case 2: // we have 2 bits and keep 7
377-
left = left << 5 | dec;
378-
mode = 3;
379-
break;
380-
381-
case 3: // we have 7 bits and keep 4
382-
vchRet.push_back((left<<1) | (dec>>4));
383-
left = dec & 15;
384-
mode = 4;
385-
break;
386-
387-
case 4: // we have 4 bits, and keep 1
388-
vchRet.push_back((left<<4) | (dec>>1));
389-
left = dec & 1;
390-
mode = 5;
391-
break;
392-
393-
case 5: // we have 1 bit, and keep 6
394-
left = left << 5 | dec;
395-
mode = 6;
396-
break;
397-
398-
case 6: // we have 6 bits, and keep 3
399-
vchRet.push_back((left<<2) | (dec>>3));
400-
left = dec & 7;
401-
mode = 7;
402-
break;
403-
404-
case 7: // we have 3 bits, and keep 0
405-
vchRet.push_back((left<<5) | dec);
406-
mode = 0;
407-
break;
408-
}
230+
const char* e = p;
231+
std::vector<uint8_t> val;
232+
val.reserve(strlen(p));
233+
while (*p != 0) {
234+
int x = decode32_table[(unsigned char)*p];
235+
if (x == -1) break;
236+
val.push_back(x);
237+
++p;
409238
}
410239

411-
if (pfInvalid)
412-
switch (mode)
413-
{
414-
case 0: // 8n base32 characters processed: ok
415-
break;
416-
417-
case 1: // 8n+1 base32 characters processed: impossible
418-
case 3: // +3
419-
case 6: // +6
420-
*pfInvalid = true;
421-
break;
422-
423-
case 2: // 8n+2 base32 characters processed: require '======'
424-
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1)
425-
*pfInvalid = true;
426-
break;
427-
428-
case 4: // 8n+4 base32 characters processed: require '===='
429-
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1)
430-
*pfInvalid = true;
431-
break;
432-
433-
case 5: // 8n+5 base32 characters processed: require '==='
434-
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1)
435-
*pfInvalid = true;
436-
break;
437-
438-
case 7: // 8n+7 base32 characters processed: require '='
439-
if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1)
440-
*pfInvalid = true;
441-
break;
240+
std::vector<unsigned char> ret;
241+
ret.reserve((val.size() * 5) / 8);
242+
bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
243+
244+
const char* q = p;
245+
while (valid && *p != 0) {
246+
if (*p != '=') {
247+
valid = false;
248+
break;
442249
}
250+
++p;
251+
}
252+
valid = valid && (p - e) % 8 == 0 && p - q < 8;
253+
if (pfInvalid) *pfInvalid = !valid;
443254

444-
return vchRet;
255+
return ret;
445256
}
446257

447258
std::string DecodeBase32(const std::string& str)

0 commit comments

Comments
 (0)