253 lines
6.7 KiB
C++
253 lines
6.7 KiB
C++
/*
|
|
** Copyright (C) 2014 Wang Yaofu
|
|
** All rights reserved.
|
|
**
|
|
**Author:Wang Yaofu voipman@qq.com
|
|
**Description: The source file of base64.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "base64.h"
|
|
namespace {
|
|
|
|
static const unsigned char EncodeTable[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
static const unsigned char* DecodeTable = NULL;
|
|
|
|
void FillDecodeTable() {
|
|
// double buffer is used here for thread safe.
|
|
static unsigned char DecodeTableBuff[256];
|
|
unsigned char buff[256];
|
|
if (DecodeTable != NULL) {
|
|
return;
|
|
}
|
|
::memset(buff, 0x80, sizeof(buff));
|
|
|
|
for (size_t k = 0; k < sizeof(EncodeTable); ++k) {
|
|
buff[(size_t)EncodeTable[k]] = k;
|
|
}
|
|
// to mark those valid characters in encoded string, but not in these
|
|
// 64 bases list.
|
|
buff[(size_t)'\r'] = buff[(size_t)'\n'] = 0x4F;
|
|
buff[(size_t)'='] = 0x40;
|
|
|
|
::memcpy(DecodeTableBuff, buff, sizeof(DecodeTableBuff));
|
|
DecodeTable = DecodeTableBuff;
|
|
}
|
|
|
|
// Get the next 4 characters from input string, '\r\n' will be trimmed off.
|
|
// The input string starts from 'p', and ends before 'q'. 'buff' is for
|
|
// storing the return characters.
|
|
// The return value, -1: error, there aren't 4 characters available, or get
|
|
// invalid character. 0-4 mean the number of valid characters, '=' is excluded.
|
|
int GetNext4EncodedCharacters(const unsigned char*& p,
|
|
const unsigned char* q,
|
|
unsigned char* buff) {
|
|
int k = 0;
|
|
unsigned char c = 0;
|
|
while (k < 4 && p < q) {
|
|
c = DecodeTable[*p];
|
|
if ((c & 0xC0) == 0) { // normal valid characters
|
|
*buff++ = c;
|
|
++p;
|
|
++k;
|
|
} else if (c & 0x80) { // not ('\r' or '\n' or '=')
|
|
return -1;
|
|
} else if (*p == '=') {
|
|
break;
|
|
} else { // ('\r' or '\n')
|
|
++p;
|
|
}
|
|
}
|
|
// success. this should be most of the cases, return as soon as possible
|
|
if (k == 4) {
|
|
return 4;
|
|
}
|
|
// get a '='
|
|
if (p < q && *p == '=') {
|
|
++p;
|
|
// there should be 4 characters in the last encode group
|
|
int tail = 4 - k - 1;
|
|
// there should not be more than 2 '=' in the end
|
|
if (tail > 1) {
|
|
return -1;
|
|
}
|
|
while (tail > 0 && p < q && ((DecodeTable[*p] & 0x40) == 0x40)) {
|
|
if (*p == '=') {
|
|
--tail;
|
|
}
|
|
++p;
|
|
}
|
|
// any character not ('\r' or '\n' or '=') appears after '='
|
|
if (tail != 0) {
|
|
return -1;
|
|
}
|
|
// only ('\r' || '\n') is allowed at the end
|
|
while (p < q) {
|
|
if ((DecodeTable[*p] & 0x4F) == 0x4F) {
|
|
++p;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return k;
|
|
}
|
|
// for ('\r' or '\n') at very end
|
|
while (p < q && (DecodeTable[*p] & 0x4F) == 0x4F) {
|
|
++p;
|
|
}
|
|
if (k == 0 && p == q) {
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
size_t ExpectedEncodeLength(size_t len)
|
|
{
|
|
size_t encodedLen = ((len * 4 / 3 + 3) / 4) * 4;
|
|
return encodedLen;
|
|
}
|
|
|
|
size_t ExpectedDecodeLength(size_t len)
|
|
{
|
|
return (size_t)((len + 3) / 4 * 3);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
bool Base64Encode(const std::string& input, std::string* output)
|
|
{
|
|
assert(output);
|
|
output->resize(ExpectedEncodeLength(input.size()));
|
|
|
|
char* buff = const_cast<char*>(output->data());
|
|
size_t len = output->size();
|
|
|
|
if (!Base64Encode(input, buff, &len)) {
|
|
output->clear();
|
|
return false;
|
|
}
|
|
output->resize(len);
|
|
return true;
|
|
}
|
|
|
|
bool Base64Encode(const std::string& input, char* output, size_t* len)
|
|
{
|
|
assert(output);
|
|
|
|
char* buff = output;
|
|
if (__builtin_expect(*len < ExpectedEncodeLength(input.size()), 0)) {
|
|
return false;
|
|
}
|
|
|
|
unsigned char *p = (unsigned char*)input.data();
|
|
unsigned char *q = p + input.size();
|
|
unsigned char c1, c2, c3;
|
|
|
|
// process 3 char every loop
|
|
for (; p + 3 <= q; p += 3) {
|
|
c1 = *p;
|
|
c2 = *(p + 1);
|
|
c3 = *(p + 2);
|
|
|
|
*buff++ = EncodeTable[c1 >> 2];
|
|
*buff++ = EncodeTable[((c1 << 4) | (c2 >> 4)) & 0x3f];
|
|
*buff++ = EncodeTable[((c2 << 2) | (c3 >> 6)) & 0x3f];
|
|
*buff++ = EncodeTable[c3 & 0x3f];
|
|
}
|
|
|
|
// the reminders
|
|
if (q - p == 1) {
|
|
c1 = *p;
|
|
*buff++ = EncodeTable[(c1 & 0xfc) >> 2];
|
|
*buff++ = EncodeTable[(c1 & 0x03) << 4];
|
|
*buff++ = '=';
|
|
*buff++ = '=';
|
|
} else if (q - p == 2) {
|
|
c1 = *p;
|
|
c2 = *(p + 1);
|
|
*buff++ = EncodeTable[(c1 & 0xfc) >> 2];
|
|
*buff++ = EncodeTable[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)];
|
|
*buff++ = EncodeTable[((c2 & 0x0f) << 2)];
|
|
*buff++ = '=';
|
|
}
|
|
|
|
*len = buff - output;
|
|
return true;
|
|
}
|
|
|
|
bool Base64Decode(const std::string& input, std::string* output)
|
|
{
|
|
assert(output);
|
|
output->resize(ExpectedDecodeLength(input.size()));
|
|
|
|
char* buff = const_cast<char*>(output->data());
|
|
size_t len = output->size();
|
|
|
|
if (!Base64Decode(input, buff, &len)) {
|
|
output->clear();
|
|
return false;
|
|
}
|
|
output->resize(len);
|
|
return true;
|
|
}
|
|
|
|
bool Base64Decode(const std::string& input, char* output, size_t* len)
|
|
{
|
|
assert(output && len);
|
|
|
|
char* buff = output;
|
|
if (__builtin_expect(*len < ExpectedDecodeLength(input.size()), 0)) {
|
|
return false;
|
|
}
|
|
if (__builtin_expect(!DecodeTable, 0)) {
|
|
FillDecodeTable();
|
|
}
|
|
if (input.empty()) {
|
|
*len = buff - output;
|
|
return true;
|
|
}
|
|
|
|
const unsigned char* p = (unsigned char*)input.data();
|
|
const unsigned char* q = (unsigned char*)input.data() + input.size();
|
|
|
|
// handle 4 bytes in every loop
|
|
while (true) {
|
|
char ch = 0;
|
|
unsigned char encoded[4];
|
|
int len = GetNext4EncodedCharacters(p, q, encoded);
|
|
if (__builtin_expect(len == 4, 1)) {
|
|
ch = encoded[0] << 2; // all 6 bits
|
|
ch |= encoded[1] >> 4; // 2 high bits
|
|
*buff++ = ch;
|
|
ch = encoded[1] << 4; // 4 low bits
|
|
ch |= encoded[2] >> 2; // 4 high bits
|
|
*buff++ = ch;
|
|
ch = encoded[2] << 6; // 2 low bits
|
|
ch |= encoded[3];
|
|
*buff++ = ch;
|
|
} else if (len >= 2) {
|
|
ch = encoded[0] << 2; // all 6 bits
|
|
ch |= encoded[1] >> 4; // 2 high bits
|
|
*buff++ = ch;
|
|
if (len == 3) {
|
|
ch = encoded[1] << 4; // 4 low bits
|
|
ch |= encoded[2] >> 2; // 4 high bits
|
|
*buff++ = ch;
|
|
}
|
|
} else if (len == 0) {
|
|
break;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
*len = buff - output;
|
|
return true;
|
|
}
|
|
|