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