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