Crow  0.3
A C++ microframework for the web
utility.h
1 #pragma once
2 
3 #include <cstdint>
4 #include <stdexcept>
5 #include <tuple>
6 #include <type_traits>
7 #include <cstring>
8 #include <functional>
9 #include <string>
10 #include <unordered_map>
11 
12 #include "crow/settings.h"
13 
14 namespace crow
15 {
16  namespace black_magic
17  {
18 #ifndef CROW_MSVC_WORKAROUND
19  struct OutOfRange
20  {
21  OutOfRange(unsigned /*pos*/, unsigned /*length*/) {}
22  };
23  constexpr unsigned requires_in_range(unsigned i, unsigned len)
24  {
25  return i >= len ? throw OutOfRange(i, len) : i;
26  }
27 
28  /// A constant string implementation.
29  class const_str
30  {
31  const char* const begin_;
32  unsigned size_;
33 
34  public:
35  template<unsigned N>
36  constexpr const_str(const char (&arr)[N]):
37  begin_(arr), size_(N - 1)
38  {
39  static_assert(N >= 1, "not a string literal");
40  }
41  constexpr char operator[](unsigned i) const
42  {
43  return requires_in_range(i, size_), begin_[i];
44  }
45 
46  constexpr operator const char*() const
47  {
48  return begin_;
49  }
50 
51  constexpr const char* begin() const { return begin_; }
52  constexpr const char* end() const { return begin_ + size_; }
53 
54  constexpr unsigned size() const
55  {
56  return size_;
57  }
58  };
59 
60  constexpr unsigned find_closing_tag(const_str s, unsigned p)
61  {
62  return s[p] == '>' ? p : find_closing_tag(s, p + 1);
63  }
64 
65  constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0)
66  {
67  return i == s.size() ? f == 0 :
68  f < 0 || f >= 2 ? false :
69  s[i] == '<' ? is_valid(s, i + 1, f + 1) :
70  s[i] == '>' ? is_valid(s, i + 1, f - 1) :
71  is_valid(s, i + 1, f);
72  }
73 
74  constexpr bool is_equ_p(const char* a, const char* b, unsigned n)
75  {
76  return *a == 0 && *b == 0 && n == 0 ? true :
77  (*a == 0 || *b == 0) ? false :
78  n == 0 ? true :
79  *a != *b ? false :
80  is_equ_p(a + 1, b + 1, n - 1);
81  }
82 
83  constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n)
84  {
85  return ai + n > a.size() || bi + n > b.size() ? false :
86  n == 0 ? true :
87  a[ai] != b[bi] ? false :
88  is_equ_n(a, ai + 1, b, bi + 1, n - 1);
89  }
90 
91  constexpr bool is_int(const_str s, unsigned i)
92  {
93  return is_equ_n(s, i, "<int>", 0, 5);
94  }
95 
96  constexpr bool is_uint(const_str s, unsigned i)
97  {
98  return is_equ_n(s, i, "<uint>", 0, 6);
99  }
100 
101  constexpr bool is_float(const_str s, unsigned i)
102  {
103  return is_equ_n(s, i, "<float>", 0, 7) ||
104  is_equ_n(s, i, "<double>", 0, 8);
105  }
106 
107  constexpr bool is_str(const_str s, unsigned i)
108  {
109  return is_equ_n(s, i, "<str>", 0, 5) ||
110  is_equ_n(s, i, "<string>", 0, 8);
111  }
112 
113  constexpr bool is_path(const_str s, unsigned i)
114  {
115  return is_equ_n(s, i, "<path>", 0, 6);
116  }
117 #endif
118  template<typename T>
120  {
121  static const int value = 0;
122  };
123 #define CROW_INTERNAL_PARAMETER_TAG(t, i) \
124  template<> \
125  struct parameter_tag<t> \
126  { \
127  static const int value = i; \
128  }
129  CROW_INTERNAL_PARAMETER_TAG(int, 1);
130  CROW_INTERNAL_PARAMETER_TAG(char, 1);
131  CROW_INTERNAL_PARAMETER_TAG(short, 1);
132  CROW_INTERNAL_PARAMETER_TAG(long, 1);
133  CROW_INTERNAL_PARAMETER_TAG(long long, 1);
134  CROW_INTERNAL_PARAMETER_TAG(unsigned int, 2);
135  CROW_INTERNAL_PARAMETER_TAG(unsigned char, 2);
136  CROW_INTERNAL_PARAMETER_TAG(unsigned short, 2);
137  CROW_INTERNAL_PARAMETER_TAG(unsigned long, 2);
138  CROW_INTERNAL_PARAMETER_TAG(unsigned long long, 2);
139  CROW_INTERNAL_PARAMETER_TAG(double, 3);
140  CROW_INTERNAL_PARAMETER_TAG(std::string, 4);
141 #undef CROW_INTERNAL_PARAMETER_TAG
142  template<typename... Args>
144 
145  template<>
147  {
148  static const int value = 0;
149  };
150 
151  template<typename Arg, typename... Args>
153  {
154  static const int sub_value =
155  compute_parameter_tag_from_args_list<Args...>::value;
156  static const int value =
158  };
159 
160  static inline bool is_parameter_tag_compatible(uint64_t a, uint64_t b)
161  {
162  if (a == 0)
163  return b == 0;
164  if (b == 0)
165  return a == 0;
166  int sa = a % 6;
167  int sb = a % 6;
168  if (sa == 5) sa = 4;
169  if (sb == 5) sb = 4;
170  if (sa != sb)
171  return false;
172  return is_parameter_tag_compatible(a / 6, b / 6);
173  }
174 
175  static inline unsigned find_closing_tag_runtime(const char* s, unsigned p)
176  {
177  return s[p] == 0 ? throw std::runtime_error("unmatched tag <") :
178  s[p] == '>' ? p :
179  find_closing_tag_runtime(s, p + 1);
180  }
181 
182  static inline uint64_t get_parameter_tag_runtime(const char* s, unsigned p = 0)
183  {
184  return s[p] == 0 ? 0 :
185  s[p] == '<' ? (
186  std::strncmp(s + p, "<int>", 5) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 :
187  std::strncmp(s + p, "<uint>", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 :
188  (std::strncmp(s + p, "<float>", 7) == 0 ||
189  std::strncmp(s + p, "<double>", 8) == 0) ?
190  get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 :
191  (std::strncmp(s + p, "<str>", 5) == 0 ||
192  std::strncmp(s + p, "<string>", 8) == 0) ?
193  get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 :
194  std::strncmp(s + p, "<path>", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 5 :
195  throw std::runtime_error("invalid parameter type")) :
196  get_parameter_tag_runtime(s, p + 1);
197  }
198 #ifndef CROW_MSVC_WORKAROUND
199  constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0)
200  {
201  return p == s.size() ? 0 :
202  s[p] == '<' ? (
203  is_int(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 :
204  is_uint(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 :
205  is_float(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 :
206  is_str(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 :
207  is_path(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 :
208  throw std::runtime_error("invalid parameter type")) :
209  get_parameter_tag(s, p + 1);
210  }
211 #endif
212 
213  template<typename... T>
214  struct S
215  {
216  template<typename U>
217  using push = S<U, T...>;
218  template<typename U>
219  using push_back = S<T..., U>;
220  template<template<typename... Args> class U>
221  using rebind = U<T...>;
222  };
223  template<typename F, typename Set>
224  struct CallHelper;
225  template<typename F, typename... Args>
226  struct CallHelper<F, S<Args...>>
227  {
228  template<typename F1, typename... Args1, typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
229  static char __test(int);
230 
231  template<typename...>
232  static int __test(...);
233 
234  static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char);
235  };
236 
237 
238  template<int N>
240  {};
241 
242  template<>
244  {
245  using type = int64_t;
246  };
247 
248  template<>
250  {
251  using type = uint64_t;
252  };
253 
254  template<>
256  {
257  using type = double;
258  };
259 
260  template<>
262  {
263  using type = std::string;
264  };
265 
266  template<>
268  {
269  using type = std::string;
270  };
271 
272 
273  template<uint64_t Tag>
274  struct arguments
275  {
276  using subarguments = typename arguments<Tag / 6>::type;
277  using type =
278  typename subarguments::template push<typename single_tag_to_type<Tag % 6>::type>;
279  };
280 
281  template<>
282  struct arguments<0>
283  {
284  using type = S<>;
285  };
286 
287  template<typename... T>
289  {
290  using type = typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type;
291  };
292 
293 
294  template<>
296  {};
297 
298 
299  // from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth
300  template<class T>
301  using Invoke = typename T::type;
302 
303  template<unsigned...>
304  struct seq
305  {
306  using type = seq;
307  };
308 
309  template<class S1, class S2>
310  struct concat;
311 
312  template<unsigned... I1, unsigned... I2>
313  struct concat<seq<I1...>, seq<I2...>> : seq<I1..., (sizeof...(I1) + I2)...>
314  {};
315 
316  template<class S1, class S2>
317  using Concat = Invoke<concat<S1, S2>>;
318 
319  template<unsigned N>
320  struct gen_seq;
321  template<unsigned N>
322  using GenSeq = Invoke<gen_seq<N>>;
323 
324  template<unsigned N>
325  struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>>
326  {};
327 
328  template<>
329  struct gen_seq<0> : seq<>
330  {};
331  template<>
332  struct gen_seq<1> : seq<0>
333  {};
334 
335  template<typename Seq, typename Tuple>
337 
338  template<unsigned... N, typename Tuple>
339  struct pop_back_helper<seq<N...>, Tuple>
340  {
341  template<template<typename... Args> class U>
342  using rebind = U<typename std::tuple_element<N, Tuple>::type...>;
343  };
344 
345  template<typename... T>
346  struct pop_back //: public pop_back_helper<typename gen_seq<sizeof...(T)-1>::type, std::tuple<T...>>
347  {
348  template<template<typename... Args> class U>
349  using rebind = typename pop_back_helper<typename gen_seq<sizeof...(T) - 1>::type, std::tuple<T...>>::template rebind<U>;
350  };
351 
352  template<>
353  struct pop_back<>
354  {
355  template<template<typename... Args> class U>
356  using rebind = U<>;
357  };
358 
359  // from http://stackoverflow.com/questions/2118541/check-if-c0x-parameter-pack-contains-a-type
360  template<typename Tp, typename... List>
361  struct contains : std::true_type
362  {};
363 
364  template<typename Tp, typename Head, typename... Rest>
365  struct contains<Tp, Head, Rest...> : std::conditional<std::is_same<Tp, Head>::value, std::true_type, contains<Tp, Rest...>>::type
366  {};
367 
368  template<typename Tp>
369  struct contains<Tp> : std::false_type
370  {};
371 
372  template<typename T>
374  {};
375 
376  template<typename T>
377  struct promote
378  {
379  using type = T;
380  };
381 
382 #define CROW_INTERNAL_PROMOTE_TYPE(t1, t2) \
383  template<> \
384  struct promote<t1> \
385  { \
386  using type = t2; \
387  }
388 
389  CROW_INTERNAL_PROMOTE_TYPE(char, int64_t);
390  CROW_INTERNAL_PROMOTE_TYPE(short, int64_t);
391  CROW_INTERNAL_PROMOTE_TYPE(int, int64_t);
392  CROW_INTERNAL_PROMOTE_TYPE(long, int64_t);
393  CROW_INTERNAL_PROMOTE_TYPE(long long, int64_t);
394  CROW_INTERNAL_PROMOTE_TYPE(unsigned char, uint64_t);
395  CROW_INTERNAL_PROMOTE_TYPE(unsigned short, uint64_t);
396  CROW_INTERNAL_PROMOTE_TYPE(unsigned int, uint64_t);
397  CROW_INTERNAL_PROMOTE_TYPE(unsigned long, uint64_t);
398  CROW_INTERNAL_PROMOTE_TYPE(unsigned long long, uint64_t);
399  CROW_INTERNAL_PROMOTE_TYPE(float, double);
400 #undef CROW_INTERNAL_PROMOTE_TYPE
401 
402  template<typename T>
403  using promote_t = typename promote<T>::type;
404 
405  } // namespace black_magic
406 
407  namespace detail
408  {
409 
410  template<class T, std::size_t N, class... Args>
412  {
413  static constexpr auto value = N;
414  };
415 
416  template<class T, std::size_t N, class... Args>
418  {
419  static constexpr auto value = N;
420  };
421 
422  template<class T, std::size_t N, class U, class... Args>
424  {
425  static constexpr auto value = get_index_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value;
426  };
427 
428  } // namespace detail
429 
430  namespace utility
431  {
432  template<class T, class... Args>
433  T& get_element_by_type(std::tuple<Args...>& t)
434  {
435  return std::get<detail::get_index_of_element_from_tuple_by_type_impl<T, 0, Args...>::value>(t);
436  }
437 
438  template<typename T>
440 
441 #ifndef CROW_MSVC_WORKAROUND
442  template<typename T>
443  struct function_traits : public function_traits<decltype(&T::operator())>
444  {
445  using parent_t = function_traits<decltype(&T::operator())>;
446  static const size_t arity = parent_t::arity;
447  using result_type = typename parent_t::result_type;
448  template<size_t i>
449  using arg = typename parent_t::template arg<i>;
450  };
451 #endif
452 
453  template<typename ClassType, typename R, typename... Args>
454  struct function_traits<R (ClassType::*)(Args...) const>
455  {
456  static const size_t arity = sizeof...(Args);
457 
458  typedef R result_type;
459 
460  template<size_t i>
461  using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
462  };
463 
464  template<typename ClassType, typename R, typename... Args>
465  struct function_traits<R (ClassType::*)(Args...)>
466  {
467  static const size_t arity = sizeof...(Args);
468 
469  typedef R result_type;
470 
471  template<size_t i>
472  using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
473  };
474 
475  template<typename R, typename... Args>
476  struct function_traits<std::function<R(Args...)>>
477  {
478  static const size_t arity = sizeof...(Args);
479 
480  typedef R result_type;
481 
482  template<size_t i>
483  using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
484  };
485 
486  inline static std::string base64encode(const unsigned char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
487  {
488  std::string ret;
489  ret.resize((size + 2) / 3 * 4);
490  auto it = ret.begin();
491  while (size >= 3)
492  {
493  *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
494  unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
495  *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xF0) >> 4)];
496  h = (static_cast<unsigned char>(*data++) & 0x0F) << 2;
497  *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xC0) >> 6)];
498  *it++ = key[static_cast<unsigned char>(*data++) & 0x3F];
499 
500  size -= 3;
501  }
502  if (size == 1)
503  {
504  *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
505  unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
506  *it++ = key[h];
507  *it++ = '=';
508  *it++ = '=';
509  }
510  else if (size == 2)
511  {
512  *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
513  unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
514  *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xF0) >> 4)];
515  h = (static_cast<unsigned char>(*data++) & 0x0F) << 2;
516  *it++ = key[h];
517  *it++ = '=';
518  }
519  return ret;
520  }
521 
522  inline static std::string base64encode(std::string data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
523  {
524  return base64encode((const unsigned char*)data.c_str(), size, key);
525  }
526 
527  inline static std::string base64encode_urlsafe(const unsigned char* data, size_t size)
528  {
529  return base64encode(data, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
530  }
531 
532  inline static std::string base64encode_urlsafe(std::string data, size_t size)
533  {
534  return base64encode((const unsigned char*)data.c_str(), size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
535  }
536 
537  inline static std::string base64decode(const char* data, size_t size, bool urlsafe = false)
538  {
539  // clang-format off
540  std::unordered_map<char, unsigned char> key ({
541  {'A', 0},{'B', 1},{'C', 2},{'D', 3},{'E', 4},{'F', 5},{'G', 6},{'H', 7},{'I', 8},{'J', 9},
542  {'K', 10},{'L', 11},{'M', 12},{'N', 13},{'O', 14},{'P', 15},{'Q', 16},{'R', 17},{'S', 18},{'T', 19},
543  {'U', 20},{'V', 21},{'W', 22},{'X', 23},{'Y', 24},{'Z', 25},{'a', 26},{'b', 27},{'c', 28},{'d', 29},
544  {'e', 30},{'f', 31},{'g', 32},{'h', 33},{'i', 34},{'j', 35},{'k', 36},{'l', 37},{'m', 38},{'n', 39},
545  {'o', 40},{'p', 41},{'q', 42},{'r', 43},{'s', 44},{'t', 45},{'u', 46},{'v', 47},{'w', 48},{'x', 49},
546  {'y', 50},{'z', 51},{'0', 52},{'1', 53},{'2', 54},{'3', 55},{'4', 56},{'5', 57},{'6', 58},{'7', 59},
547  {'8', 60},{'9', 61},{urlsafe ? '-' : '+', 62},{urlsafe ? '_' : '/', 63}});
548 
549  // clang-format on
550 
551  // Not padded
552  if (size % 4 == 2) // missing last 2 characters
553  size = (size / 4 * 3) + 1; // Not subtracting extra characters because they're truncated in int division
554  else if (size % 4 == 3) // missing last character
555  size = (size / 4 * 3) + 2; // Not subtracting extra characters because they're truncated in int division
556 
557  // Padded
558  else if (data[size - 2] == '=') // padded with '=='
559  size = (size / 4 * 3) - 2; // == padding means the last block only has 1 character instead of 3, hence the '-2'
560  else if (data[size - 1] == '=') // padded with '='
561  size = (size / 4 * 3) - 1; // = padding means the last block only has 2 character instead of 3, hence the '-1'
562 
563  // Padding not needed
564  else
565  size = size / 4 * 3;
566 
567  std::string ret;
568  ret.resize(size);
569  auto it = ret.begin();
570 
571  // These will be used to decode 1 character at a time
572  unsigned char odd; // char1 and char3
573  unsigned char even; // char2 and char4
574 
575  // Take 4 character blocks to turn into 3
576  while (size >= 3)
577  {
578  // dec_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
579  odd = key[*data++];
580  even = key[*data++];
581  *it++ = (odd << 2) | ((even & 0x30) >> 4);
582  // dec_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
583  odd = key[*data++];
584  *it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
585  // dec_char3 = ((char3 AND 00000011) shifted 6 bits left) OR (char4)
586  even = key[*data++];
587  *it++ = ((odd & 0x03) << 6) | (even);
588 
589  size -= 3;
590  }
591  if (size == 2)
592  {
593  // d_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
594  odd = key[*data++];
595  even = key[*data++];
596  *it++ = (odd << 2) | ((even & 0x30) >> 4);
597  // d_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
598  odd = key[*data++];
599  *it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
600  }
601  else if (size == 1)
602  {
603  // d_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
604  odd = key[*data++];
605  even = key[*data++];
606  *it++ = (odd << 2) | ((even & 0x30) >> 4);
607  }
608  return ret;
609  }
610 
611  inline static std::string base64decode(std::string data, size_t size, bool urlsafe = false)
612  {
613  return base64decode(data.c_str(), size, urlsafe);
614  }
615 
616  } // namespace utility
617 } // namespace crow
crow::black_magic::S
Definition: utility.h:214
crow::black_magic::OutOfRange
Definition: utility.h:19
crow::black_magic::single_tag_to_type< 5 >
Definition: utility.h:267
crow::black_magic::compute_parameter_tag_from_args_list
Definition: utility.h:143
crow::black_magic::pop_back_helper
Definition: utility.h:336
crow::black_magic::last_element_type
Definition: utility.h:288
crow::utility::function_traits
Definition: utility.h:439
crow::black_magic::const_str
A constant string implementation.
Definition: utility.h:29
crow::black_magic::CallHelper
Definition: utility.h:224
crow::black_magic::single_tag_to_type< 1 >
Definition: utility.h:243
crow::detail::get_index_of_element_from_tuple_by_type_impl
Definition: utility.h:411
crow::black_magic::single_tag_to_type< 4 >
Definition: utility.h:261
crow::black_magic::seq
Definition: utility.h:304
crow::black_magic::empty_context
Definition: utility.h:373
crow::black_magic::arguments
Definition: utility.h:274
crow::black_magic::gen_seq
Definition: utility.h:320
crow::black_magic::promote
Definition: utility.h:377
crow::black_magic::concat
Definition: utility.h:310
crow::black_magic::pop_back
Definition: utility.h:346
crow::black_magic::single_tag_to_type< 3 >
Definition: utility.h:255
crow::black_magic::contains
Definition: utility.h:361
crow::black_magic::single_tag_to_type< 2 >
Definition: utility.h:249
crow::black_magic::parameter_tag
Definition: utility.h:119
crow::black_magic::single_tag_to_type
Definition: utility.h:239