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