Crow  1.1
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 <cctype>
9 #include <functional>
10 #include <string>
11 #include <sstream>
12 #include <unordered_map>
13 #include <random>
14 #include <algorithm>
15 
16 #include "crow/settings.h"
17 
18 #if defined(CROW_CAN_USE_CPP17) && !defined(CROW_FILESYSTEM_IS_EXPERIMENTAL)
19 #include <filesystem>
20 #endif
21 
22 // TODO(EDev): Adding C++20's [[likely]] and [[unlikely]] attributes might be useful
23 #if defined(__GNUG__) || defined(__clang__)
24 #define CROW_LIKELY(X) __builtin_expect(!!(X), 1)
25 #define CROW_UNLIKELY(X) __builtin_expect(!!(X), 0)
26 #else
27 #define CROW_LIKELY(X) (X)
28 #define CROW_UNLIKELY(X) (X)
29 #endif
30 
31 namespace crow
32 {
33  /// @cond SKIP
34  namespace black_magic
35  {
36 #ifndef CROW_MSVC_WORKAROUND
37  /// Out of Range Exception for const_str
38  struct OutOfRange
39  {
40  OutOfRange(unsigned /*pos*/, unsigned /*length*/) {}
41  };
42  /// Helper function to throw an exception if i is larger than len
43  constexpr unsigned requires_in_range(unsigned i, unsigned len)
44  {
45  return i >= len ? throw OutOfRange(i, len) : i;
46  }
47 
48  /// A constant string implementation.
49  class const_str
50  {
51  const char* const begin_;
52  unsigned size_;
53 
54  public:
55  template<unsigned N>
56  constexpr const_str(const char (&arr)[N]):
57  begin_(arr), size_(N - 1)
58  {
59  static_assert(N >= 1, "not a string literal");
60  }
61  constexpr char operator[](unsigned i) const
62  {
63  return requires_in_range(i, size_), begin_[i];
64  }
65 
66  constexpr operator const char*() const
67  {
68  return begin_;
69  }
70 
71  constexpr const char* begin() const { return begin_; }
72  constexpr const char* end() const { return begin_ + size_; }
73 
74  constexpr unsigned size() const
75  {
76  return size_;
77  }
78  };
79 
80  constexpr unsigned find_closing_tag(const_str s, unsigned p)
81  {
82  return s[p] == '>' ? p : find_closing_tag(s, p + 1);
83  }
84 
85  /// Check that the CROW_ROUTE string is valid
86  constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0)
87  {
88  return i == s.size() ? f == 0 :
89  f < 0 || f >= 2 ? false :
90  s[i] == '<' ? is_valid(s, i + 1, f + 1) :
91  s[i] == '>' ? is_valid(s, i + 1, f - 1) :
92  is_valid(s, i + 1, f);
93  }
94 
95  constexpr bool is_equ_p(const char* a, const char* b, unsigned n)
96  {
97  return *a == 0 && *b == 0 && n == 0 ? true :
98  (*a == 0 || *b == 0) ? false :
99  n == 0 ? true :
100  *a != *b ? false :
101  is_equ_p(a + 1, b + 1, n - 1);
102  }
103 
104  constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n)
105  {
106  return ai + n > a.size() || bi + n > b.size() ? false :
107  n == 0 ? true :
108  a[ai] != b[bi] ? false :
109  is_equ_n(a, ai + 1, b, bi + 1, n - 1);
110  }
111 
112  constexpr bool is_int(const_str s, unsigned i)
113  {
114  return is_equ_n(s, i, "<int>", 0, 5);
115  }
116 
117  constexpr bool is_uint(const_str s, unsigned i)
118  {
119  return is_equ_n(s, i, "<uint>", 0, 6);
120  }
121 
122  constexpr bool is_float(const_str s, unsigned i)
123  {
124  return is_equ_n(s, i, "<float>", 0, 7) ||
125  is_equ_n(s, i, "<double>", 0, 8);
126  }
127 
128  constexpr bool is_str(const_str s, unsigned i)
129  {
130  return is_equ_n(s, i, "<str>", 0, 5) ||
131  is_equ_n(s, i, "<string>", 0, 8);
132  }
133 
134  constexpr bool is_path(const_str s, unsigned i)
135  {
136  return is_equ_n(s, i, "<path>", 0, 6);
137  }
138 #endif
139  template<typename T>
140  struct parameter_tag
141  {
142  static const int value = 0;
143  };
144 #define CROW_INTERNAL_PARAMETER_TAG(t, i) \
145  template<> \
146  struct parameter_tag<t> \
147  { \
148  static const int value = i; \
149  }
150  CROW_INTERNAL_PARAMETER_TAG(int, 1);
151  CROW_INTERNAL_PARAMETER_TAG(char, 1);
152  CROW_INTERNAL_PARAMETER_TAG(short, 1);
153  CROW_INTERNAL_PARAMETER_TAG(long, 1);
154  CROW_INTERNAL_PARAMETER_TAG(long long, 1);
155  CROW_INTERNAL_PARAMETER_TAG(unsigned int, 2);
156  CROW_INTERNAL_PARAMETER_TAG(unsigned char, 2);
157  CROW_INTERNAL_PARAMETER_TAG(unsigned short, 2);
158  CROW_INTERNAL_PARAMETER_TAG(unsigned long, 2);
159  CROW_INTERNAL_PARAMETER_TAG(unsigned long long, 2);
160  CROW_INTERNAL_PARAMETER_TAG(double, 3);
161  CROW_INTERNAL_PARAMETER_TAG(std::string, 4);
162 #undef CROW_INTERNAL_PARAMETER_TAG
163  template<typename... Args>
164  struct compute_parameter_tag_from_args_list;
165 
166  template<>
167  struct compute_parameter_tag_from_args_list<>
168  {
169  static const int value = 0;
170  };
171 
172  template<typename Arg, typename... Args>
173  struct compute_parameter_tag_from_args_list<Arg, Args...>
174  {
175  static const int sub_value =
176  compute_parameter_tag_from_args_list<Args...>::value;
177  static const int value =
178  parameter_tag<typename std::decay<Arg>::type>::value ? sub_value * 6 + parameter_tag<typename std::decay<Arg>::type>::value : sub_value;
179  };
180 
181  static inline bool is_parameter_tag_compatible(uint64_t a, uint64_t b)
182  {
183  if (a == 0)
184  return b == 0;
185  if (b == 0)
186  return a == 0;
187  int sa = a % 6;
188  int sb = a % 6;
189  if (sa == 5) sa = 4;
190  if (sb == 5) sb = 4;
191  if (sa != sb)
192  return false;
193  return is_parameter_tag_compatible(a / 6, b / 6);
194  }
195 
196  static inline unsigned find_closing_tag_runtime(const char* s, unsigned p)
197  {
198  return s[p] == 0 ? throw std::runtime_error("unmatched tag <") :
199  s[p] == '>' ? p :
200  find_closing_tag_runtime(s, p + 1);
201  }
202 
203  static inline uint64_t get_parameter_tag_runtime(const char* s, unsigned p = 0)
204  {
205  return s[p] == 0 ? 0 :
206  s[p] == '<' ? (
207  std::strncmp(s + p, "<int>", 5) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 :
208  std::strncmp(s + p, "<uint>", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 :
209  (std::strncmp(s + p, "<float>", 7) == 0 ||
210  std::strncmp(s + p, "<double>", 8) == 0) ?
211  get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 :
212  (std::strncmp(s + p, "<str>", 5) == 0 ||
213  std::strncmp(s + p, "<string>", 8) == 0) ?
214  get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 :
215  std::strncmp(s + p, "<path>", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 5 :
216  throw std::runtime_error("invalid parameter type")) :
217  get_parameter_tag_runtime(s, p + 1);
218  }
219 #ifndef CROW_MSVC_WORKAROUND
220  constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0)
221  {
222  return p == s.size() ? 0 :
223  s[p] == '<' ? (
224  is_int(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 :
225  is_uint(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 :
226  is_float(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 :
227  is_str(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 :
228  is_path(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 :
229  throw std::runtime_error("invalid parameter type")) :
230  get_parameter_tag(s, p + 1);
231  }
232 #endif
233 
234  template<typename... T>
235  struct S
236  {
237  template<typename U>
238  using push = S<U, T...>;
239  template<typename U>
240  using push_back = S<T..., U>;
241  template<template<typename... Args> class U>
242  using rebind = U<T...>;
243  };
244 
245  // Check whether the template function can be called with specific arguments
246  template<typename F, typename Set>
247  struct CallHelper;
248  template<typename F, typename... Args>
249  struct CallHelper<F, S<Args...>>
250  {
251  template<typename F1, typename... Args1, typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
252  static char __test(int);
253 
254  template<typename...>
255  static int __test(...);
256 
257  static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char);
258  };
259 
260  // Check Tuple contains type T
261  template<typename T, typename Tuple>
262  struct has_type;
263 
264  template<typename T>
265  struct has_type<T, std::tuple<>> : std::false_type
266  {};
267 
268  template<typename T, typename U, typename... Ts>
269  struct has_type<T, std::tuple<U, Ts...>> : has_type<T, std::tuple<Ts...>>
270  {};
271 
272  template<typename T, typename... Ts>
273  struct has_type<T, std::tuple<T, Ts...>> : std::true_type
274  {};
275 
276  // Find index of type in tuple
277  template<class T, class Tuple>
278  struct tuple_index;
279 
280  template<class T, class... Types>
281  struct tuple_index<T, std::tuple<T, Types...>>
282  {
283  static const int value = 0;
284  };
285 
286  template<class T, class U, class... Types>
287  struct tuple_index<T, std::tuple<U, Types...>>
288  {
289  static const int value = 1 + tuple_index<T, std::tuple<Types...>>::value;
290  };
291 
292  // Extract element from forward tuple or get default
293 #ifdef CROW_CAN_USE_CPP14
294  template<typename T, typename Tup>
295  typename std::enable_if<has_type<T&, Tup>::value, typename std::decay<T>::type&&>::type
296  tuple_extract(Tup& tup)
297  {
298  return std::move(std::get<T&>(tup));
299  }
300 #else
301  template<typename T, typename Tup>
302  typename std::enable_if<has_type<T&, Tup>::value, T&&>::type
303  tuple_extract(Tup& tup)
304  {
305  return std::move(std::get<tuple_index<T&, Tup>::value>(tup));
306  }
307 #endif
308 
309  template<typename T, typename Tup>
310  typename std::enable_if<!has_type<T&, Tup>::value, T>::type
311  tuple_extract(Tup&)
312  {
313  return T{};
314  }
315 
316  // Kind of fold expressions in C++11
317  template<bool...>
318  struct bool_pack;
319  template<bool... bs>
320  using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
321 
322  template<int N>
323  struct single_tag_to_type
324  {};
325 
326  template<>
327  struct single_tag_to_type<1>
328  {
329  using type = int64_t;
330  };
331 
332  template<>
333  struct single_tag_to_type<2>
334  {
335  using type = uint64_t;
336  };
337 
338  template<>
339  struct single_tag_to_type<3>
340  {
341  using type = double;
342  };
343 
344  template<>
345  struct single_tag_to_type<4>
346  {
347  using type = std::string;
348  };
349 
350  template<>
351  struct single_tag_to_type<5>
352  {
353  using type = std::string;
354  };
355 
356 
357  template<uint64_t Tag>
358  struct arguments
359  {
360  using subarguments = typename arguments<Tag / 6>::type;
361  using type =
362  typename subarguments::template push<typename single_tag_to_type<Tag % 6>::type>;
363  };
364 
365  template<>
366  struct arguments<0>
367  {
368  using type = S<>;
369  };
370 
371  template<typename... T>
372  struct last_element_type
373  {
374  using type = typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type;
375  };
376 
377 
378  template<>
379  struct last_element_type<>
380  {};
381 
382 
383  // from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth
384  template<class T>
385  using Invoke = typename T::type;
386 
387  template<unsigned...>
388  struct seq
389  {
390  using type = seq;
391  };
392 
393  template<class S1, class S2>
394  struct concat;
395 
396  template<unsigned... I1, unsigned... I2>
397  struct concat<seq<I1...>, seq<I2...>> : seq<I1..., (sizeof...(I1) + I2)...>
398  {};
399 
400  template<class S1, class S2>
401  using Concat = Invoke<concat<S1, S2>>;
402 
403  template<unsigned N>
404  struct gen_seq;
405  template<unsigned N>
406  using GenSeq = Invoke<gen_seq<N>>;
407 
408  template<unsigned N>
409  struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>>
410  {};
411 
412  template<>
413  struct gen_seq<0> : seq<>
414  {};
415  template<>
416  struct gen_seq<1> : seq<0>
417  {};
418 
419  template<typename Seq, typename Tuple>
420  struct pop_back_helper;
421 
422  template<unsigned... N, typename Tuple>
423  struct pop_back_helper<seq<N...>, Tuple>
424  {
425  template<template<typename... Args> class U>
426  using rebind = U<typename std::tuple_element<N, Tuple>::type...>;
427  };
428 
429  template<typename... T>
430  struct pop_back //: public pop_back_helper<typename gen_seq<sizeof...(T)-1>::type, std::tuple<T...>>
431  {
432  template<template<typename... Args> class U>
433  using rebind = typename pop_back_helper<typename gen_seq<sizeof...(T) - 1>::type, std::tuple<T...>>::template rebind<U>;
434  };
435 
436  template<>
437  struct pop_back<>
438  {
439  template<template<typename... Args> class U>
440  using rebind = U<>;
441  };
442 
443  // from http://stackoverflow.com/questions/2118541/check-if-c0x-parameter-pack-contains-a-type
444  template<typename Tp, typename... List>
445  struct contains : std::true_type
446  {};
447 
448  template<typename Tp, typename Head, typename... Rest>
449  struct contains<Tp, Head, Rest...> : std::conditional<std::is_same<Tp, Head>::value, std::true_type, contains<Tp, Rest...>>::type
450  {};
451 
452  template<typename Tp>
453  struct contains<Tp> : std::false_type
454  {};
455 
456  template<typename T>
457  struct empty_context
458  {};
459 
460  template<typename T>
461  struct promote
462  {
463  using type = T;
464  };
465 
466 #define CROW_INTERNAL_PROMOTE_TYPE(t1, t2) \
467  template<> \
468  struct promote<t1> \
469  { \
470  using type = t2; \
471  }
472 
473  CROW_INTERNAL_PROMOTE_TYPE(char, int64_t);
474  CROW_INTERNAL_PROMOTE_TYPE(short, int64_t);
475  CROW_INTERNAL_PROMOTE_TYPE(int, int64_t);
476  CROW_INTERNAL_PROMOTE_TYPE(long, int64_t);
477  CROW_INTERNAL_PROMOTE_TYPE(long long, int64_t);
478  CROW_INTERNAL_PROMOTE_TYPE(unsigned char, uint64_t);
479  CROW_INTERNAL_PROMOTE_TYPE(unsigned short, uint64_t);
480  CROW_INTERNAL_PROMOTE_TYPE(unsigned int, uint64_t);
481  CROW_INTERNAL_PROMOTE_TYPE(unsigned long, uint64_t);
482  CROW_INTERNAL_PROMOTE_TYPE(unsigned long long, uint64_t);
483  CROW_INTERNAL_PROMOTE_TYPE(float, double);
484 #undef CROW_INTERNAL_PROMOTE_TYPE
485 
486  template<typename T>
487  using promote_t = typename promote<T>::type;
488 
489  } // namespace black_magic
490 
491  namespace detail
492  {
493 
494  template<class T, std::size_t N, class... Args>
495  struct get_index_of_element_from_tuple_by_type_impl
496  {
497  static constexpr auto value = N;
498  };
499 
500  template<class T, std::size_t N, class... Args>
501  struct get_index_of_element_from_tuple_by_type_impl<T, N, T, Args...>
502  {
503  static constexpr auto value = N;
504  };
505 
506  template<class T, std::size_t N, class U, class... Args>
507  struct get_index_of_element_from_tuple_by_type_impl<T, N, U, Args...>
508  {
509  static constexpr auto value = get_index_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value;
510  };
511  } // namespace detail
512 
513  namespace utility
514  {
515  template<class T, class... Args>
516  T& get_element_by_type(std::tuple<Args...>& t)
517  {
518  return std::get<detail::get_index_of_element_from_tuple_by_type_impl<T, 0, Args...>::value>(t);
519  }
520 
521  template<typename T>
522  struct function_traits;
523 
524 #ifndef CROW_MSVC_WORKAROUND
525  template<typename T>
526  struct function_traits : public function_traits<decltype(&T::operator())>
527  {
528  using parent_t = function_traits<decltype(&T::operator())>;
529  static const size_t arity = parent_t::arity;
530  using result_type = typename parent_t::result_type;
531  template<size_t i>
532  using arg = typename parent_t::template arg<i>;
533  };
534 #endif
535 
536  template<typename ClassType, typename R, typename... Args>
537  struct function_traits<R (ClassType::*)(Args...) const>
538  {
539  static const size_t arity = sizeof...(Args);
540 
541  typedef R result_type;
542 
543  template<size_t i>
544  using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
545  };
546 
547  template<typename ClassType, typename R, typename... Args>
548  struct function_traits<R (ClassType::*)(Args...)>
549  {
550  static const size_t arity = sizeof...(Args);
551 
552  typedef R result_type;
553 
554  template<size_t i>
555  using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
556  };
557 
558  template<typename R, typename... Args>
559  struct function_traits<std::function<R(Args...)>>
560  {
561  static const size_t arity = sizeof...(Args);
562 
563  typedef R result_type;
564 
565  template<size_t i>
566  using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
567  };
568  /// @endcond
569 
570  inline static std::string base64encode(const unsigned char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
571  {
572  std::string ret;
573  ret.resize((size + 2) / 3 * 4);
574  auto it = ret.begin();
575  while (size >= 3)
576  {
577  *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
578  unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
579  *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xF0) >> 4)];
580  h = (static_cast<unsigned char>(*data++) & 0x0F) << 2;
581  *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xC0) >> 6)];
582  *it++ = key[static_cast<unsigned char>(*data++) & 0x3F];
583 
584  size -= 3;
585  }
586  if (size == 1)
587  {
588  *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
589  unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
590  *it++ = key[h];
591  *it++ = '=';
592  *it++ = '=';
593  }
594  else if (size == 2)
595  {
596  *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
597  unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
598  *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xF0) >> 4)];
599  h = (static_cast<unsigned char>(*data++) & 0x0F) << 2;
600  *it++ = key[h];
601  *it++ = '=';
602  }
603  return ret;
604  }
605 
606  inline static std::string base64encode(std::string data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
607  {
608  return base64encode((const unsigned char*)data.c_str(), size, key);
609  }
610 
611  inline static std::string base64encode_urlsafe(const unsigned char* data, size_t size)
612  {
613  return base64encode(data, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
614  }
615 
616  inline static std::string base64encode_urlsafe(std::string data, size_t size)
617  {
618  return base64encode((const unsigned char*)data.c_str(), size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
619  }
620 
621  inline static std::string base64decode(const char* data, size_t size)
622  {
623  // We accept both regular and url encoding here, as there does not seem to be any downside to that.
624  // If we want to distinguish that we should use +/ for non-url and -_ for url.
625 
626  // Mapping logic from characters to [0-63]
627  auto key = [](char c) -> unsigned char {
628  if ((c >= 'A') && (c <= 'Z')) return c - 'A';
629  if ((c >= 'a') && (c <= 'z')) return c - 'a' + 26;
630  if ((c >= '0') && (c <= '9')) return c - '0' + 52;
631  if ((c == '+') || (c == '-')) return 62;
632  if ((c == '/') || (c == '_')) return 63;
633  return 0;
634  };
635 
636  // Not padded
637  if (size % 4 == 2) // missing last 2 characters
638  size = (size / 4 * 3) + 1; // Not subtracting extra characters because they're truncated in int division
639  else if (size % 4 == 3) // missing last character
640  size = (size / 4 * 3) + 2; // Not subtracting extra characters because they're truncated in int division
641 
642  // Padded
643  else if (data[size - 2] == '=') // padded with '=='
644  size = (size / 4 * 3) - 2; // == padding means the last block only has 1 character instead of 3, hence the '-2'
645  else if (data[size - 1] == '=') // padded with '='
646  size = (size / 4 * 3) - 1; // = padding means the last block only has 2 character instead of 3, hence the '-1'
647 
648  // Padding not needed
649  else
650  size = size / 4 * 3;
651 
652  std::string ret;
653  ret.resize(size);
654  auto it = ret.begin();
655 
656  // These will be used to decode 1 character at a time
657  unsigned char odd; // char1 and char3
658  unsigned char even; // char2 and char4
659 
660  // Take 4 character blocks to turn into 3
661  while (size >= 3)
662  {
663  // dec_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
664  odd = key(*data++);
665  even = key(*data++);
666  *it++ = (odd << 2) | ((even & 0x30) >> 4);
667  // dec_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
668  odd = key(*data++);
669  *it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
670  // dec_char3 = ((char3 AND 00000011) shifted 6 bits left) OR (char4)
671  even = key(*data++);
672  *it++ = ((odd & 0x03) << 6) | (even);
673 
674  size -= 3;
675  }
676  if (size == 2)
677  {
678  // d_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
679  odd = key(*data++);
680  even = key(*data++);
681  *it++ = (odd << 2) | ((even & 0x30) >> 4);
682  // d_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
683  odd = key(*data++);
684  *it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
685  }
686  else if (size == 1)
687  {
688  // d_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
689  odd = key(*data++);
690  even = key(*data++);
691  *it++ = (odd << 2) | ((even & 0x30) >> 4);
692  }
693  return ret;
694  }
695 
696  inline static std::string base64decode(const std::string& data, size_t size)
697  {
698  return base64decode(data.data(), size);
699  }
700 
701  inline static std::string base64decode(const std::string& data)
702  {
703  return base64decode(data.data(), data.length());
704  }
705 
706  inline static std::string normalize_path(const std::string& directoryPath)
707  {
708  std::string normalizedPath = directoryPath;
709  std::replace(normalizedPath.begin(), normalizedPath.end(), '\\', '/');
710  if (!normalizedPath.empty() && normalizedPath.back() != '/')
711  normalizedPath += '/';
712  return normalizedPath;
713  }
714 
715  inline static void sanitize_filename(std::string& data, char replacement = '_')
716  {
717  if (data.length() > 255)
718  data.resize(255);
719 
720  static const auto toUpper = [](char c) {
721  return ((c >= 'a') && (c <= 'z')) ? (c - ('a' - 'A')) : c;
722  };
723  // Check for special device names. The Windows behavior is really odd here, it will consider both AUX and AUX.txt
724  // a special device. Thus we search for the string (case-insensitive), and then check if the string ends or if
725  // is has a dangerous follow up character (.:\/)
726  auto sanitizeSpecialFile = [](std::string& source, unsigned ofs, const char* pattern, bool includeNumber, char replacement) {
727  unsigned i = ofs;
728  size_t len = source.length();
729  const char* p = pattern;
730  while (*p)
731  {
732  if (i >= len) return;
733  if (toUpper(source[i]) != *p) return;
734  ++i;
735  ++p;
736  }
737  if (includeNumber)
738  {
739  if ((i >= len) || (source[i] < '1') || (source[i] > '9')) return;
740  ++i;
741  }
742  if ((i >= len) || (source[i] == '.') || (source[i] == ':') || (source[i] == '/') || (source[i] == '\\'))
743  {
744  source.erase(ofs + 1, (i - ofs) - 1);
745  source[ofs] = replacement;
746  }
747  };
748  bool checkForSpecialEntries = true;
749  for (unsigned i = 0; i < data.length(); ++i)
750  {
751  // Recognize directory traversals and the special devices CON/PRN/AUX/NULL/COM[1-]/LPT[1-9]
752  if (checkForSpecialEntries)
753  {
754  checkForSpecialEntries = false;
755  switch (toUpper(data[i]))
756  {
757  case 'A':
758  sanitizeSpecialFile(data, i, "AUX", false, replacement);
759  break;
760  case 'C':
761  sanitizeSpecialFile(data, i, "CON", false, replacement);
762  sanitizeSpecialFile(data, i, "COM", true, replacement);
763  break;
764  case 'L':
765  sanitizeSpecialFile(data, i, "LPT", true, replacement);
766  break;
767  case 'N':
768  sanitizeSpecialFile(data, i, "NUL", false, replacement);
769  break;
770  case 'P':
771  sanitizeSpecialFile(data, i, "PRN", false, replacement);
772  break;
773  case '.':
774  sanitizeSpecialFile(data, i, "..", false, replacement);
775  break;
776  }
777  }
778 
779  // Sanitize individual characters
780  unsigned char c = data[i];
781  if ((c < ' ') || ((c >= 0x80) && (c <= 0x9F)) || (c == '?') || (c == '<') || (c == '>') || (c == ':') || (c == '*') || (c == '|') || (c == '\"'))
782  {
783  data[i] = replacement;
784  }
785  else if ((c == '/') || (c == '\\'))
786  {
787  if (CROW_UNLIKELY(i == 0)) //Prevent Unix Absolute Paths (Windows Absolute Paths are prevented with `(c == ':')`)
788  {
789  data[i] = replacement;
790  }
791  else
792  {
793  checkForSpecialEntries = true;
794  }
795  }
796  }
797  }
798 
799  inline static std::string random_alphanum(std::size_t size)
800  {
801  static const char alphabet[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
802  std::random_device dev;
803  std::mt19937 rng(dev());
804  std::uniform_int_distribution<std::mt19937::result_type> dist(0, sizeof(alphabet) - 2);
805  std::string out;
806  out.reserve(size);
807  for (std::size_t i = 0; i < size; i++)
808  out.push_back(alphabet[dist(rng)]);
809  return out;
810  }
811 
812  inline static std::string join_path(std::string path, const std::string& fname)
813  {
814 #if defined(CROW_CAN_USE_CPP17) && !defined(CROW_FILESYSTEM_IS_EXPERIMENTAL)
815  return (std::filesystem::path(path) / fname).string();
816 #else
817  if (!(path.back() == '/' || path.back() == '\\'))
818  path += '/';
819  path += fname;
820  return path;
821 #endif
822  }
823 
824  /**
825  * @brief Checks two string for equality.
826  * Always returns false if strings differ in size.
827  * Defaults to case-insensitive comparison.
828  */
829  inline static bool string_equals(const std::string& l, const std::string& r, bool case_sensitive = false)
830  {
831  if (l.length() != r.length())
832  return false;
833 
834  for (size_t i = 0; i < l.length(); i++)
835  {
836  if (case_sensitive)
837  {
838  if (l[i] != r[i])
839  return false;
840  }
841  else
842  {
843  if (std::toupper(l[i]) != std::toupper(r[i]))
844  return false;
845  }
846  }
847 
848  return true;
849  }
850 
851  template<typename T, typename U>
852  inline static T lexical_cast(const U& v)
853  {
854  std::stringstream stream;
855  T res;
856 
857  stream << v;
858  stream >> res;
859 
860  return res;
861  }
862 
863  template<typename T>
864  inline static T lexical_cast(const char* v, size_t count)
865  {
866  std::stringstream stream;
867  T res;
868 
869  stream.write(v, count);
870  stream >> res;
871 
872  return res;
873  }
874 
875 
876  /// Return a copy of the given string with its
877  /// leading and trailing whitespaces removed.
878  inline static std::string trim(const std::string& v)
879  {
880  if (v.empty())
881  return "";
882 
883  size_t begin = 0, end = v.length();
884 
885  size_t i;
886  for (i = 0; i < v.length(); i++)
887  {
888  if (!std::isspace(v[i]))
889  {
890  begin = i;
891  break;
892  }
893  }
894 
895  if (i == v.length())
896  return "";
897 
898  for (i = v.length(); i > 0; i--)
899  {
900  if (!std::isspace(v[i - 1]))
901  {
902  end = i;
903  break;
904  }
905  }
906 
907  return v.substr(begin, end - begin);
908  }
909  } // namespace utility
910 } // namespace crow