Im provements in base64.hpp

This commit is contained in:
Arun M 2017-11-22 15:00:47 +05:30
parent da6d4932c3
commit 2290c8733a
5 changed files with 134 additions and 25 deletions

View file

@ -7,8 +7,27 @@
namespace jwt { namespace jwt {
/*! // Returns the maximum number of bytes required to
// encode an input byte string of length `n` to base64.
inline constexpr
size_t encoding_size(size_t n)
{
return 4 * ((n + 2) / 3);
}
// Returns the maximum number of bytes required
// to store a decoded base64 byte string.
inline constexpr
size_t decoding_size(size_t n)
{
return n / 4 * 3;
}
/**
* Encoding map. * Encoding map.
* A constexpr helper class for performing base64
* encoding on the input byte string.
*/ */
class EMap class EMap
{ {
@ -31,21 +50,32 @@ private:
}; };
}; };
/*! /**
* Encodes a sequence of octet into base64 string.
* Returns std::string resized to contain only the
* encoded data (as usual without null terminator).
*
* The encoded string is atleast `encoding_size(input len)`
* in size.
*
* Arguments:
* @in : Input byte string to be encoded.
* @len : Length of the input byte string.
*/ */
std::string base64_encode(const char* in, size_t len) std::string base64_encode(const char* in, size_t len)
{ {
std::string result; std::string result;
result.resize(128); const auto encoded_siz = encoding_size(len);
result.resize(encoded_siz);
constexpr static const EMap emap{}; constexpr static const EMap emap{};
int i = 0; int i = 0;
int j = 0; int j = 0;
for (; i < len - 2; i += 3) { for (; i < len - 2; i += 3) {
auto& first = in[i]; const auto first = in[i];
auto& second = in[i+1]; const auto second = in[i+1];
auto& third = in[i+2]; const auto third = in[i+2];
result[j++] = emap.at( (first >> 2) & 0x3F ); result[j++] = emap.at( (first >> 2) & 0x3F );
result[j++] = emap.at(((first & 0x03) << 4) | ((second & 0xF0) >> 4)); result[j++] = emap.at(((first & 0x03) << 4) | ((second & 0xF0) >> 4));
@ -56,8 +86,8 @@ std::string base64_encode(const char* in, size_t len)
switch (len % 3) { switch (len % 3) {
case 2: case 2:
{ {
auto& first = in[i]; const auto first = in[i];
auto& second = in[i+1]; const auto second = in[i+1];
result[j++] = emap.at( (first >> 2) & 0x3F ); result[j++] = emap.at( (first >> 2) & 0x3F );
result[j++] = emap.at(((first & 0x03) << 4) | ((second & 0xF0) >> 4)); result[j++] = emap.at(((first & 0x03) << 4) | ((second & 0xF0) >> 4));
@ -67,7 +97,7 @@ std::string base64_encode(const char* in, size_t len)
} }
case 1: case 1:
{ {
auto& first = in[i]; const auto first = in[i];
result[j++] = emap.at((first >> 2) & 0x3F); result[j++] = emap.at((first >> 2) & 0x3F);
result[j++] = emap.at((first & 0x03) << 4); result[j++] = emap.at((first & 0x03) << 4);
@ -79,6 +109,8 @@ std::string base64_encode(const char* in, size_t len)
break; break;
}; };
result.resize(j);
return result; return result;
} }
@ -86,8 +118,10 @@ std::string base64_encode(const char* in, size_t len)
//======================= Decoder ========================== //======================= Decoder ==========================
/* /**
* Decoding map. * Decoding map.
* A helper constexpr class for providing interface
* to the decoding map for base64.
*/ */
class DMap class DMap
{ {
@ -122,12 +156,21 @@ private:
}; };
}; };
/*! /**
* Decodes octet of base64 encoded byte string.
*
* Returns a std::string with the decoded byte string.
*
* Arguments:
* @in : Encoded base64 byte string.
* @len : Length of the encoded input byte string.
*/ */
std::string base64_decode(const char* in, size_t len) std::string base64_decode(const char* in, size_t len)
{ {
std::string result; std::string result;
result.resize(128); const auto decoded_siz = decoding_size(len);
result.resize(decoded_siz);
int i = 0; int i = 0;
size_t bytes_rem = len; size_t bytes_rem = len;
size_t bytes_wr = 0; size_t bytes_wr = 0;
@ -141,13 +184,13 @@ std::string base64_decode(const char* in, size_t len)
// Error case in input // Error case in input
if (dmap.at(*in) == -1) return result; if (dmap.at(*in) == -1) return result;
auto first = dmap.at(in[0]); const auto first = dmap.at(in[0]);
auto second = dmap.at(in[1]); const auto second = dmap.at(in[1]);
auto third = dmap.at(in[2]); const auto third = dmap.at(in[2]);
auto fourth = dmap.at(in[3]); const auto fourth = dmap.at(in[3]);
result[i] = (first << 2) | (second >> 4); result[i] = (first << 2) | (second >> 4);
result[i + 1] = (second << 4) | (third >> 2); result[i + 1] = (second << 4) | (third >> 2);
result[i + 2] = (third << 6) | fourth; result[i + 2] = (third << 6) | fourth;
bytes_rem -= 4; bytes_rem -= 4;
@ -159,24 +202,24 @@ std::string base64_decode(const char* in, size_t len)
switch(bytes_rem) { switch(bytes_rem) {
case 4: case 4:
{ {
auto third = dmap.at(in[2]); const auto third = dmap.at(in[2]);
auto fourth = dmap.at(in[3]); const auto fourth = dmap.at(in[3]);
result[i + 2] = (third << 6) | fourth; result[i + 2] = (third << 6) | fourth;
bytes_wr++; bytes_wr++;
//FALLTHROUGH //FALLTHROUGH
} }
case 3: case 3:
{ {
auto second = dmap.at(in[1]); const auto second = dmap.at(in[1]);
auto third = dmap.at(in[2]); const auto third = dmap.at(in[2]);
result[i + 1] = (second << 4) | (third >> 2); result[i + 1] = (second << 4) | (third >> 2);
bytes_wr++; bytes_wr++;
//FALLTHROUGH //FALLTHROUGH
} }
case 2: case 2:
{ {
auto first = dmap.at(in[0]); const auto first = dmap.at(in[0]);
auto second = dmap.at(in[1]); const auto second = dmap.at(in[1]);
result[i] = (first << 2) | (second >> 4); result[i] = (first << 2) | (second >> 4);
bytes_wr++; bytes_wr++;
} }
@ -187,7 +230,17 @@ std::string base64_decode(const char* in, size_t len)
return result; return result;
} }
/*! /**
* Makes the base64 encoded byte string URL safe.
* Overwrites/skips few URL unsafe characters
* from the input sequence.
*
* Arguments:
* @data : Base64 encoded byte string.
* @len : Length of the base64 byte string.
*
* Returns:
* Length of the URL safe base64 encoded byte string.
*/ */
size_t base64_uri_encode(char* data, size_t len) noexcept size_t base64_uri_encode(char* data, size_t len) noexcept
{ {
@ -212,7 +265,15 @@ size_t base64_uri_encode(char* data, size_t len) noexcept
return j; return j;
} }
/*! /**
* Decodes an input URL safe base64 encoded byte string.
*
* NOTE: To be used only for decoding URL safe base64 encoded
* byte string.
*
* Arguments:
* @data : URL safe base64 encoded byte string.
* @len : Length of the input byte string.
*/ */
std::string base64_uri_decode(const char* data, size_t len) std::string base64_uri_decode(const char* data, size_t len)
{ {

View file

View file

BIN
include/jwt/test/test_base64 Executable file

Binary file not shown.

View file

@ -0,0 +1,48 @@
#include <iostream>
#include <string>
#include <cassert>
#include "jwt/base64.hpp"
void base64_test_encode()
{
std::string input = "ArunMu";
std::string output = jwt::base64_encode(input.c_str(), input.length());
assert (output == "QXJ1bk11");
input = "Something really strange!!";
output = jwt::base64_encode(input.c_str(), input.length());
assert (output == "U29tZXRoaW5nIHJlYWxseSBzdHJhbmdlISE=");
input = "Do you want to know something more stranger ????";
output = jwt::base64_encode(input.c_str(), input.length());
assert (output == "RG8geW91IHdhbnQgdG8ga25vdyBzb21ldGhpbmcgbW9yZSBzdHJhbmdlciA/Pz8/");
input = R"({"a" : "b", "c" : [1,2,3,4,5]})";
output = jwt::base64_encode(input.c_str(), input.length());
assert (output == "eyJhIiA6ICJiIiwgImMiIDogWzEsMiwzLDQsNV19");
}
void base64_test_decode()
{
std::string input = "QXJ1bk11";
std::string output = jwt::base64_decode(input.c_str(), input.length());
assert (output == "ArunMu");
input = "U29tZXRoaW5nIHJlYWxseSBzdHJhbmdlISE=";
output = jwt::base64_decode(input.c_str(), input.length());
assert (output == "Something really strange!!");
input = "RG8geW91IHdhbnQgdG8ga25vdyBzb21ldGhpbmcgbW9yZSBzdHJhbmdlciA/Pz8/";
output = jwt::base64_decode(input.c_str(), input.length());
assert (output == "Do you want to know something more stranger ????");
input = "eyJhIiA6ICJiIiwgImMiIDogWzEsMiwzLDQsNV19";
output = jwt::base64_decode(input.c_str(), input.length());
assert (output == R"({"a" : "b", "c" : [1,2,3,4,5]})");
}
int main() {
base64_test_encode();
base64_test_decode();
return 0;
}