#ifndef UTIL_H
#define UTIL_H
#include "nghttp2_config.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif #include <getopt.h>
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif #ifdef __QNX__
# include <sys/time.h>
#endif
#include <cmath>
#include <cstring>
#include <cassert>
#include <vector>
#include <string>
#include <algorithm>
#include <sstream>
#include <memory>
#include <chrono>
#include <unordered_map>
#include <random>
#include <optional>
#include <ranges>
#include <bit>
#ifdef HAVE_LIBEV
# include <ev.h>
#endif
#include "urlparse.h"
#include "template.h"
#include "network.h"
#include "allocator.h"
using namespace std::literals;
namespace nghttp2 {
inline constexpr auto NGHTTP2_H2_ALPN = "\x2h2"sv;
inline constexpr auto NGHTTP2_H2 = "h2"sv;
inline constexpr auto NGHTTP2_H1_1_ALPN = "\x8http/1.1"sv;
inline constexpr auto NGHTTP2_H1_1 = "http/1.1"sv;
namespace util {
template <std::predicate<size_t> Pred>
consteval auto pred_tbl_gen256(Pred pred) {
std::array<bool, 256> tbl;
for (size_t i = 0; i < tbl.size(); ++i) {
tbl[i] = pred(i);
}
return tbl;
}
consteval auto alpha_pred(size_t i) noexcept {
return ('A' <= i && i <= 'Z') || ('a' <= i && i <= 'z');
}
inline constexpr auto is_alpha_tbl = pred_tbl_gen256(alpha_pred);
constexpr bool is_alpha(char c) noexcept {
return is_alpha_tbl[static_cast<uint8_t>(c)];
}
consteval auto digit_pred(size_t i) noexcept { return '0' <= i && i <= '9'; }
inline constexpr auto is_digit_tbl = pred_tbl_gen256(digit_pred);
constexpr bool is_digit(char c) noexcept {
return is_digit_tbl[static_cast<uint8_t>(c)];
}
consteval auto hex_digit_pred(size_t i) noexcept {
return digit_pred(i) || ('A' <= i && i <= 'F') || ('a' <= i && i <= 'f');
}
inline constexpr auto is_hex_digit_tbl = pred_tbl_gen256(hex_digit_pred);
constexpr bool is_hex_digit(char c) noexcept {
return is_hex_digit_tbl[static_cast<uint8_t>(c)];
}
template <std::input_iterator I> constexpr bool is_hex_string(I first, I last) {
return !(std::ranges::distance(first, last) & 1) &&
std::ranges::all_of(first, last, is_hex_digit);
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
constexpr bool is_hex_string(R &&r) {
return is_hex_string(std::ranges::begin(r), std::ranges::end(r));
}
consteval auto rfc3986_unreserved_chars_pred(size_t i) noexcept {
switch (i) {
case '-':
case '.':
case '_':
case '~':
return true;
}
return digit_pred(i) || alpha_pred(i);
}
inline constexpr auto in_rfc3986_unreserved_chars_tbl =
pred_tbl_gen256(rfc3986_unreserved_chars_pred);
constexpr bool in_rfc3986_unreserved_chars(char c) noexcept {
return in_rfc3986_unreserved_chars_tbl[static_cast<uint8_t>(c)];
}
consteval auto rfc3986_sub_delims_pred(size_t i) noexcept {
switch (i) {
case '!':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
return true;
}
return false;
}
inline constexpr auto in_rfc3986_sub_delims_tbl =
pred_tbl_gen256(rfc3986_sub_delims_pred);
constexpr bool in_rfc3986_sub_delims(char c) noexcept {
return in_rfc3986_sub_delims_tbl[static_cast<uint8_t>(c)];
}
consteval auto token_pred(size_t i) noexcept {
switch (i) {
case '!':
case '#':
case '$':
case '%':
case '&':
case '\'':
case '*':
case '+':
case '-':
case '.':
case '^':
case '_':
case '`':
case '|':
case '~':
return true;
}
return digit_pred(i) || alpha_pred(i);
}
inline constexpr auto in_token_tbl = pred_tbl_gen256(token_pred);
constexpr bool in_token(char c) noexcept {
return in_token_tbl[static_cast<uint8_t>(c)];
}
consteval auto attr_char_pred(size_t i) noexcept {
switch (i) {
case '*':
case '\'':
case '%':
return false;
}
return token_pred(i);
}
inline constexpr auto in_attr_char_tbl = pred_tbl_gen256(attr_char_pred);
constexpr bool in_attr_char(char c) noexcept {
return in_attr_char_tbl[static_cast<uint8_t>(c)];
}
inline constexpr auto hex_to_uint_tbl = []() {
std::array<uint32_t, 256> tbl;
std::ranges::fill(tbl, 256);
for (char i = '0'; i <= '9'; ++i) {
tbl[static_cast<uint8_t>(i)] = static_cast<uint32_t>(i - '0');
}
for (char i = 'A'; i <= 'F'; ++i) {
tbl[static_cast<uint8_t>(i)] = static_cast<uint32_t>(i - 'A' + 10);
}
for (char i = 'a'; i <= 'f'; ++i) {
tbl[static_cast<uint8_t>(i)] = static_cast<uint32_t>(i - 'a' + 10);
}
return tbl;
}();
constexpr uint32_t hex_to_uint(char c) noexcept {
return hex_to_uint_tbl[static_cast<uint8_t>(c)];
}
template <std::input_iterator I, std::weakly_incrementable O>
requires(std::indirectly_copyable<I, O>)
constexpr O percent_decode(I first, I last, O result) {
using result_type = std::iter_value_t<O>;
for (; first != last; ++first) {
if (*first != '%') {
*result++ = static_cast<result_type>(*first);
continue;
}
auto dig1 = std::ranges::next(first, 1);
if (dig1 == last || !is_hex_digit(*dig1)) {
*result++ = static_cast<result_type>(*first);
continue;
}
auto dig2 = std::ranges::next(dig1, 1);
if (dig2 == last || !is_hex_digit(*dig2)) {
*result++ = static_cast<result_type>(*first);
continue;
}
*result++ =
static_cast<result_type>((hex_to_uint(*dig1) << 4) | hex_to_uint(*dig2));
first = dig2;
}
return result;
}
template <std::input_iterator I>
constexpr std::string percent_decode(I first, I last) {
std::string result;
result.resize(as_unsigned(std::ranges::distance(first, last)));
auto p = percent_decode(std::move(first), std::move(last),
std::ranges::begin(result));
result.resize(
as_unsigned(std::ranges::distance(std::ranges::begin(result), p)));
return result;
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
constexpr std::string percent_decode(R &&r) {
return percent_decode(std::ranges::begin(r), std::ranges::end(r));
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
std::string_view percent_decode(BlockAllocator &balloc, R &&r) {
auto iov = make_byte_ref(balloc, std::ranges::size(r) + 1);
auto p = percent_decode(std::ranges::begin(r), std::ranges::end(r),
std::ranges::begin(iov));
*p = '\0';
return as_string_view(std::ranges::begin(iov), p);
}
template <std::input_iterator I, std::weakly_incrementable O>
requires(std::indirectly_copyable<I, O>)
constexpr O quote_string(I first, I last, O result) noexcept {
for (; first != last; ++first) {
if (*first == '"') {
*result++ = '\\';
*result++ = '"';
} else {
*result++ = static_cast<std::iter_value_t<O>>(*first);
}
}
return result;
}
template <std::ranges::input_range R, std::weakly_incrementable O>
requires(std::indirectly_copyable<std::ranges::iterator_t<R>, O> &&
!std::is_array_v<std::remove_cvref_t<R>>)
constexpr O quote_string(R &&r, O result) {
return quote_string(std::ranges::begin(r), std::ranges::end(r),
std::move(result));
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
std::string_view quote_string(BlockAllocator &balloc, R &&r) {
auto cnt = std::ranges::count(r, '"');
if (cnt == 0) {
return make_string_ref(balloc, std::forward<R>(r));
}
auto iov =
make_byte_ref(balloc, std::ranges::size(r) + static_cast<size_t>(cnt) + 1);
auto p = quote_string(std::forward<R>(r), std::ranges::begin(iov));
*p = '\0';
return as_string_view(std::ranges::begin(iov), p);
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
constexpr size_t quote_stringlen(R &&r) {
size_t n = 0;
for (auto c : r) {
if (c == '"') {
n += 2;
} else {
++n;
}
}
return n;
}
inline constexpr auto hexdigits = []() {
constexpr char LOWER_XDIGITS[] = "0123456789abcdef";
std::array<char, 512> tbl;
for (size_t i = 0; i < 256; ++i) {
tbl[i * 2] = LOWER_XDIGITS[static_cast<size_t>(i >> 4)];
tbl[i * 2 + 1] = LOWER_XDIGITS[static_cast<size_t>(i & 0xf)];
}
return tbl;
}();
template <std::input_iterator I, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char> &&
sizeof(std::iter_value_t<I>) == sizeof(uint8_t))
constexpr O format_hex(I first, I last, O result) {
for (; first != last; ++first) {
result = std::ranges::copy_n(
hexdigits.data() + static_cast<uint8_t>(*first) * 2, 2, result)
.out;
}
return result;
}
template <std::ranges::input_range R, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char> &&
!std::is_array_v<std::remove_cvref_t<R>> &&
sizeof(std::ranges::range_value_t<R>) == sizeof(uint8_t))
constexpr O format_hex(R &&r, O result) {
return format_hex(std::ranges::begin(r), std::ranges::end(r),
std::move(result));
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>> &&
sizeof(std::ranges::range_value_t<R>) == sizeof(uint8_t))
std::string_view format_hex(BlockAllocator &balloc, R &&r) {
auto iov = make_byte_ref(balloc, std::ranges::size(r) * 2 + 1);
auto p = format_hex(std::forward<R>(r), std::ranges::begin(iov));
*p = '\0';
return as_string_view(std::ranges::begin(iov), p);
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>> &&
sizeof(std::ranges::range_value_t<R>) == sizeof(uint8_t))
constexpr std::string format_hex(R &&r) {
std::string res;
res.resize(as_unsigned(std::ranges::distance(r) * 2));
format_hex(std::forward<R>(r), std::ranges::begin(res));
return res;
}
template <std::unsigned_integral T, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
constexpr O format_hex(T n, O result) {
if constexpr (sizeof(n) == 1) {
return std::ranges::copy_n(hexdigits.data() + n * 2, 2, result).out;
}
if constexpr (std::endian::native == std::endian::little) {
auto end = reinterpret_cast<uint8_t *>(&n);
auto p = end + sizeof(n);
for (; p != end; --p) {
result =
std::ranges::copy_n(hexdigits.data() + *(p - 1) * 2, 2, result).out;
}
} else {
auto p = reinterpret_cast<uint8_t *>(&n);
auto end = p + sizeof(n);
for (; p != end; ++p) {
result = std::ranges::copy_n(hexdigits.data() + *p * 2, 2, result).out;
}
}
return result;
}
inline constexpr auto upper_hexdigits = []() {
constexpr char UPPER_XDIGITS[] = "0123456789ABCDEF";
std::array<char, 512> tbl;
for (size_t i = 0; i < 256; ++i) {
tbl[i * 2] = UPPER_XDIGITS[static_cast<size_t>(i >> 4)];
tbl[i * 2 + 1] = UPPER_XDIGITS[static_cast<size_t>(i & 0xf)];
}
return tbl;
}();
template <std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
constexpr O format_upper_hex(uint8_t c, O result) {
return std::ranges::copy_n(upper_hexdigits.data() + c * 2, 2, result).out;
}
template <std::input_iterator I, std::weakly_incrementable O>
requires(std::indirectly_writable<O, uint8_t>)
constexpr O decode_hex(I first, I last, O result) {
for (; first != last; first = std::ranges::next(first, 2)) {
*result++ = static_cast<std::iter_value_t<O>>(
(hex_to_uint(*first) << 4) | hex_to_uint(*std::ranges::next(first, 1)));
}
return result;
}
template <std::ranges::input_range R, std::weakly_incrementable O>
requires(std::indirectly_writable<O, uint8_t> &&
!std::is_array_v<std::remove_cvref_t<R>>)
constexpr O decode_hex(R &&r, O result) {
return decode_hex(std::ranges::begin(r), std::ranges::end(r),
std::move(result));
}
template <std::input_iterator I>
std::span<const uint8_t> decode_hex(BlockAllocator &balloc, I first, I last) {
auto iov =
make_byte_ref(balloc, as_unsigned(std::ranges::distance(first, last) / 2));
auto p =
decode_hex(std::move(first), std::move(last), std::ranges::begin(iov));
return {std::ranges::begin(iov), p};
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
std::span<const uint8_t> decode_hex(BlockAllocator &balloc, R &&r) {
return decode_hex(balloc, std::ranges::begin(r), std::ranges::end(r));
}
template <std::input_iterator I, std::weakly_incrementable O>
requires(std::indirectly_copyable<I, O>)
constexpr O percent_encode_token(I first, I last, O result) noexcept {
using result_type = std::iter_value_t<O>;
for (; first != last; ++first) {
auto c = static_cast<uint8_t>(*first);
if (c != '%' && in_token(as_signed(c))) {
*result++ = static_cast<result_type>(c);
continue;
}
*result++ = '%';
result = format_upper_hex(c, result);
}
return result;
}
template <std::ranges::input_range R, std::weakly_incrementable O>
requires(std::indirectly_copyable<std::ranges::iterator_t<R>, O> &&
!std::is_array_v<std::remove_cvref_t<R>>)
constexpr O percent_encode_token(R &&r, O result) {
return percent_encode_token(std::ranges::begin(r), std::ranges::end(r),
std::move(result));
}
template <std::ranges::input_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
constexpr size_t percent_encode_tokenlen(R &&r) noexcept {
size_t n = 0;
for (auto c : r) {
if (c != '%' && in_token(c)) {
++n;
continue;
}
n += 3;
}
return n;
}
time_t parse_http_date(const std::string_view &s);
time_t parse_openssl_asn1_time_print(const std::string_view &s);
inline constexpr auto upcase_tbl = []() {
std::array<char, 256> tbl;
for (size_t i = 0; i < 256; ++i) {
if ('a' <= i && i <= 'z') {
tbl[i] = static_cast<char>(i - 'a' + 'A');
} else {
tbl[i] = static_cast<char>(i);
}
}
return tbl;
}();
constexpr char upcase(char c) noexcept {
return upcase_tbl[static_cast<uint8_t>(c)];
}
inline constexpr uint8_t lowcase_tbl[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'z', 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
255,
};
constexpr char lowcase(char c) noexcept {
return static_cast<char>(lowcase_tbl[static_cast<uint8_t>(c)]);
}
template <std::ranges::input_range R1, std::ranges::input_range R2>
constexpr bool starts_with(R1 &&s, R2 &&prefix) {
auto prefixlen = std::ranges::distance(prefix);
return std::ranges::distance(s) >= prefixlen &&
std::ranges::equal(std::views::take(std::forward<R1>(s), prefixlen),
std::forward<R2>(prefix));
}
struct CaseCmp {
constexpr bool operator()(char lhs, char rhs) const noexcept {
return lowcase(lhs) == lowcase(rhs);
}
};
template <std::ranges::input_range R1, std::ranges::input_range R2>
constexpr bool istarts_with(R1 &&s, R2 &&prefix) {
auto prefixlen = std::ranges::distance(prefix);
return std::ranges::distance(s) >= prefixlen &&
std::ranges::equal(std::views::take(std::forward<R1>(s), prefixlen),
std::forward<R2>(prefix), CaseCmp());
}
template <std::ranges::input_range R1, std::ranges::input_range R2>
constexpr bool ends_with(R1 &&s, R2 &&suffix) {
auto slen = std::ranges::distance(s);
auto suffixlen = std::ranges::distance(suffix);
return slen >= suffixlen &&
std::ranges::equal(
std::views::drop(std::forward<R1>(s), slen - suffixlen),
std::forward<R2>(suffix));
}
template <std::ranges::input_range R1, std::ranges::input_range R2>
constexpr bool iends_with(R1 &&s, R2 &&suffix) {
auto slen = std::ranges::distance(s);
auto suffixlen = std::ranges::distance(suffix);
return slen >= suffixlen &&
std::ranges::equal(
std::views::drop(std::forward<R1>(s), slen - suffixlen),
std::forward<R2>(suffix), CaseCmp());
}
template <std::ranges::input_range R1, std::ranges::input_range R2>
constexpr bool strieq(R1 &&a, R2 &&b) {
return std::ranges::equal(std::forward<R1>(a), std::forward<R2>(b),
CaseCmp());
}
template <std::ranges::input_range R1, std::ranges::input_range R2>
constexpr bool streq(R1 &&a, R2 &&b) {
return std::ranges::equal(std::forward<R1>(a), std::forward<R2>(b));
}
template <std::input_iterator I, std::weakly_incrementable O>
requires(std::indirectly_copyable<I, O>)
constexpr O tolower(I first, I last, O result) {
return std::ranges::transform(std::move(first), std::move(last),
std::move(result), lowcase)
.out;
}
template <std::ranges::input_range R, std::weakly_incrementable O>
requires(std::indirectly_copyable<std::ranges::iterator_t<R>, O> &&
!std::is_array_v<std::remove_cvref_t<R>>)
constexpr O tolower(R &&r, O result) {
return std::ranges::transform(std::forward<R>(r), std::move(result), lowcase)
.out;
}
std::string dtos(double n);
inline constexpr auto count_digit_tbl = []() {
std::array<uint64_t, std::numeric_limits<uint64_t>::digits10> tbl;
uint64_t x = 1;
for (size_t i = 0; i < tbl.size(); ++i) {
x *= 10;
tbl[i] = x - 1;
}
return tbl;
}();
template <std::unsigned_integral T> constexpr size_t count_digit(T x) {
auto y = static_cast<size_t>(19 * (std::numeric_limits<T>::digits - 1 -
std::countl_zero(static_cast<T>(x | 1))) >>
6);
y += x > count_digit_tbl[y];
return y + 1;
}
inline constexpr auto utos_digits = []() {
std::array<char, 200> a;
for (size_t i = 0; i < 100; ++i) {
a[i * 2] = '0' + static_cast<char>(i / 10);
a[i * 2 + 1] = '0' + static_cast<char>(i % 10);
}
return a;
}();
struct UIntFormatter {
template <std::unsigned_integral T, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
constexpr O operator()(T n, O result) {
using result_type = std::iter_value_t<O>;
if (n < 10) {
*result++ = static_cast<result_type>('0' + static_cast<char>(n));
return result;
}
if (n < 100) {
return std::ranges::copy_n(utos_digits.data() + n * 2, 2, result).out;
}
std::ranges::advance(result, as_signed(count_digit(n)));
auto p = result;
for (; n >= 100; n /= 100) {
std::ranges::advance(p, -2);
std::ranges::copy_n(utos_digits.data() + (n % 100) * 2, 2, p);
}
if (n < 10) {
*--p = static_cast<result_type>('0' + static_cast<char>(n));
return result;
}
std::ranges::advance(p, -2);
std::ranges::copy_n(utos_digits.data() + n * 2, 2, p);
return result;
}
};
template <std::unsigned_integral T, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
constexpr O utos(T n, O result) {
return UIntFormatter{}(std::move(n), std::move(result));
}
template <std::unsigned_integral T> constexpr std::string utos(T n) {
using namespace std::literals;
if (n == 0) {
return "0"s;
}
std::string res;
res.resize(count_digit(n));
utos(n, std::ranges::begin(res));
return res;
}
template <std::unsigned_integral T>
std::string_view make_string_ref_uint(BlockAllocator &balloc, T n) {
auto iov = make_byte_ref(
balloc, count_digit(static_cast<std::make_unsigned_t<T>>(n)) + 1);
auto p = std::ranges::begin(iov);
p = util::utos(n, p);
*p = '\0';
return as_string_view(std::ranges::begin(iov), p);
}
template <std::unsigned_integral T> constexpr std::string utos_unit(T n) {
char u;
if (n >= (1 << 30)) {
u = 'G';
n /= (1 << 30);
} else if (n >= (1 << 20)) {
u = 'M';
n /= (1 << 20);
} else if (n >= (1 << 10)) {
u = 'K';
n /= (1 << 10);
} else {
return utos(n);
}
return utos(n) + u;
}
template <std::unsigned_integral T> constexpr std::string utos_funit(T n) {
char u;
int b;
if (n >= (1 << 30)) {
u = 'G';
b = 30;
} else if (n >= (1 << 20)) {
u = 'M';
b = 20;
} else if (n >= (1 << 10)) {
u = 'K';
b = 10;
} else {
return utos(n);
}
return dtos(static_cast<double>(n) / (1 << b)) + u;
}
struct CompactHexFormatter {
template <std::integral T, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
O operator()(T n, O result) {
using result_type = std::iter_value_t<O>;
if (n == 0) {
*result++ = '0';
return result;
}
if constexpr (std::endian::native == std::endian::little) {
auto end = reinterpret_cast<uint8_t *>(&n);
auto p = end + sizeof(n);
for (; p != end && *(p - 1) == 0; --p)
;
assert(p != end);
if (*(p - 1) < 16) {
*result++ = static_cast<result_type>(upper_hexdigits[*--p * 2 + 1]);
}
for (; p != end; --p) {
result = format_upper_hex(*(p - 1), result);
}
} else {
auto p = reinterpret_cast<uint8_t *>(&n);
auto end = p + sizeof(n);
for (; p != end && *p == 0; ++p)
;
if (*p < 16) {
*result++ = static_cast<result_type>(upper_hexdigits[*p++ * 2 + 1]);
}
for (; p != end; ++p) {
result = format_upper_hex(*p, result);
}
}
return result;
}
};
template <std::integral T, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
O utox(T n, O result) {
return CompactHexFormatter{}(std::move(n), std::move(result));
}
void to_token68(std::string &base64str);
std::string_view to_base64(BlockAllocator &balloc,
const std::string_view &token68str);
void show_candidates(const char *unkopt, const option *options);
bool has_uri_field(const urlparse_url &u, urlparse_url_fields field);
bool fieldeq(const char *uri1, const urlparse_url &u1, const char *uri2,
const urlparse_url &u2, urlparse_url_fields field);
bool fieldeq(const char *uri, const urlparse_url &u, urlparse_url_fields field,
const char *t);
bool fieldeq(const char *uri, const urlparse_url &u, urlparse_url_fields field,
const std::string_view &t);
std::string_view get_uri_field(const char *uri, const urlparse_url &u,
urlparse_url_fields field);
uint16_t get_default_port(const char *uri, const urlparse_url &u);
bool porteq(const char *uri1, const urlparse_url &u1, const char *uri2,
const urlparse_url &u2);
void write_uri_field(std::ostream &o, const char *uri, const urlparse_url &u,
urlparse_url_fields field);
bool numeric_host(const char *hostname);
bool numeric_host(const char *hostname, int family);
std::string numeric_name(const struct sockaddr *sa, socklen_t salen);
std::string to_numeric_addr(const Address *addr);
std::string to_numeric_addr(const struct sockaddr *sa, socklen_t salen);
void set_port(Address &addr, uint16_t port);
uint16_t get_port(const sockaddr_union *su);
bool quic_prohibited_port(uint16_t port);
std::string ascii_dump(const uint8_t *data, size_t len);
char *get_exec_path(size_t argc, char **const argv, const char *cwd);
bool check_path(const std::string &path);
int64_t to_time64(const timeval &tv);
bool check_h2_is_selected(const std::string_view &proto);
bool select_h2(const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen);
bool select_protocol(const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen,
std::vector<std::string> proto_list);
std::vector<std::string> parse_config_str_list(const std::string_view &s,
char delim = ',');
std::vector<std::string_view> split_str(const std::string_view &s, char delim);
std::vector<std::string_view> split_str(const std::string_view &s, char delim,
size_t n);
std::string_view
format_common_log(char *out, const std::chrono::system_clock::time_point &tp);
#ifdef HAVE_STD_CHRONO_TIME_ZONE
std::string_view
format_common_log(char *out, const std::chrono::system_clock::time_point &tp,
const std::chrono::time_zone *tz);
#endif
std::string format_iso8601(const std::chrono::system_clock::time_point &tp);
std::string_view
format_iso8601(char *out, const std::chrono::system_clock::time_point &tp);
#ifdef HAVE_STD_CHRONO_TIME_ZONE
std::string_view format_iso8601(char *out,
const std::chrono::system_clock::time_point &tp,
const std::chrono::time_zone *tz);
#endif
std::string_view
format_iso8601_basic(char *out,
const std::chrono::system_clock::time_point &tp);
#ifdef HAVE_STD_CHRONO_TIME_ZONE
std::string_view
format_iso8601_basic(char *out, const std::chrono::system_clock::time_point &tp,
const std::chrono::time_zone *tz);
#endif
std::string format_http_date(const std::chrono::system_clock::time_point &tp);
std::string_view
format_http_date(char *out, const std::chrono::system_clock::time_point &tp);
template <typename Clock, typename Rep> Rep clock_precision() {
std::chrono::duration<Rep, std::nano> duration = typename Clock::duration(1);
return duration.count();
}
#ifdef HAVE_LIBEV
template <typename Duration = std::chrono::steady_clock::duration>
Duration duration_from(ev_tstamp d) {
return std::chrono::duration_cast<Duration>(std::chrono::duration<double>(d));
}
template <typename Duration> ev_tstamp ev_tstamp_from(const Duration &d) {
return std::chrono::duration<double>(d).count();
}
#endif
int make_socket_closeonexec(int fd);
int make_socket_nonblocking(int fd);
int make_socket_nodelay(int fd);
int create_nonblock_socket(int family);
int create_nonblock_udp_socket(int family);
int bind_any_addr_udp(int fd, int family);
bool check_socket_connected(int fd);
int get_socket_error(int fd);
bool ipv6_numeric_addr(const char *host);
std::optional<int64_t> parse_uint_with_unit(const std::string_view &s);
std::optional<int64_t> parse_uint(const std::string_view &s);
std::optional<double> parse_duration_with_unit(const std::string_view &s);
std::string duration_str(double t);
std::string format_duration(const std::chrono::microseconds &u);
std::string format_duration(double t);
inline constexpr size_t max_hostport = NI_MAXHOST + 2 +
1 +
5 + 1;
std::string_view make_hostport(BlockAllocator &balloc,
const std::string_view &host, uint16_t port);
template <std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
std::string_view make_hostport(const std::string_view &host, uint16_t port,
O result) {
auto ipv6 = ipv6_numeric_addr(host.data());
auto p = result;
if (ipv6) {
*p++ = '[';
}
p = std::ranges::copy(host, p).out;
if (ipv6) {
*p++ = ']';
}
*p++ = ':';
p = utos(port, p);
*p = '\0';
return as_string_view(result, p);
}
std::string_view make_http_hostport(BlockAllocator &balloc,
const std::string_view &host,
uint16_t port);
template <std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
std::string_view make_http_hostport(const std::string_view &host, uint16_t port,
O result) {
if (port != 80 && port != 443) {
return make_hostport(host, port, std::move(result));
}
auto ipv6 = ipv6_numeric_addr(host.data());
auto p = result;
if (ipv6) {
*p++ = '[';
}
p = std::ranges::copy(host, p).out;
if (ipv6) {
*p++ = ']';
}
*p = '\0';
return as_string_view(result, p);
}
int hexdump(FILE *out, const void *data, size_t datalen);
void put_uint16be(uint8_t *buf, uint16_t n);
void put_uint32be(uint8_t *buf, uint32_t n);
uint16_t get_uint16(const uint8_t *data);
uint32_t get_uint32(const uint8_t *data);
uint64_t get_uint64(const uint8_t *data);
int read_mime_types(std::unordered_map<std::string, std::string> &res,
const char *filename);
template <typename OutputIt, typename Generator>
OutputIt random_alpha_digit(OutputIt first, OutputIt last, Generator &gen) {
static constexpr char s[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
std::uniform_int_distribution<> dis(0, 26 * 2 + 10 - 1);
for (; first != last; ++first) {
*first = static_cast<std::iter_value_t<OutputIt>>(s[dis(gen)]);
}
return first;
}
template <std::input_or_output_iterator O, typename Generator>
void random_bytes(O first, O last, Generator &&gen) {
std::uniform_int_distribution<uint8_t> dis;
std::ranges::generate(std::move(first), std::move(last),
[&dis, &gen]() { return dis(gen); });
}
void secure_random(uint8_t *dest, size_t destlen);
template <std::random_access_iterator I, typename Generator, typename Swap>
void shuffle(I first, I last, Generator &&gen, Swap swap) {
auto len = std::ranges::distance(first, last);
if (len < 2) {
return;
}
using dist_type = std::uniform_int_distribution<decltype(len)>;
using param_type = dist_type::param_type;
dist_type d;
for (decltype(len) i = 0; i < len - 1; ++i) {
swap(first + i, first + d(gen, param_type(i, len - 1)));
}
}
template <std::ranges::input_range R, typename Generator, typename Swap>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
void shuffle(R &&r, Generator &&gen, Swap swap) {
return shuffle(std::ranges::begin(r), std::ranges::end(r),
std::forward<Generator>(gen), std::move(swap));
}
double int_pow(double x, size_t y);
uint32_t hash32(const std::string_view &s);
int sha256(uint8_t *buf, const std::string_view &s);
int sha1(uint8_t *buf, const std::string_view &s);
std::string_view extract_host(const std::string_view &hostport);
std::pair<std::string_view, std::string_view>
split_hostport(const std::string_view &hostport);
std::mt19937 make_mt19937();
int daemonize(int nochdir, int noclose);
std::string_view rstrip(BlockAllocator &balloc, const std::string_view &s);
template <std::ranges::input_range R, typename T>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
bool contains(R &&r, const T &value) {
return std::ranges::find(r, value) != std::ranges::end(r);
}
template <std::input_iterator I, typename T>
constexpr bool contains(I first, I last, const T &value) {
return std::ranges::find(std::move(first), last, value) != last;
}
#ifdef ENABLE_HTTP3
int msghdr_get_local_addr(Address &dest, msghdr *msg, int family);
uint8_t msghdr_get_ecn(msghdr *msg, int family);
size_t msghdr_get_udp_gro(msghdr *msg);
#endif
}
}
#endif