summaryrefslogtreecommitdiff
path: root/src/modules/json/JSON_Base64.cpp
blob: 66f2002b6b9413af89052c4d3e4e78d85f29265e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2009 Miranda ICQ/IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "..\..\core\commonheaders.h"

#include "JSON_Base64.h"
#include "JSONDefs.h"

#ifdef JSON_BINARY  //if this is not needed, don't waste space compiling it

static const json_char * chars64 = JSON_TEXT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
#ifdef JSON_UNICODE
	static std::string BASE64_EMPTY_STRING;
#else
	#define BASE64_EMPTY_STRING EMPTY_STRING
#endif

json_string JSONBase64::json_encode64(const unsigned char * binary, size_t bytes){
	size_t misaligned = bytes % 3;
	size_t aligned = (bytes - misaligned) / 3;  //divide by three once and inc is faster than add 3 each time
	json_string result;
	result.reserve((size_t)(((float)bytes) * 1.37f) + 4);

	//do all of the ones that are 3 byte aligned
	for (size_t i=0; i < aligned; ++i){
		result += chars64[(binary[0] & 0xFC) >> 2];
		result += chars64[((binary[0] & 0x03) << 4) + ((binary[1] & 0xF0) >> 4)];
		result += chars64[((binary[1] & 0x0F) << 2) + ((binary[2] & 0xC0) >> 6)];
		result += chars64[binary[2] & 0x3F];
		binary += 3;
	}

	if (misaligned){
		//copy the rest into a temporary buffer
		unsigned char temp[3];
		for (unsigned int i=0; i < misaligned; ++i){
			temp[i] = *binary++;
		}
		for (unsigned int i = (unsigned int)misaligned; i < 3; ++i){
			temp[i] = '\0';
		}

		//now do the final three bytes
		result += chars64[(temp[0] & 0xFC) >> 2];
		result += chars64[((temp[0] & 0x03) << 4) + ((temp[1] & 0xF0) >> 4)];
		if (misaligned == 2){
			result += chars64[((temp[1] & 0x0F) << 2) + ((temp[2] & 0xC0) >> 6)];
			result += JSON_TEXT('=');
		} else {
			result += JSON_TEXT(" = = ");
		}
	}
	JSON_ASSERT((size_t)(((float)bytes) * 1.37f) + 4 >= result.length(), JSON_TEXT("Didn't reserve enough space for a one-time go"));
	return result;
}

inline json_char toBinary(json_char c){
	if (c == JSON_TEXT('+')) {
		return JSON_TEXT('>');
	} else if (c == JSON_TEXT('/')) {
		return JSON_TEXT('?');
	} else if (c < JSON_TEXT(':')) {
		return c + JSON_TEXT('\x04');
	} else if (c < JSON_TEXT('[')) {
		return c - JSON_TEXT('\x41');
	}
	return c - 71;
}

/*
 Must be a std::string because it's binary, and chars must be 1 byte
 */
std::string JSONBase64::json_decode64(const json_string & encoded){
	const size_t length = encoded.length();
	#if defined JSON_DEBUG || defined JSON_SAFE
		size_t pos = encoded.find_first_not_of(chars64);
		if (pos != json_string::npos){
			JSON_ASSERT_SAFE(encoded[pos] == JSON_TEXT('='), JSON_TEXT("Not Base64"), return BASE64_EMPTY_STRING;);
			if (pos != length - 1){
				JSON_ASSERT_SAFE(pos == length - 2, JSON_TEXT("Not Base64"), return BASE64_EMPTY_STRING;);
				JSON_ASSERT_SAFE(encoded[pos + 1] == JSON_TEXT('='), JSON_TEXT("Not Base64"), return BASE64_EMPTY_STRING;);
			}
		}
	#endif
	const json_char * runner = encoded.c_str();
	size_t aligned = length / 4; //don't do the last ones as they might be = padding
	std::string result;
	if (aligned){
		--aligned;
		result.reserve((size_t)((float)length / 1.37) + 1);

		//first do the ones that can not have any padding
		for (unsigned int i=0; i < aligned; ++i){
			const json_char second = toBinary(runner[1]);
			const json_char third = toBinary(runner[2]);
			result += (toBinary(runner[0]) << 2) + ((second & 0x30) >> 4);
			result += ((second & 0xf) << 4) + ((third & 0x3c) >> 2);
			result += ((third & 0x3) << 6) + toBinary(runner[3]);
			runner += 4;
		}

		//now do the ones that might have padding, the first two characters can not be padding, so do them quickly
		const char second = toBinary(runner[1]);
		result += (toBinary(runner[0]) << 2) + ((second & 0x30) >> 4);
		if (runner[2] != '=') {  //not two = pads
			const char third = toBinary(runner[2]);
			result += ((second & 0xf) << 4) + ((third & 0x3c) >> 2);
			if (runner[3] != '=') {  //no padding
				result += ((third & 0x3) << 6) + toBinary(runner[3]);
			}
		}
	}
	return result;
}

#endif