/
githubmirror
/
nghttp2
Обзор
Документация
Войти
/
githubmirror
/
nghttp2
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/template.h
413 строк
12 KB
Tatsuhiro Tsujikawa
src: MSDMI part2
12 май 2026, 15:11
12 май 2026, 15:11
f1aefd0
Код
Авторство
О чём код?
/* * nghttp2 - HTTP/2 C Library * * Copyright (c) 2015 Tatsuhiro Tsujikawa * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef TEMPLATE_H #define TEMPLATE_H #include "nghttp2_config.h" #include <cstdint> #include <cstring> #include <cstdio> #include <cstdlib> #include <memory> #include <array> #include <functional> #include <typeinfo> #include <algorithm> #include <ostream> #include <utility> #include <span> #include <string_view> #include <compare> #include <type_traits> #include <print> namespace nghttp2 { template <std::integral T> [[nodiscard]] constexpr auto as_unsigned(T n) noexcept { return static_cast<std::make_unsigned_t<T>>(n); } template <std::unsigned_integral T> [[nodiscard]] constexpr auto as_signed(T n) noexcept { return static_cast<std::make_signed_t<T>>(n); } template <typename T, size_t N> constexpr size_t array_size(T (&)[N]) { return N; } template <typename T, size_t N> constexpr size_t str_size(T (&)[N]) { return N - 1; } template <std::invocable F> struct Defer { using Invocable = std::decay_t<F>; template <typename G> [[nodiscard]] explicit Defer(G &&g) noexcept( std::is_nothrow_constructible_v<Invocable, G &&>) : f{std::forward<G>(g)} {} ~Defer() { f(); } Defer(Defer &&o) = delete; Defer(const Defer &) = delete; Defer &operator=(const Defer &) = delete; Defer &operator=(Defer &&) = delete; Invocable f; }; template <std::invocable F> [[nodiscard]] Defer<std::decay_t<F>> defer(F &&f) { return Defer<std::decay_t<F>>(std::forward<F>(f)); } template <typename T, typename F> bool test_flags(T t, F flags) { return (t & flags) == flags; } // doubly linked list of element T*. T must have field T *dlprev and // T *dlnext, which point to previous element and next element in the // list respectively. template <typename T> struct DList { DList() noexcept = default; DList(const DList &) = delete; DList &operator=(const DList &) = delete; DList(DList &&other) noexcept : head{std::exchange(other.head, nullptr)}, tail{std::exchange(other.tail, nullptr)}, len{std::exchange(other.len, 0)} {} DList &operator=(DList &&other) noexcept { if (this == &other) { return *this; } head = std::exchange(other.head, nullptr); tail = std::exchange(other.tail, nullptr); len = std::exchange(other.len, 0); return *this; } void append(T *t) { ++len; if (tail) { tail->dlnext = t; t->dlprev = tail; tail = t; return; } head = tail = t; } void remove(T *t) { --len; auto p = t->dlprev; auto n = t->dlnext; if (p) { p->dlnext = n; } else { head = n; } if (n) { n->dlprev = p; } else { tail = p; } t->dlprev = t->dlnext = nullptr; } bool empty() const { return head == nullptr; } size_t size() const { return len; } T *head{}, *tail{}; size_t len{}; }; template <typename T> void dlist_delete_all(DList<T> &dl) { for (auto e = dl.head; e;) { auto next = e->dlnext; delete e; e = next; } } // User-defined literals for K, M, and G (powers of 1024) [[nodiscard]] constexpr unsigned long long operator""_k(unsigned long long k) noexcept { return k * 1024; } [[nodiscard]] constexpr unsigned long long operator""_m(unsigned long long m) noexcept { return m * 1024 * 1024; } [[nodiscard]] constexpr unsigned long long operator""_g(unsigned long long g) noexcept { return g * 1024 * 1024 * 1024; } // User-defined literals for time, converted into double in seconds // hours [[nodiscard]] constexpr double operator""_h(unsigned long long h) noexcept { return static_cast<double>(h * 60 * 60); } // minutes [[nodiscard]] constexpr double operator""_min(unsigned long long min) noexcept { return static_cast<double>(min * 60); } // seconds [[nodiscard]] constexpr double operator""_s(unsigned long long s) noexcept { return static_cast<double>(s); } // milliseconds [[nodiscard]] constexpr double operator""_ms(unsigned long long ms) noexcept { return static_cast<double>(ms) / 1000.; } // ImmutableString represents string that is immutable unlike // std::string. It has c_str() and size() functions to mimic // std::string. It manages buffer by itself. Just like std::string, // c_str() returns NULL-terminated string, but NULL character may // appear before the final terminal NULL. class ImmutableString { public: using traits_type = std::char_traits<char>; using value_type = traits_type::char_type; using allocator_type = std::allocator<char>; using size_type = std::allocator_traits<allocator_type>::size_type; using difference_type = std::allocator_traits<allocator_type>::difference_type; using const_reference = const value_type &; using const_pointer = const value_type *; using const_iterator = const_pointer; using const_reverse_iterator = std::reverse_iterator<const_iterator>; constexpr ImmutableString() noexcept = default; constexpr ImmutableString(const char *s, size_t slen) : len(slen), base(copystr(s, s + len)) {} constexpr explicit ImmutableString(const char *s) : len(traits_type::length(s)), base(copystr(s, s + len)) {} ImmutableString(std::nullptr_t) = delete; template <std::ranges::sized_range R> requires(std::is_same_v<std::ranges::range_value_t<R>, value_type> && !std::is_same_v<std::remove_cvref_t<R>, ImmutableString> && !std::is_array_v<std::remove_cvref_t<R>>) constexpr explicit ImmutableString(R &&r) : len(std::ranges::size(r)), base(copystr(std::forward<R>(r))) {} template <std::forward_iterator I> requires(std::is_same_v<std::iter_value_t<I>, value_type>) constexpr ImmutableString(I first, I last) : len(as_unsigned(std::ranges::distance(first, last))), base(copystr(std::move(first), std::move(last))) {} constexpr ImmutableString(const ImmutableString &other) : len(other.len), base(copystr(other)) {} constexpr ImmutableString(ImmutableString &&other) noexcept : len{std::exchange(other.len, 0)}, base{std::exchange(other.base, "")} {} constexpr ~ImmutableString() { if (len) { delete[] base; } } constexpr ImmutableString &operator=(const ImmutableString &other) { if (this == &other) { return *this; } if (len) { delete[] base; } len = other.len; base = copystr(other); return *this; } constexpr ImmutableString &operator=(ImmutableString &&other) noexcept { if (this == &other) { return *this; } if (len) { delete[] base; } len = std::exchange(other.len, 0); base = std::exchange(other.base, ""); return *this; } constexpr const_iterator begin() const noexcept { return base; } constexpr const_iterator cbegin() const noexcept { return base; } constexpr const_iterator end() const noexcept { return base + len; } constexpr const_iterator cend() const noexcept { return base + len; } constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{base + len}; } constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{base + len}; } constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator{base}; } constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator{base}; } constexpr const_pointer c_str() const noexcept { return base; } constexpr const_pointer data() const noexcept { return base; } constexpr size_type size() const noexcept { return len; } constexpr bool empty() const noexcept { return len == 0; } constexpr const_reference operator[](size_type pos) const noexcept { return *(base + pos); } private: template <std::forward_iterator I> constexpr const char *copystr(I first, I last) { auto len = static_cast<size_t>(std::ranges::distance(first, last)); if (len == 0) { return ""; } auto res = new char[len + 1]; *std::ranges::copy(first, last, res).out = '\0'; return res; } template <std::ranges::forward_range R> requires(!std::is_array_v<std::remove_cvref_t<R>>) constexpr const char *copystr(R &&r) { return copystr(std::ranges::begin(r), std::ranges::end(r)); } size_type len{}; const char *base{""}; }; inline bool operator==(const ImmutableString &lhs, const ImmutableString &rhs) { return std::ranges::equal(lhs, rhs); } inline std::ostream &operator<<(std::ostream &o, const ImmutableString &s) { return o.write(s.c_str(), static_cast<std::streamsize>(s.size())); } inline std::string &operator+=(std::string &lhs, const ImmutableString &rhs) { lhs.append(rhs.c_str(), rhs.size()); return lhs; } inline bool operator==(const ImmutableString &lhs, std::string_view rhs) { return std::ranges::equal(lhs, rhs); } inline std::strong_ordering operator<=>(const ImmutableString &lhs, const ImmutableString &rhs) noexcept { return std::string_view{lhs.data(), lhs.size()} <=> std::string_view{rhs.data(), rhs.size()}; } constexpr ImmutableString operator""_is(const char *str, size_t len) { return {str, len}; } template <typename T, std::size_t N> [[nodiscard]] auto as_uint8_span(std::span<T, N> s) noexcept { return std::span<const uint8_t, N == std::dynamic_extent ? std::dynamic_extent : N * sizeof(T)>{ reinterpret_cast<const uint8_t *>(s.data()), s.size_bytes()}; } template <typename T, std::size_t N> [[nodiscard]] auto as_writable_uint8_span(std::span<T, N> s) noexcept { return std::span<uint8_t, N == std::dynamic_extent ? std::dynamic_extent : N * sizeof(T)>{ reinterpret_cast<uint8_t *>(s.data()), s.size_bytes()}; } template <typename R> requires(std::ranges::contiguous_range<R> && std::ranges::sized_range<R> && std::ranges::borrowed_range<R> && !std::is_array_v<std::remove_cvref_t<R>> && sizeof(std::ranges::range_value_t<R>) == sizeof(std::string_view::value_type)) [[nodiscard]] std::string_view as_string_view(R &&r) { return std::string_view{ reinterpret_cast<std::string_view::const_pointer>(std::ranges::data(r)), std::ranges::size(r)}; } // Returns std::string_view over a given range [|first|, |last|). template <std::contiguous_iterator I> requires(sizeof(std::iter_value_t<I>) == sizeof(std::string_view::value_type)) [[nodiscard]] std::string_view as_string_view(I first, I last) { return std::string_view{ reinterpret_cast<std::string_view::const_pointer>(std::to_address(first)), static_cast<size_t>(std::ranges::distance(first, last))}; } // Returns std::string_view over a given range [|first|, |first| + |n|). template <std::contiguous_iterator I> requires(sizeof(std::iter_value_t<I>) == sizeof(std::string_view::value_type)) [[nodiscard]] std::string_view as_string_view(I first, size_t n) { return std::string_view{ reinterpret_cast<std::string_view::const_pointer>(std::to_address(first)), n}; } inline int run_app(std::function<int(int, char **)> app, int argc, char **argv) { try { return app(argc, argv); } catch (const std::bad_alloc &) { fputs("Out of memory\n", stderr); } catch (const std::exception &x) { std::println(stderr, "Caught {}:\n{}", typeid(x).name(), x.what()); } catch (...) { fputs("Unknown exception caught\n", stderr); } return EXIT_FAILURE; } } // namespace nghttp2 #endif // !defined(TEMPLATE_H)