Crow  1.1
A C++ microframework for the web
 
Loading...
Searching...
No Matches
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
31namespace 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
The main namespace of the library. In this namespace is defined the most important classes and functi...