Coverage Report

Created: 2026-08-14 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/crow/include/crow/utility.h
Line
Count
Source
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
908k
#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
30
namespace 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
0
            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
0
        {
44
0
            return i >= len ? throw OutOfRange(i, len) : i;
45
0
        }
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
0
            {
62
0
                return requires_in_range(i, size_), begin_[i];
63
0
            }
64
65
            constexpr operator const char*() const
66
0
            {
67
0
                return begin_;
68
0
            }
69
70
0
            constexpr const char* begin() const { return begin_; }
71
0
            constexpr const char* end() const { return begin_ + size_; }
72
73
            constexpr unsigned size() const
74
0
            {
75
0
                return size_;
76
0
            }
77
        };
78
79
        constexpr unsigned find_closing_tag(const_str s, unsigned p)
80
0
        {
81
0
            return s[p] == '>' ? p : find_closing_tag(s, p + 1);
82
0
        }
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
0
        {
87
0
            return i == s.size()   ? f == 0 :
88
0
                   f < 0 || f >= 2 ? false :
89
0
                   s[i] == '<'     ? is_valid(s, i + 1, f + 1) :
90
0
                   s[i] == '>'     ? is_valid(s, i + 1, f - 1) :
91
0
                                     is_valid(s, i + 1, f);
92
0
        }
93
94
        constexpr bool is_equ_p(const char* a, const char* b, unsigned n)
95
0
        {
96
0
            return *a == 0 && *b == 0 && n == 0 ? true :
97
0
                   (*a == 0 || *b == 0)         ? false :
98
0
                   n == 0                       ? true :
99
0
                   *a != *b                     ? false :
100
0
                                                  is_equ_p(a + 1, b + 1, n - 1);
101
0
        }
102
103
        constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n)
104
0
        {
105
0
            return ai + n > a.size() || bi + n > b.size() ? false :
106
0
                   n == 0                                 ? true :
107
0
                   a[ai] != b[bi]                         ? false :
108
0
                                                            is_equ_n(a, ai + 1, b, bi + 1, n - 1);
109
0
        }
110
111
        constexpr bool is_int(const_str s, unsigned i)
112
0
        {
113
0
            return is_equ_n(s, i, "<int>", 0, 5);
114
0
        }
115
116
        constexpr bool is_uint(const_str s, unsigned i)
117
0
        {
118
0
            return is_equ_n(s, i, "<uint>", 0, 6);
119
0
        }
120
121
        constexpr bool is_float(const_str s, unsigned i)
122
0
        {
123
0
            return is_equ_n(s, i, "<float>", 0, 7) ||
124
0
                   is_equ_n(s, i, "<double>", 0, 8);
125
0
        }
126
127
        constexpr bool is_str(const_str s, unsigned i)
128
0
        {
129
0
            return is_equ_n(s, i, "<str>", 0, 5) ||
130
0
                   is_equ_n(s, i, "<string>", 0, 8);
131
0
        }
132
133
        constexpr bool is_path(const_str s, unsigned i)
134
0
        {
135
0
            return is_equ_n(s, i, "<path>", 0, 6);
136
0
        }
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
0
        {
182
0
            if (a == 0)
183
0
                return b == 0;
184
0
            if (b == 0)
185
0
                return a == 0;
186
0
            int sa = a % 6;
187
0
            int sb = a % 6;
188
0
            if (sa == 5) sa = 4;
189
0
            if (sb == 5) sb = 4;
190
0
            if (sa != sb)
191
0
                return false;
192
0
            return is_parameter_tag_compatible(a / 6, b / 6);
193
0
        }
194
195
        static inline unsigned find_closing_tag_runtime(const char* s, unsigned p)
196
0
        {
197
0
            return s[p] == 0   ? throw std::runtime_error("unmatched tag <") :
198
0
                   s[p] == '>' ? p :
199
0
                                 find_closing_tag_runtime(s, p + 1);
200
0
        }
201
202
        static inline uint64_t get_parameter_tag_runtime(const char* s, unsigned p = 0)
203
0
        {
204
0
            return s[p] == 0   ? 0 :
205
0
                   s[p] == '<' ? (
206
0
                                   std::strncmp(s + p, "<int>", 5) == 0  ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 :
207
0
                                   std::strncmp(s + p, "<uint>", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 :
208
0
                                   (std::strncmp(s + p, "<float>", 7) == 0 ||
209
0
                                    std::strncmp(s + p, "<double>", 8) == 0) ?
210
0
                                                                           get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 :
211
0
                                   (std::strncmp(s + p, "<str>", 5) == 0 ||
212
0
                                    std::strncmp(s + p, "<string>", 8) == 0) ?
213
0
                                                                           get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 :
214
0
                                   std::strncmp(s + p, "<path>", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 5 :
215
0
                                                                           throw std::runtime_error("invalid parameter type")) :
216
0
                                 get_parameter_tag_runtime(s, p + 1);
217
0
        }
218
#ifndef CROW_MSVC_WORKAROUND
219
        constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0)
220
0
        {
221
0
            return p == s.size() ? 0 :
222
0
                   s[p] == '<'   ? (
223
0
                                   is_int(s, p)   ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 :
224
0
                                     is_uint(s, p)  ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 :
225
0
                                     is_float(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 :
226
0
                                     is_str(s, p)   ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 :
227
0
                                     is_path(s, p)  ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 :
228
0
                                                      throw std::runtime_error("invalid parameter type")) :
229
0
                                 get_parameter_tag(s, p + 1);
230
0
        }
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
0
        {
562
0
            std::string ret;
563
0
            ret.resize((size + 2) / 3 * 4);
564
0
            auto it = ret.begin();
565
0
            while (size >= 3)
566
0
            {
567
0
                *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
568
0
                unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
569
0
                *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xF0) >> 4)];
570
0
                h = (static_cast<unsigned char>(*data++) & 0x0F) << 2;
571
0
                *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xC0) >> 6)];
572
0
                *it++ = key[static_cast<unsigned char>(*data++) & 0x3F];
573
0
574
0
                size -= 3;
575
0
            }
576
0
            if (size == 1)
577
0
            {
578
0
                *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
579
0
                unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
580
0
                *it++ = key[h];
581
0
                *it++ = '=';
582
0
                *it++ = '=';
583
0
            }
584
0
            else if (size == 2)
585
0
            {
586
0
                *it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
587
0
                unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
588
0
                *it++ = key[h | ((static_cast<unsigned char>(*data) & 0xF0) >> 4)];
589
0
                h = (static_cast<unsigned char>(*data++) & 0x0F) << 2;
590
0
                *it++ = key[h];
591
0
                *it++ = '=';
592
0
            }
593
0
            return ret;
594
0
        }
595
596
        inline static std::string base64encode(std::string data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
597
0
        {
598
0
            return base64encode((const unsigned char*)data.c_str(), size, key);
599
0
        }
600
601
        inline static std::string base64encode_urlsafe(const unsigned char* data, size_t size)
602
0
        {
603
0
            return base64encode(data, size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
604
0
        }
605
606
        inline static std::string base64encode_urlsafe(std::string data, size_t size)
607
0
        {
608
0
            return base64encode((const unsigned char*)data.c_str(), size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
609
0
        }
610
611
        inline static std::string base64decode(const char* data, size_t size)
612
0
        {
613
0
            // We accept both regular and url encoding here, as there does not seem to be any downside to that.
614
0
            // If we want to distinguish that we should use +/ for non-url and -_ for url.
615
0
616
0
            // Mapping logic from characters to [0-63]
617
0
            auto key = [](char c) -> unsigned char {
618
0
                if ((c >= 'A') && (c <= 'Z')) return c - 'A';
619
0
                if ((c >= 'a') && (c <= 'z')) return c - 'a' + 26;
620
0
                if ((c >= '0') && (c <= '9')) return c - '0' + 52;
621
0
                if ((c == '+') || (c == '-')) return 62;
622
0
                if ((c == '/') || (c == '_')) return 63;
623
0
                return 0;
624
0
            };
625
0
626
0
            // Not padded
627
0
            if (size % 4 == 2)             // missing last 2 characters
628
0
                size = (size / 4 * 3) + 1; // Not subtracting extra characters because they're truncated in int division
629
0
            else if (size % 4 == 3)        // missing last character
630
0
                size = (size / 4 * 3) + 2; // Not subtracting extra characters because they're truncated in int division
631
0
632
0
            // Padded
633
0
            else if (size >= 2 && data[size - 2] == '=') // padded with '=='
634
0
                size = (size / 4 * 3) - 2;               // == padding means the last block only has 1 character instead of 3, hence the '-2'
635
0
            else if (size >= 1 && data[size - 1] == '=') // padded with '='
636
0
                size = (size / 4 * 3) - 1;               // = padding means the last block only has 2 character instead of 3, hence the '-1'
637
0
638
0
            // Padding not needed
639
0
            else
640
0
                size = size / 4 * 3;
641
0
642
0
            std::string ret;
643
0
            ret.resize(size);
644
0
            auto it = ret.begin();
645
0
646
0
            // These will be used to decode 1 character at a time
647
0
            unsigned char odd;  // char1 and char3
648
0
            unsigned char even; // char2 and char4
649
0
650
0
            // Take 4 character blocks to turn into 3
651
0
            while (size >= 3)
652
0
            {
653
0
                // dec_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
654
0
                odd = key(*data++);
655
0
                even = key(*data++);
656
0
                *it++ = (odd << 2) | ((even & 0x30) >> 4);
657
0
                // dec_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
658
0
                odd = key(*data++);
659
0
                *it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
660
0
                // dec_char3 = ((char3 AND 00000011) shifted 6 bits left) OR (char4)
661
0
                even = key(*data++);
662
0
                *it++ = ((odd & 0x03) << 6) | (even);
663
0
664
0
                size -= 3;
665
0
            }
666
0
            if (size == 2)
667
0
            {
668
0
                // d_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
669
0
                odd = key(*data++);
670
0
                even = key(*data++);
671
0
                *it++ = (odd << 2) | ((even & 0x30) >> 4);
672
0
                // d_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
673
0
                odd = key(*data++);
674
0
                *it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
675
0
            }
676
0
            else if (size == 1)
677
0
            {
678
0
                // d_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
679
0
                odd = key(*data++);
680
0
                even = key(*data++);
681
0
                *it++ = (odd << 2) | ((even & 0x30) >> 4);
682
0
            }
683
0
            return ret;
684
0
        }
685
686
        inline static std::string base64decode(const std::string& data, size_t size)
687
0
        {
688
0
            return base64decode(data.data(), size);
689
0
        }
690
691
        inline static std::string base64decode(const std::string& data)
692
0
        {
693
0
            return base64decode(data.data(), data.length());
694
0
        }
695
696
        inline static std::string normalize_path(const std::string& directoryPath)
697
0
        {
698
0
            std::string normalizedPath = directoryPath;
699
0
            std::replace(normalizedPath.begin(), normalizedPath.end(), '\\', '/');
700
0
            if (!normalizedPath.empty() && normalizedPath.back() != '/')
701
0
                normalizedPath += '/';
702
0
            return normalizedPath;
703
0
        }
704
705
        inline static void sanitize_filename(std::string& data, char replacement = '_')
706
2.99M
        {
707
2.99M
            if (data.length() > 255)
708
56.1k
                data.resize(255);
709
710
4.56M
            static const auto toUpper = [](char c) {
711
4.56M
                return ((c >= 'a') && (c <= 'z')) ? (c - ('a' - 'A')) : c;
712
4.56M
            };
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
2.99M
            auto sanitizeSpecialFile = [](std::string& source, unsigned ofs, const char* pattern, bool includeNumber, char replacement_) {
717
1.22M
                unsigned i = ofs;
718
1.22M
                size_t len = source.length();
719
1.22M
                const char* p = pattern;
720
3.82M
                while (*p)
721
3.38M
                {
722
3.38M
                    if (i >= len) return;
723
3.28M
                    if (toUpper(source[i]) != *p) return;
724
2.59M
                    ++i;
725
2.59M
                    ++p;
726
2.59M
                }
727
434k
                if (includeNumber)
728
241k
                {
729
241k
                    if ((i >= len) || (source[i] < '1') || (source[i] > '9')) return;
730
151k
                    ++i;
731
151k
                }
732
344k
                if ((i >= len) || (source[i] == '.') || (source[i] == ':') || (source[i] == '/') || (source[i] == '\\'))
733
195k
                {
734
195k
                    source.erase(ofs + 1, (i - ofs) - 1);
735
195k
                    source[ofs] = replacement_;
736
195k
                }
737
344k
            };
738
2.99M
            bool checkForSpecialEntries = true;
739
19.2M
            for (unsigned i = 0; i < data.length(); ++i)
740
16.2M
            {
741
                // Recognize directory traversals and the special devices CON/PRN/AUX/NULL/COM[1-]/LPT[1-9]
742
16.2M
                if (checkForSpecialEntries)
743
1.28M
                {
744
1.28M
                    checkForSpecialEntries = false;
745
1.28M
                    switch (toUpper(data[i]))
746
1.28M
                    {
747
115k
                        case 'A':
748
115k
                            sanitizeSpecialFile(data, i, "AUX", false, replacement);
749
115k
                            break;
750
423k
                        case 'C':
751
423k
                            sanitizeSpecialFile(data, i, "CON", false, replacement);
752
423k
                            sanitizeSpecialFile(data, i, "COM", true, replacement);
753
423k
                            break;
754
24.0k
                        case 'L':
755
24.0k
                            sanitizeSpecialFile(data, i, "LPT", true, replacement);
756
24.0k
                            break;
757
100k
                        case 'N':
758
100k
                            sanitizeSpecialFile(data, i, "NUL", false, replacement);
759
100k
                            break;
760
92.2k
                        case 'P':
761
92.2k
                            sanitizeSpecialFile(data, i, "PRN", false, replacement);
762
92.2k
                            break;
763
49.3k
                        case '.':
764
49.3k
                            sanitizeSpecialFile(data, i, "..", false, replacement);
765
49.3k
                            break;
766
1.28M
                    }
767
1.28M
                }
768
769
                // Sanitize individual characters
770
16.2M
                unsigned char c = data[i];
771
16.2M
                if ((c < ' ') || ((c >= 0x80) && (c <= 0x9F)) || (c == '?') || (c == '<') || (c == '>') || (c == ':') || (c == '*') || (c == '|') || (c == '\"'))
772
3.50M
                {
773
3.50M
                    data[i] = replacement;
774
3.50M
                }
775
12.7M
                else if ((c == '/') || (c == '\\'))
776
908k
                {
777
908k
                    if (CROW_UNLIKELY(i == 0)) //Prevent Unix Absolute Paths (Windows Absolute Paths are prevented with `(c == ':')`)
778
8.66k
                    {
779
8.66k
                        data[i] = replacement;
780
8.66k
                    }
781
899k
                    else
782
899k
                    {
783
899k
                        checkForSpecialEntries = true;
784
899k
                    }
785
908k
                }
786
16.2M
            }
787
2.99M
        }
788
789
        inline static std::string random_alphanum(std::size_t size)
790
0
        {
791
0
            static const char alphabet[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
792
0
            std::random_device dev;
793
0
            std::mt19937 rng(dev());
794
0
            std::uniform_int_distribution<std::mt19937::result_type> dist(0, sizeof(alphabet) - 2);
795
0
            std::string out;
796
0
            out.reserve(size);
797
0
            for (std::size_t i = 0; i < size; i++)
798
0
                out.push_back(alphabet[dist(rng)]);
799
0
            return out;
800
0
        }
801
802
        inline static std::string join_path(std::string path, const std::string& fname)
803
2.99M
        {
804
2.99M
            return (std::filesystem::path(path) / fname).string();
805
2.99M
        }
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
0
        {
814
0
            if (l.length() != r.length())
815
0
                return false;
816
0
817
0
            for (size_t i = 0; i < l.length(); i++)
818
0
            {
819
0
                if (case_sensitive)
820
0
                {
821
0
                    if (l[i] != r[i])
822
0
                        return false;
823
0
                }
824
0
                else
825
0
                {
826
0
                    if (std::toupper(l[i]) != std::toupper(r[i]))
827
0
                        return false;
828
0
                }
829
0
            }
830
0
831
0
            return true;
832
0
        }
833
834
        template<typename T, typename U>
835
        inline static T lexical_cast(const U& v)
836
51
        {
837
51
            std::stringstream stream;
838
51
            T res;
839
840
51
            stream << v;
841
51
            stream >> res;
842
843
51
            return res;
844
51
        }
845
846
        template<typename T>
847
        inline static T lexical_cast(const char* v, size_t count)
848
0
        {
849
0
            std::stringstream stream;
850
0
            T res;
851
0
852
0
            stream.write(v, count);
853
0
            stream >> res;
854
0
855
0
            return res;
856
0
        }
Unexecuted instantiation: template_fuzzer.cpp:long crow::utility::lexical_cast<long>(char const*, unsigned long)
Unexecuted instantiation: template_fuzzer.cpp:unsigned long crow::utility::lexical_cast<unsigned long>(char const*, unsigned long)
Unexecuted instantiation: template_fuzzer.cpp:double crow::utility::lexical_cast<double>(char const*, unsigned long)
857
858
        /// Return string view of the given string view with its
859
        /// leading and trailing whitespaces removed.
860
0
        inline static std::string_view trim(const std::string_view sv) {
861
0
            const size_t first = sv.find_first_not_of(" \t\n\r\f\v"); // same as isspace
862
0
            if (std::string_view::npos == first) {
863
0
                return sv.substr(0, 0);
864
0
            }
865
0
            const size_t last = sv.find_last_not_of(" \t\n\r\f\v");
866
0
            return sv.substr(first, (last - first + 1));
867
0
        }
868
869
870
        /**
871
         * @brief splits a string based on a separator
872
         */
873
        inline static std::vector<std::string> split(const std::string& v, const std::string& separator)
874
0
        {
875
0
            std::vector<std::string> result;
876
0
            size_t startPos = 0;
877
0
878
0
            for (size_t foundPos = v.find(separator); foundPos != std::string::npos; foundPos = v.find(separator, startPos))
879
0
            {
880
0
                result.push_back(v.substr(startPos, foundPos - startPos));
881
0
                startPos = foundPos + separator.size();
882
0
            }
883
0
884
0
            result.push_back(v.substr(startPos));
885
0
            return result;
886
0
        }
887
888
        /**
889
         * @brief Returns the first occurence that matches between two ranges of iterators
890
         * @param first1 begin() iterator of the first range
891
         * @param last1 end() iterator of the first range
892
         * @param first2 begin() iterator of the second range
893
         * @param last2 end() iterator of the second range
894
         * @return first occurence that matches between two ranges of iterators 
895
        */
896
        template<typename Iter1, typename Iter2>
897
        inline static Iter1 find_first_of(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2)
898
        {
899
            for (; first1 != last1; ++first1)
900
            {
901
                if (std::find(first2, last2, *first1) != last2)
902
                {
903
                    return first1;
904
                }
905
            }
906
            return last1;
907
        }
908
    } // namespace utility
909
} // namespace crow