Skip to content

Commit

Permalink
Addition of string_view support
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanel committed Jan 20, 2023
1 parent b07326c commit a73ec5c
Show file tree
Hide file tree
Showing 11 changed files with 564 additions and 346 deletions.
3 changes: 2 additions & 1 deletion docs/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ struct my_favorite_json_library_traits {
static boolean_type as_boolean(const value_type &val);

// serialization and parsing
static bool parse(value_type &val, string_type str);
template <class string_t> // could be the json string_type, or std::string_view for instance
static bool parse(value_type &val, const string_t& str);
static string_type serialize(const value_type &val); // with no extra whitespace, padding or indentation
};
```
99 changes: 47 additions & 52 deletions include/jwt-cpp/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#include <array>
#include <stdexcept>
#include <string>
#include <vector>

#include "string_types.h"

#ifdef __has_cpp_attribute
#if __has_cpp_attribute(fallthrough)
Expand Down Expand Up @@ -37,10 +38,7 @@ namespace jwt {
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}};
return data;
}
static const std::string& fill() {
static std::string fill{"="};
return fill;
}
static std::array<string_constant, 1> fill() { return std::array<string_constant, 1>{"="}; }
};
/**
* \brief valid list of character when working with [Base64URL](https://tools.ietf.org/html/rfc4648#section-5)
Expand All @@ -60,10 +58,7 @@ namespace jwt {
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
return data;
}
static const std::string& fill() {
static std::string fill{"%3d"};
return fill;
}
static std::array<string_constant, 1> fill() { return std::array<string_constant, 1>{"%3d"}; }
};
namespace helper {
/**
Expand All @@ -81,15 +76,15 @@ namespace jwt {
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}};
return data;
}
static const std::initializer_list<std::string>& fill() {
static std::initializer_list<std::string> fill{"%3D", "%3d"};
return fill;
}
static std::array<string_constant, 2> fill() { return std::array<string_constant, 2>{"%3D", "%3d"}; }
};
} // namespace helper

inline uint32_t index(const std::array<char, 64>& alphabet, char symbol) {
auto itr = std::find_if(alphabet.cbegin(), alphabet.cend(), [symbol](char c) { return c == symbol; });
if (symbol >= 'A' && symbol <= 'Z') { return static_cast<uint32_t>(symbol - 'A'); }
if (symbol >= 'a' && symbol <= 'z') { return static_cast<uint32_t>(26 + symbol - 'a'); }
if (symbol >= '0' && symbol <= '9') { return static_cast<uint32_t>(52 + symbol - '0'); }
auto itr = std::find(std::next(alphabet.cbegin(), 62U), alphabet.cend(), symbol);
if (itr == alphabet.cend()) { throw std::runtime_error("Invalid input: not within alphabet"); }

return std::distance(alphabet.cbegin(), itr);
Expand All @@ -116,30 +111,31 @@ namespace jwt {
}
};

inline padding count_padding(const std::string& base, const std::vector<std::string>& fills) {
for (const auto& fill : fills) {
if (base.size() < fill.size()) continue;
template<class StrInputIt>
padding count_padding(string_view base, StrInputIt fillStart, StrInputIt fillEnd) {
for (StrInputIt fillIt = fillStart; fillIt != fillEnd; ++fillIt) {
int fillLen = static_cast<int>(fillIt->size());
int deltaLen = static_cast<int>(base.size()) - fillLen;
// Does the end of the input exactly match the fill pattern?
if (base.substr(base.size() - fill.size()) == fill) {
return padding{1, fill.length()} +
count_padding(base.substr(0, base.size() - fill.size()), fills);
if (deltaLen >= 0 && base.substr(deltaLen) == *fillIt) {
return padding{1, static_cast<size_t>(fillLen)} +
count_padding(base.substr(0, deltaLen), fillStart, fillEnd);
}
}

return {};
}

inline std::string encode(const std::string& bin, const std::array<char, 64>& alphabet,
const std::string& fill) {
inline std::string encode(string_view bin, const std::array<char, 64>& alphabet, string_view fill) {
size_t size = bin.size();
std::string res;

// clear incomplete bytes
size_t fast_size = size - size % 3;
for (size_t i = 0; i < fast_size;) {
uint32_t octet_a = static_cast<unsigned char>(bin[i++]);
uint32_t octet_b = static_cast<unsigned char>(bin[i++]);
uint32_t octet_c = static_cast<unsigned char>(bin[i++]);
for (size_t i = 0; i < fast_size; i += 3) {
uint32_t octet_a = static_cast<unsigned char>(bin[i]);
uint32_t octet_b = static_cast<unsigned char>(bin[i + 1]);
uint32_t octet_c = static_cast<unsigned char>(bin[i + 2]);

uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

Expand Down Expand Up @@ -178,9 +174,10 @@ namespace jwt {
return res;
}

inline std::string decode(const std::string& base, const std::array<char, 64>& alphabet,
const std::vector<std::string>& fill) {
const auto pad = count_padding(base, fill);
template<class StrInputIt>
inline std::string decode(string_view base, const std::array<char, 64>& alphabet, StrInputIt fillStart,
StrInputIt fillEnd) {
const auto pad = count_padding(base, fillStart, fillEnd);
if (pad.count > 2) throw std::runtime_error("Invalid input: too much fill");

const size_t size = base.size() - pad.length;
Expand Down Expand Up @@ -224,44 +221,42 @@ namespace jwt {
return res;
}

inline std::string decode(const std::string& base, const std::array<char, 64>& alphabet,
const std::string& fill) {
return decode(base, alphabet, std::vector<std::string>{fill});
}

inline std::string pad(const std::string& base, const std::string& fill) {
std::string padding;
switch (base.size() % 4) {
case 1: padding += fill; JWT_FALLTHROUGH;
case 2: padding += fill; JWT_FALLTHROUGH;
case 3: padding += fill; JWT_FALLTHROUGH;
inline std::string pad(string_view base, string_view fill) {
std::string res(base);
switch (res.size() % 4) {
case 1: res += fill; JWT_FALLTHROUGH;
case 2: res += fill; JWT_FALLTHROUGH;
case 3: res += fill; JWT_FALLTHROUGH;
default: break;
}

return base + padding;
return res;
}

inline std::string trim(const std::string& base, const std::string& fill) {
inline std::string trim(string_view base, string_view fill) {
auto pos = base.find(fill);
return base.substr(0, pos);
return static_cast<std::string>(base.substr(0, pos));
}
} // namespace details

template<typename T>
std::string encode(const std::string& bin) {
return details::encode(bin, T::data(), T::fill());
std::string encode(string_view bin) {
static const auto fills = T::fill();
return details::encode(bin, T::data(), fills.front());
}
template<typename T>
std::string decode(const std::string& base) {
return details::decode(base, T::data(), T::fill());
std::string decode(string_view base) {
static const auto fills = T::fill();
return details::decode(base, T::data(), fills.begin(), fills.end());
}
template<typename T>
std::string pad(const std::string& base) {
return details::pad(base, T::fill());
std::string pad(string_view base) {
static const auto fills = T::fill();
return details::pad(base, fills.front());
}
template<typename T>
std::string trim(const std::string& base) {
return details::trim(base, T::fill());
std::string trim(string_view base) {
static const auto fills = T::fill();
return details::trim(base, fills.front());
}
} // namespace base
} // namespace jwt
Expand Down
Loading

0 comments on commit a73ec5c

Please sign in to comment.