blob: a07c0415af87d29a0ea7e21cf7c5054158098677 [file] [log] [blame]
#pragma once
#include "nlohmann/json.hpp"
#include <boost/utility/string_view.hpp>
#include <cstdint>
#include <cstring>
#include <functional>
#include <regex>
#include <stdexcept>
#include <string>
#include <tuple>
namespace crow
{
namespace black_magic
{
struct OutOfRange
{
OutOfRange(unsigned /*pos*/, unsigned /*length*/)
{
}
};
constexpr unsigned requiresInRange(unsigned i, unsigned len)
{
return i >= len ? throw OutOfRange(i, len) : i;
}
class ConstStr
{
const char* const beginPtr;
unsigned sizeUint;
public:
template <unsigned N>
constexpr ConstStr(const char (&arr)[N]) : beginPtr(arr), sizeUint(N - 1)
{
static_assert(N >= 1, "not a string literal");
}
constexpr char operator[](unsigned i) const
{
return requiresInRange(i, sizeUint), beginPtr[i];
}
constexpr operator const char*() const
{
return beginPtr;
}
constexpr const char* begin() const
{
return beginPtr;
}
constexpr const char* end() const
{
return beginPtr + sizeUint;
}
constexpr unsigned size() const
{
return sizeUint;
}
};
constexpr unsigned findClosingTag(ConstStr s, unsigned p)
{
return s[p] == '>' ? p : findClosingTag(s, p + 1);
}
constexpr bool isValid(ConstStr s, unsigned i = 0, int f = 0)
{
return i == s.size()
? f == 0
: f < 0 || f >= 2
? false
: s[i] == '<' ? isValid(s, i + 1, f + 1)
: s[i] == '>' ? isValid(s, i + 1, f - 1)
: isValid(s, i + 1, f);
}
constexpr bool isEquP(const char* a, const char* b, unsigned n)
{
return *a == 0 && *b == 0 && n == 0
? true
: (*a == 0 || *b == 0)
? false
: n == 0 ? true
: *a != *b ? false : isEquP(a + 1, b + 1, n - 1);
}
constexpr bool isEquN(ConstStr a, unsigned ai, ConstStr b, unsigned bi,
unsigned n)
{
return ai + n > a.size() || bi + n > b.size()
? false
: n == 0 ? true
: a[ai] != b[bi] ? false
: isEquN(a, ai + 1, b, bi + 1, n - 1);
}
constexpr bool isInt(ConstStr s, unsigned i)
{
return isEquN(s, i, "<int>", 0, 5);
}
constexpr bool isUint(ConstStr s, unsigned i)
{
return isEquN(s, i, "<uint>", 0, 6);
}
constexpr bool isFloat(ConstStr s, unsigned i)
{
return isEquN(s, i, "<float>", 0, 7) || isEquN(s, i, "<double>", 0, 8);
}
constexpr bool isStr(ConstStr s, unsigned i)
{
return isEquN(s, i, "<str>", 0, 5) || isEquN(s, i, "<string>", 0, 8);
}
constexpr bool isPath(ConstStr s, unsigned i)
{
return isEquN(s, i, "<path>", 0, 6);
}
template <typename T> struct parameter_tag
{
static const int value = 0;
};
#define BMCWEB_INTERNAL_PARAMETER_TAG(t, i) \
template <> struct parameter_tag<t> \
{ \
static const int value = i; \
}
BMCWEB_INTERNAL_PARAMETER_TAG(int, 1);
BMCWEB_INTERNAL_PARAMETER_TAG(char, 1);
BMCWEB_INTERNAL_PARAMETER_TAG(short, 1);
BMCWEB_INTERNAL_PARAMETER_TAG(long, 1);
BMCWEB_INTERNAL_PARAMETER_TAG(long long, 1);
BMCWEB_INTERNAL_PARAMETER_TAG(unsigned int, 2);
BMCWEB_INTERNAL_PARAMETER_TAG(unsigned char, 2);
BMCWEB_INTERNAL_PARAMETER_TAG(unsigned short, 2);
BMCWEB_INTERNAL_PARAMETER_TAG(unsigned long, 2);
BMCWEB_INTERNAL_PARAMETER_TAG(unsigned long long, 2);
BMCWEB_INTERNAL_PARAMETER_TAG(double, 3);
BMCWEB_INTERNAL_PARAMETER_TAG(std::string, 4);
#undef BMCWEB_INTERNAL_PARAMETER_TAG
template <typename... Args> struct compute_parameter_tag_from_args_list;
template <> struct compute_parameter_tag_from_args_list<>
{
static const int value = 0;
};
template <typename Arg, typename... Args>
struct compute_parameter_tag_from_args_list<Arg, Args...>
{
static const int subValue =
compute_parameter_tag_from_args_list<Args...>::value;
static const int value =
parameter_tag<typename std::decay<Arg>::type>::value
? subValue * 6 +
parameter_tag<typename std::decay<Arg>::type>::value
: subValue;
};
static inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
{
if (a == 0)
{
return b == 0;
}
if (b == 0)
{
return a == 0;
}
int sa = a % 6;
int sb = a % 6;
if (sa == 5)
{
sa = 4;
}
if (sb == 5)
{
sb = 4;
}
if (sa != sb)
{
return false;
}
return isParameterTagCompatible(a / 6, b / 6);
}
static inline unsigned findClosingTagRuntime(const char* s, unsigned p)
{
return s[p] == 0 ? throw std::runtime_error("unmatched tag <")
: s[p] == '>' ? p : findClosingTagRuntime(s, p + 1);
}
static inline uint64_t getParameterTagRuntime(const char* s, unsigned p = 0)
{
return s[p] == 0
? 0
: s[p] == '<'
? (std::strncmp(s + p, "<int>", 5) == 0
? getParameterTagRuntime(
s, findClosingTagRuntime(s, p)) *
6 +
1
: std::strncmp(s + p, "<uint>", 6) == 0
? getParameterTagRuntime(
s, findClosingTagRuntime(s, p)) *
6 +
2
: (std::strncmp(s + p, "<float>", 7) == 0 ||
std::strncmp(s + p, "<double>", 8) == 0)
? getParameterTagRuntime(
s, findClosingTagRuntime(s, p)) *
6 +
3
: (std::strncmp(s + p, "<str>", 5) ==
0 ||
std::strncmp(s + p, "<string>", 8) ==
0)
? getParameterTagRuntime(
s, findClosingTagRuntime(
s, p)) *
6 +
4
: std::strncmp(s + p, "<path>",
6) == 0
? getParameterTagRuntime(
s,
findClosingTagRuntime(
s, p)) *
6 +
5
: throw std::runtime_error(
"invalid parameter "
"type"))
: getParameterTagRuntime(s, p + 1);
}
constexpr uint64_t get_parameter_tag(ConstStr s, unsigned p = 0)
{
return p == s.size()
? 0
: s[p] == '<'
? (isInt(s, p)
? get_parameter_tag(s, findClosingTag(s, p)) * 6 + 1
: isUint(s, p)
? get_parameter_tag(s, findClosingTag(s, p)) *
6 +
2
: isFloat(s, p)
? get_parameter_tag(
s, findClosingTag(s, p)) *
6 +
3
: isStr(s, p)
? get_parameter_tag(
s, findClosingTag(s, p)) *
6 +
4
: isPath(s, p)
? get_parameter_tag(
s, findClosingTag(
s, p)) *
6 +
5
: throw std::runtime_error(
"invalid parameter "
"type"))
: get_parameter_tag(s, p + 1);
}
template <typename... T> struct S
{
template <typename U> using push = S<U, T...>;
template <typename U> using push_back = S<T..., U>;
template <template <typename... Args> class U> using rebind = U<T...>;
};
template <typename F, typename Set> struct CallHelper;
template <typename F, typename... Args> struct CallHelper<F, S<Args...>>
{
template <typename F1, typename... Args1,
typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
static char __test(int);
template <typename...> static int __test(...);
static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char);
};
template <int N> struct SingleTagToType
{
};
template <> struct SingleTagToType<1>
{
using type = int64_t;
};
template <> struct SingleTagToType<2>
{
using type = uint64_t;
};
template <> struct SingleTagToType<3>
{
using type = double;
};
template <> struct SingleTagToType<4>
{
using type = std::string;
};
template <> struct SingleTagToType<5>
{
using type = std::string;
};
template <uint64_t Tag> struct Arguments
{
using subarguments = typename Arguments<Tag / 6>::type;
using type = typename subarguments::template push<
typename SingleTagToType<Tag % 6>::type>;
};
template <> struct Arguments<0>
{
using type = S<>;
};
template <typename... T> struct LastElementType
{
using type =
typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type;
};
template <> struct LastElementType<>
{
};
// from
// http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth
template <class T> using Invoke = typename T::type;
template <unsigned...> struct Seq
{
using type = Seq;
};
template <class S1, class S2> struct concat;
template <unsigned... I1, unsigned... I2>
struct concat<Seq<I1...>, Seq<I2...>> : Seq<I1..., (sizeof...(I1) + I2)...>
{
};
template <class S1, class S2> using Concat = Invoke<concat<S1, S2>>;
template <unsigned N> struct gen_seq;
template <unsigned N> using GenSeq = Invoke<gen_seq<N>>;
template <unsigned N> struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>>
{
};
template <> struct gen_seq<0> : Seq<>
{
};
template <> struct gen_seq<1> : Seq<0>
{
};
template <typename Seq, typename Tuple> struct PopBackHelper;
template <unsigned... N, typename Tuple> struct PopBackHelper<Seq<N...>, Tuple>
{
template <template <typename... Args> class U>
using rebind = U<typename std::tuple_element<N, Tuple>::type...>;
};
template <typename... T>
struct PopBack //: public PopBackHelper<typename
// gen_seq<sizeof...(T)-1>::type, std::tuple<T...>>
{
template <template <typename... Args> class U>
using rebind =
typename PopBackHelper<typename gen_seq<sizeof...(T) - 1>::type,
std::tuple<T...>>::template rebind<U>;
};
template <> struct PopBack<>
{
template <template <typename... Args> class U> using rebind = U<>;
};
// from
// http://stackoverflow.com/questions/2118541/check-if-c0x-parameter-pack-contains-a-type
template <typename Tp, typename... List> struct Contains : std::true_type
{
};
template <typename Tp, typename Head, typename... Rest>
struct Contains<Tp, Head, Rest...>
: std::conditional<std::is_same<Tp, Head>::value, std::true_type,
Contains<Tp, Rest...>>::type
{
};
template <typename Tp> struct Contains<Tp> : std::false_type
{
};
template <typename T> struct EmptyContext
{
};
template <typename T> struct promote
{
using type = T;
};
#define BMCWEB_INTERNAL_PROMOTE_TYPE(t1, t2) \
template <> struct promote<t1> \
{ \
using type = t2; \
}
BMCWEB_INTERNAL_PROMOTE_TYPE(char, int64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(short, int64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(int, int64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(long, int64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(long long, int64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(unsigned char, uint64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(unsigned short, uint64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(unsigned int, uint64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(unsigned long, uint64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(unsigned long long, uint64_t);
BMCWEB_INTERNAL_PROMOTE_TYPE(float, double);
#undef BMCWEB_INTERNAL_PROMOTE_TYPE
template <typename T> using promote_t = typename promote<T>::type;
} // namespace black_magic
namespace detail
{
template <class T, std::size_t N, class... Args>
struct GetIndexOfElementFromTupleByTypeImpl
{
static constexpr auto value = N;
};
template <class T, std::size_t N, class... Args>
struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...>
{
static constexpr auto value = N;
};
template <class T, std::size_t N, class U, class... Args>
struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...>
{
static constexpr auto value =
GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value;
};
} // namespace detail
namespace utility
{
template <class T, class... Args> T& getElementByType(std::tuple<Args...>& t)
{
return std::get<
detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t);
}
template <typename T> struct function_traits;
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{
using parent_t = function_traits<decltype(&T::operator())>;
static const size_t arity = parent_t::arity;
using result_type = typename parent_t::result_type;
template <size_t i> using arg = typename parent_t::template arg<i>;
};
template <typename ClassType, typename r, typename... Args>
struct function_traits<r (ClassType::*)(Args...) const>
{
static const size_t arity = sizeof...(Args);
using result_type = r;
template <size_t i>
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
template <typename ClassType, typename r, typename... Args>
struct function_traits<r (ClassType::*)(Args...)>
{
static const size_t arity = sizeof...(Args);
using result_type = r;
template <size_t i>
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
template <typename r, typename... Args>
struct function_traits<std::function<r(Args...)>>
{
static const size_t arity = sizeof...(Args);
using result_type = r;
template <size_t i>
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
inline static std::string base64encode(
const char* data, size_t size,
const char* key =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
{
std::string ret;
ret.resize((size + 2) / 3 * 4);
auto it = ret.begin();
while (size >= 3)
{
*it++ = key[(((unsigned char)*data) & 0xFC) >> 2];
unsigned char h = (((unsigned char)*data++) & 0x03) << 4;
*it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)];
h = (((unsigned char)*data++) & 0x0F) << 2;
*it++ = key[h | ((((unsigned char)*data) & 0xC0) >> 6)];
*it++ = key[((unsigned char)*data++) & 0x3F];
size -= 3;
}
if (size == 1)
{
*it++ = key[(((unsigned char)*data) & 0xFC) >> 2];
unsigned char h = (((unsigned char)*data++) & 0x03) << 4;
*it++ = key[h];
*it++ = '=';
*it++ = '=';
}
else if (size == 2)
{
*it++ = key[(((unsigned char)*data) & 0xFC) >> 2];
unsigned char h = (((unsigned char)*data++) & 0x03) << 4;
*it++ = key[h | ((((unsigned char)*data) & 0xF0) >> 4)];
h = (((unsigned char)*data++) & 0x0F) << 2;
*it++ = key[h];
*it++ = '=';
}
return ret;
}
inline static std::string base64encodeUrlsafe(const char* data, size_t size)
{
return base64encode(
data, size,
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
}
// TODO this is temporary and should be deleted once base64 is refactored out of
// crow
inline bool base64Decode(const std::string_view input, std::string& output)
{
static const char nop = -1;
// See note on encoding_data[] in above function
static const char decodingData[] = {
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, nop, nop, nop, nop, nop, nop, nop, 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, nop, nop, nop, nop, nop, nop, 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, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop};
size_t inputLength = input.size();
// allocate space for output string
output.clear();
output.reserve(((inputLength + 2) / 3) * 4);
// for each 4-bytes sequence from the input, extract 4 6-bits sequences by
// droping first two bits
// and regenerate into 3 8-bits sequences
for (size_t i = 0; i < inputLength; i++)
{
char base64code0;
char base64code1;
char base64code2 = 0; // initialized to 0 to suppress warnings
char base64code3;
base64code0 = decodingData[static_cast<int>(input[i])]; // NOLINT
if (base64code0 == nop)
{ // non base64 character
return false;
}
if (!(++i < inputLength))
{ // we need at least two input bytes for first
// byte output
return false;
}
base64code1 = decodingData[static_cast<int>(input[i])]; // NOLINT
if (base64code1 == nop)
{ // non base64 character
return false;
}
output +=
static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
if (++i < inputLength)
{
char c = input[i];
if (c == '=')
{ // padding , end of input
return (base64code1 & 0x0f) == 0;
}
base64code2 = decodingData[static_cast<int>(input[i])]; // NOLINT
if (base64code2 == nop)
{ // non base64 character
return false;
}
output += static_cast<char>(((base64code1 << 4) & 0xf0) |
((base64code2 >> 2) & 0x0f));
}
if (++i < inputLength)
{
char c = input[i];
if (c == '=')
{ // padding , end of input
return (base64code2 & 0x03) == 0;
}
base64code3 = decodingData[static_cast<int>(input[i])]; // NOLINT
if (base64code3 == nop)
{ // non base64 character
return false;
}
output +=
static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
}
}
return true;
}
inline void escapeHtml(std::string& data)
{
std::string buffer;
// less than 5% of characters should be larger, so reserve a buffer of the
// right size
buffer.reserve(data.size() * 1.05);
for (size_t pos = 0; pos != data.size(); ++pos)
{
switch (data[pos])
{
case '&':
buffer.append("&amp;");
break;
case '\"':
buffer.append("&quot;");
break;
case '\'':
buffer.append("&apos;");
break;
case '<':
buffer.append("&lt;");
break;
case '>':
buffer.append("&gt;");
break;
default:
buffer.append(&data[pos], 1);
break;
}
}
data.swap(buffer);
}
inline void convertToLinks(std::string& s)
{
const static std::regex r{"(&quot;@odata\\.((id)|(Context))&quot;[ \\n]*:[ "
"\\n]*)(&quot;((?!&quot;).*)&quot;)"};
s = std::regex_replace(s, r, "$1<a href=\"$6\">$5</a>");
const static std::regex nextLink{
"(&quot;Members@odata\\.((nextLink))&quot;[ \\n]*:[ "
"\\n]*)(&quot;((?!&quot;).*)&quot;)"};
s = std::regex_replace(s, nextLink, "$1<a href=\"$5\">$4</a>");
}
} // namespace utility
} // namespace crow