1 #ifndef LIBBASE64_CPP_H
2 #define LIBBASE64_CPP_H
3 
4 #include <string>
5 //#define LIBBASE64_THROW_STD_INVALID_ARGUMENT
6 
7 //version info
8 #define __LIBBASE64_MAJOR__ 1
9 #define __LIBBASE64_MINOR__ 1
10 #define __LIBBASE64_PATCH__ 0
11 #define __LIBBASE64_VERSION__ (__LIBBASE64_MAJOR__ * 10000 + __LIBBASE64_MINOR__ * 100 + __LIBBASE64_PATCH__)
12 
13 //code coverage and asserts
14 #ifdef NDEBUG
15 	#define LIBBASE64_ASSERT(cond, msg) (void)0
16 	#define CREATEBOUNDCHECKER(type, name, ubound, lbound) (void)0
17 	#define GETITEM_BOUNDCHECK(loc, name) (*(loc))
18 #else
19 	#include <iostream>
20 	#define LIBBASE64_ASSERT(cond, msg) if (!(cond)){ std::cerr << msg << std::endl; throw false; }
21 
22 	template<typename T>
23 	class libbase64_boundChecker {
24 	public:
libbase64_boundChecker(const T * lbound,const T * ubound)25 		libbase64_boundChecker(const T * lbound, const T * ubound) : upperbound(ubound), lowerbound(lbound){};
getLocation(const T * loc)26 		T getLocation(const T * loc){
27 			LIBBASE64_ASSERT(loc < upperbound, "Array index above bounds");
28 			LIBBASE64_ASSERT(loc >= lowerbound, "Array index below bounds");
29 			return *loc;
30 		}
31 	private:
32 		const T * lowerbound;
33 		const T * upperbound;
34 	};
35 	#define CREATEBOUNDCHECKER(type, name, ubound, lbound) libbase64_boundChecker<type> name(ubound, lbound)
36 	#define GETITEM_BOUNDCHECK(loc, name) name.getLocation(loc)
37 
38 	#ifdef LIBBASE64CODECOVERAGE
39 		#define LIBBASE64CODECOVERAGEBRANCH { static bool f_codeCoverage_ = false; if (f_codeCoverage_ == false){ libbase64::getCoverageHits<STRINGTYPE, CHARTYPE, UCHARTYPE, SAFETY>(true); f_codeCoverage_ = true; } }
40 	#endif
41 #endif
42 #ifndef LIBBASE64CODECOVERAGE
43 	#define LIBBASE64CODECOVERAGEBRANCH (void)0
44 #endif
45 
46 //predictive branching optimizations
47 #ifdef __GNUC__
48 	#define LIBBASE64_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100)
49 	#if (LIBBASE64_GCC_VERSION >= 29600)
50 		  #define libbase64_likely(x) __builtin_expect((long)((bool)(x)),1)
51 		  #define libbase64_unlikely(x) __builtin_expect((long)((bool)(x)),0)
52 	#endif
53 #endif
54 #ifndef libbase64_likely
55 	#define libbase64_likely(x) x
56 	#define libbase64_unlikely(x) x
57 #endif
58 
59 
60 namespace libbase64 {
61 	#ifdef LIBBASE64CODECOVERAGE  //Gets the number of branches that has been made
62 		template<class STRINGTYPE, typename CHARTYPE, typename UCHARTYPE, bool SAFETY>
getCoverageHits(bool inc)63 		static size_t getCoverageHits(bool inc){
64 			static size_t hits = 0;
65 			if (inc) ++hits;
66 			return hits;
67 		}
68 	#endif
69 
70 	//characters used in convertions
71 	namespace libbase64_characters {
72 		template<typename T>
getChar64(void)73 		inline static const T * getChar64(void){
74 			static const T char64s[64] = {
75 				(T)'A', (T)'B', (T)'C', (T)'D', (T)'E', (T)'F', (T)'G', (T)'H', (T)'I', (T)'J', (T)'K', (T)'L', (T)'M',
76 				(T)'N', (T)'O', (T)'P', (T)'Q', (T)'R', (T)'S', (T)'T', (T)'U', (T)'V', (T)'W', (T)'X', (T)'Y', (T)'Z',
77 				(T)'a', (T)'b', (T)'c', (T)'d', (T)'e', (T)'f', (T)'g', (T)'h', (T)'i', (T)'j', (T)'k', (T)'l', (T)'m',
78 				(T)'n', (T)'o', (T)'p', (T)'q', (T)'r', (T)'s', (T)'t', (T)'u', (T)'v', (T)'w', (T)'x', (T)'y', (T)'z',
79 				(T)'0', (T)'1', (T)'2', (T)'3', (T)'4', (T)'5', (T)'6', (T)'7', (T)'8', (T)'9', (T)'+', (T)'/'
80 			};
81 			return char64s;
82 		}
83 
84 		template<typename T>
getChar(unsigned char bin)85 		inline static T getChar(unsigned char bin){
86 			CREATEBOUNDCHECKER(T, char64bounds, getChar64<T>(), getChar64<T>() + 64);
87 			return GETITEM_BOUNDCHECK(getChar64<T>() + bin, char64bounds);
88 		}
89 
90 		template<typename T>
toBinary(T c)91 		inline static T toBinary(T c) {
92 			static T binaryConvert[80] = {62,48,49,50,63,52,53,54,55,56,57,58,59,60,61,249,250,251,252,253,254,255,0,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,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};
93 			CREATEBOUNDCHECKER(T, binaryConvertsbounds, binaryConvert, binaryConvert + 80);
94 			return GETITEM_BOUNDCHECK(binaryConvert + c - 43, binaryConvertsbounds);
95 		}
96 
97 		template<typename T>
emptyString(void)98 		static inline T & emptyString(void){
99 			static T t;
100 			return t;
101 		}
102 	}
103 
104 	namespace libbase64_Calculator {
getEncodingSize(size_t bytes)105 		inline static size_t getEncodingSize(size_t bytes){
106 			return (bytes + 2 - ((bytes + 2) % 3)) / 3 * 4;
107 		}
getDecodingSize(size_t res)108 		inline static size_t getDecodingSize(size_t res){
109 			return res * 3 / 4;
110 		}
111 	}
112 
113 
114 	/**
115 	 *	Encodes data into a base64 string of STRINGTYPE
116 	 */
117 	template<class STRINGTYPE, typename CHARTYPE, typename UCHARTYPE, bool SAFETY>
encode(const unsigned char * binary,size_t bytes)118 	static STRINGTYPE encode(const unsigned char * binary, size_t bytes){
119 		CREATEBOUNDCHECKER(unsigned char, binarybounds, binary, binary + bytes);
120 
121 		//make sure that there is actually something to encode
122 		if (SAFETY){
123 			if (libbase64_unlikely(bytes == 0)){
124 				LIBBASE64CODECOVERAGEBRANCH;
125 				return libbase64_characters::emptyString<STRINGTYPE>();
126 			}
127 		}
128 
129 		//calculate length and how misaligned it is
130 		size_t misaligned = bytes % 3;
131 		STRINGTYPE result;
132 		result.reserve(libbase64_Calculator::getEncodingSize(bytes));
133 
134 		//do all of the ones that are 3 byte aligned
135 		for (size_t i = 0, aligned((bytes - misaligned) / 3); i < aligned; ++i){
136 			LIBBASE64CODECOVERAGEBRANCH;
137 			result += libbase64_characters::getChar<CHARTYPE>((GETITEM_BOUNDCHECK(binary, binarybounds) & 0xFC) >> 2);
138 			result += libbase64_characters::getChar<CHARTYPE>(((GETITEM_BOUNDCHECK(binary, binarybounds) & 0x03) << 4) + ((GETITEM_BOUNDCHECK(binary + 1, binarybounds) & 0xF0) >> 4));
139 			result += libbase64_characters::getChar<CHARTYPE>(((GETITEM_BOUNDCHECK(binary + 1, binarybounds) & 0x0F) << 2) + ((GETITEM_BOUNDCHECK(binary + 2, binarybounds) & 0xC0) >> 6));
140 			result += libbase64_characters::getChar<CHARTYPE>(GETITEM_BOUNDCHECK(binary + 2, binarybounds) & 0x3F);
141 			binary += 3;
142 		}
143 
144 		//handle any additional characters at the end of it
145 		if (libbase64_likely(misaligned != 0)){
146 			LIBBASE64CODECOVERAGEBRANCH;
147 			//copy the rest into a temporary buffer, need it for the null terminators
148 			unsigned char temp[3] = { '\0', '\0', '\0' };
149 			for (unsigned char i = 0; i < (unsigned char)misaligned; ++i){
150 				LIBBASE64CODECOVERAGEBRANCH;
151 				temp[i] = GETITEM_BOUNDCHECK(binary++, binarybounds);
152 			}
153 
154 			//now do the final three bytes
155 			result += libbase64_characters::getChar<CHARTYPE>((temp[0] & 0xFC) >> 2);
156 			result += libbase64_characters::getChar<CHARTYPE>(((temp[0] & 0x03) << 4) + ((temp[1] & 0xF0) >> 4));
157 			if (misaligned == 2){
158 				LIBBASE64CODECOVERAGEBRANCH;
159 				result += libbase64_characters::getChar<CHARTYPE>(((temp[1] & 0x0F) << 2) + ((temp[2] & 0xC0) >> 6));
160 			} else {
161 				LIBBASE64CODECOVERAGEBRANCH;
162 				result += (CHARTYPE)'=';
163 			}
164 			result += (CHARTYPE)'=';
165 		} else {
166 			LIBBASE64CODECOVERAGEBRANCH;
167 		}
168 
169 		LIBBASE64_ASSERT(libbase64_Calculator::getEncodingSize(bytes) == result.length(), "Reserve wasn't the correct guess");
170 		return result;
171 	}
172 
173 	template<class STRINGTYPE, typename CHARTYPE, typename UCHARTYPE, bool SAFETY>
decode(const STRINGTYPE & encoded)174     static std::string decode(const STRINGTYPE & encoded){
175 		//check length to be sure its acceptable for base64
176 		const size_t length = encoded.length();
177 
178 		if (SAFETY){
179 			if (libbase64_unlikely((length % 4) != 0)){
180 				LIBBASE64CODECOVERAGEBRANCH;
181 				return libbase64_characters::emptyString<std::string>();
182 			}
183 			if (libbase64_unlikely(length == 0)){
184 				LIBBASE64CODECOVERAGEBRANCH;
185 				return libbase64_characters::emptyString<std::string>();
186 			}
187 
188 			//check to be sure there aren't odd characters or characters in the wrong places
189 			size_t pos = encoded.find_first_not_of(libbase64_characters::getChar64<CHARTYPE>());
190 			if (libbase64_unlikely(pos != STRINGTYPE::npos)){
191 				LIBBASE64CODECOVERAGEBRANCH;
192 				if (libbase64_unlikely(encoded[pos] != (CHARTYPE)'=')){
193 					LIBBASE64CODECOVERAGEBRANCH;  //INVALID_CHAR
194 					#ifdef LIBBASE64_THROW_STD_INVALID_ARGUMENT
195 						throw std::invalid_argument("invalid character in base64");
196 					#else
197 						return libbase64_characters::emptyString<std::string>();
198 					#endif
199 				}
200 				if (pos != length - 1){
201 					LIBBASE64CODECOVERAGEBRANCH;
202 					if (libbase64_unlikely(pos != length - 2)){
203 						LIBBASE64CODECOVERAGEBRANCH; //EQUAL_WRONG_PLACE
204 						#ifdef LIBBASE64_THROW_STD_INVALID_ARGUMENT
205 							throw std::invalid_argument("equal sign in wrong place in base64");
206 						#else
207 							return libbase64_characters::emptyString<std::string>();
208 						#endif
209 					}
210 					if (libbase64_unlikely(encoded[pos + 1] != (CHARTYPE)'=')){
211 						LIBBASE64CODECOVERAGEBRANCH;  //EQUAL_NOT_LAST
212 						#ifdef LIBBASE64_THROW_STD_INVALID_ARGUMENT
213 							throw std::invalid_argument("invalid character in base64");
214 						#else
215 							return libbase64_characters::emptyString<std::string>();
216 						#endif
217 					}
218 					LIBBASE64CODECOVERAGEBRANCH;
219 				} else {
220 					LIBBASE64CODECOVERAGEBRANCH;
221 				}
222 			} else {
223 				LIBBASE64CODECOVERAGEBRANCH;
224 			}
225 		}
226 
227 		const CHARTYPE * runner = encoded.data();
228 		const CHARTYPE * end = runner + encoded.length();
229 		CREATEBOUNDCHECKER(CHARTYPE, encodedbounds, runner, end);
230 		size_t aligned = length / 4; //don't do the last ones as they might be = padding
231 		std::string result;
232 		--aligned;
233 		result.reserve(libbase64_Calculator::getDecodingSize(length));
234 
235 		//first do the ones that can not have any padding
236 		for (unsigned int i = 0; i < aligned; ++i){
237 			const CHARTYPE second = libbase64_characters::toBinary<UCHARTYPE>(GETITEM_BOUNDCHECK(runner + 1, encodedbounds));
238 			const CHARTYPE third = libbase64_characters::toBinary<UCHARTYPE>(GETITEM_BOUNDCHECK(runner + 2, encodedbounds));
239 			result += (libbase64_characters::toBinary<UCHARTYPE>(GETITEM_BOUNDCHECK(runner, encodedbounds)) << 2) + ((second & 0x30) >> 4);
240 			result += ((second & 0xf) << 4) + ((third & 0x3c) >> 2);
241 			result += ((third & 0x3) << 6) + libbase64_characters::toBinary<UCHARTYPE>(GETITEM_BOUNDCHECK(runner + 3, encodedbounds));
242 			runner += 4;
243 		}
244 
245 		//now do the ones that might have padding, the first two characters can not be padding, so do them quickly
246 		const CHARTYPE second = libbase64_characters::toBinary<UCHARTYPE>(GETITEM_BOUNDCHECK(runner + 1, encodedbounds));
247 		result += (libbase64_characters::toBinary<UCHARTYPE>(GETITEM_BOUNDCHECK(runner + 0, encodedbounds)) << 2) + ((second & 0x30) >> 4);
248 		runner += 2;
249 		if ((runner != end) && (*runner != (CHARTYPE)'=')){  //not two = pads
250 			LIBBASE64CODECOVERAGEBRANCH;
251 			const CHARTYPE third = libbase64_characters::toBinary<UCHARTYPE>(GETITEM_BOUNDCHECK(runner, encodedbounds));
252 			result += ((second & 0xf) << 4) + ((third & 0x3c) >> 2);
253 			++runner;
254 			if ((runner != end) && (*runner != (CHARTYPE)'=')){  //no padding
255 				LIBBASE64CODECOVERAGEBRANCH;
256 				result += ((third & 0x3) << 6) + libbase64_characters::toBinary<UCHARTYPE>(GETITEM_BOUNDCHECK(runner, encodedbounds));
257 			} else {
258 				LIBBASE64CODECOVERAGEBRANCH;
259 			}
260 		} else {
261 			LIBBASE64CODECOVERAGEBRANCH;
262 		}
263 
264 		LIBBASE64_ASSERT(libbase64_Calculator::getDecodingSize(length) >= result.length(), "Reserve wasn't the correct guess, too small");
265 		LIBBASE64_ASSERT((result.length() <= 3) || (libbase64_Calculator::getDecodingSize(length) > result.length() - 3), "Reserve wasn't the correct guess, too big");
266 		return result;
267 	}
268 }
269 
270 #endif
271