summaryrefslogtreecommitdiffstats
path: root/server/src/util.cppm
blob: 2857fe581f2336d26e3b4e5a73667e01dd0ff278 (about) (plain) (blame)
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
// Stuff that doesn't really have a place right now, but that is
// broadly useful.
export module routemon:util;

import std;

namespace routemon::util {

// For use with e.g. std::visit (on std::variant).
template <class... Ts>
struct overloaded : Ts...
{
  using Ts::operator()...;
};

constexpr auto parse_double(
    std::string_view s,
    std::chars_format fmt = std::chars_format::general) noexcept
    -> std::optional<double>
{
  auto x = 0.0;
  auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt);
  if (ec == std::errc{})
  {
    return x;
  }
  else
  {
    return std::nullopt;
  }
}

constexpr auto parse_float(
    std::string_view s,
    std::chars_format fmt = std::chars_format::general) noexcept
    -> std::optional<float>
{
  auto x = 0.0;
  auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt);
  if (ec == std::errc{})
  {
    return x;
  }
  else
  {
    return std::nullopt;
  }
}

template <class T>
class aolist : public std::enable_shared_from_this<aolist<T>>
{
  T v_;
  std::shared_ptr<aolist<T> const> next_;

  explicit aolist(T v, std::shared_ptr<aolist<T> const> next)
    : v_{v}, next_{next}
  {
  }

public:
  static auto nil() -> std::shared_ptr<aolist<T>> { return nullptr; }

  static auto cons(T v, std::shared_ptr<aolist<T> const> l)
      -> std::shared_ptr<aolist<T>>
  {
    return std::shared_ptr<aolist<T>>{new aolist<T>{v, l}};
  }

  auto next() const -> std::shared_ptr<aolist<T> const> { return next_; }

  auto value() const noexcept -> T const& { return v_; }
};

constexpr auto size_from_int(int x) -> std::optional<std::size_t>
{
  static_assert(
      sizeof(int) <= sizeof(std::size_t),
      "cannot cast int to smaller size_t type");
  if (x < 0)
    return std::nullopt;
  return static_cast<std::size_t>(x);
}

constexpr auto int_from_size(std::size_t x) -> std::optional<int>
{
  constexpr auto int_max = size_from_int(std::numeric_limits<int>::max());
  static_assert(int_max.has_value());
  if (x > *int_max)
    return std::nullopt;
  return static_cast<int>(x);
}

class zstring_view
{
  char const* s_;
  std::size_t length_;

public:
  constexpr explicit zstring_view(char const* s, std::size_t length)
    : s_{s}, length_{length}
  {
  }

  constexpr zstring_view(char const* s)
    : zstring_view{s, std::char_traits<char>::length(s)}
  {
  }

  constexpr auto length() const -> std::size_t { return length_; }

  constexpr auto c_str() const -> char const* { return s_; }

  constexpr operator std::string_view() const
  {
    return std::string_view{s_, length_};
  }

  constexpr operator char const*() const { return s_; }
};

// View for null-terminated strings for which we might not
// necessarily be interested in the length. String length is only
// calculated on demand, at most once on each thread (on more than
// one thread when racing). May be null.
//
// It is undefined behavior to assign to a lazy_zstring_view when
// it is in use by other threads.
class lazy_zstring_view
{
  static constexpr auto unset_length = std::numeric_limits<std::size_t>::max();

  char const* s_; // nullable
  mutable std::atomic<std::size_t> length_;
  static_assert(decltype(length_)::is_always_lock_free);

public:
  constexpr explicit lazy_zstring_view(char const* s)
    : s_{s}, length_{s ? unset_length : 0}
  {
  }
  lazy_zstring_view(lazy_zstring_view const& sv) noexcept;
  lazy_zstring_view(lazy_zstring_view&& sv) noexcept;

  ~lazy_zstring_view() = default;

  auto operator=(lazy_zstring_view const& rhs) noexcept -> lazy_zstring_view&;
  auto operator=(lazy_zstring_view&& rhs) noexcept -> lazy_zstring_view&;

  auto length() const noexcept -> std::size_t;
  auto c_str() const noexcept -> char const*;

  operator std::string_view() const noexcept;
  operator char const*() const noexcept;

  auto operator==(std::string_view sv) const noexcept -> bool;
};

constexpr auto operator""_zsv(char const* s, std::size_t length) noexcept
    -> zstring_view
{
  return zstring_view{s, length};
}

auto operator==(zstring_view lhs, zstring_view rhs) -> bool;

constexpr auto split_on(std::string_view s, char c)
    -> std::pair<std::string_view, std::optional<std::string_view>>
{
  if (auto i = s.find(c); i != std::string_view::npos)
    return std::make_pair(s.substr(0, i), s.substr(i + 1));
  return std::make_pair(s, std::nullopt);
}

template <class T>
class not_null;

template <class T>
class not_null<T*>
{
  T* p_;

  struct guaranteed_not_null_t
  {
  };
  explicit not_null(T* p, guaranteed_not_null_t) noexcept : p_{p} {}

public:
  explicit not_null(T* p) : p_{p}
  {
    if (!p_)
      throw std::runtime_error{"not_null constructed with null pointer"};
  }
  ~not_null() = default;

  not_null(not_null const& other) = default;
  not_null(not_null&& other) noexcept = default;
  auto operator=(not_null const& rhs) noexcept -> not_null& = default;
  auto operator=(not_null&& rhs) noexcept -> not_null& = default;

  friend auto make_not_null(T* p) noexcept -> std::optional<not_null>
  {
    if (p)
      return not_null(p, guaranteed_not_null_t{});
    else
      return std::nullopt;
  }

  [[nodiscard]] auto get() const noexcept -> T* { return p_; }

  auto operator*() const noexcept -> std::add_lvalue_reference_t<T>
  {
    return *p_;
  }

  auto operator->() const noexcept -> T* { return p_; }
};
template <class T>
explicit not_null(T*) -> not_null<T*>;

template <>
class not_null<lazy_zstring_view>
{
  lazy_zstring_view s_;

public:
  explicit not_null(lazy_zstring_view s) : s_{std::move(s)}
  {
    if (!s_)
      throw std::runtime_error{"not_null constructed with null pointer"};
  }

  [[nodiscard]] auto get() const noexcept -> lazy_zstring_view { return s_; }

  operator lazy_zstring_view() const noexcept { return s_; }
  operator std::string_view() const noexcept { return s_; }
  operator char const*() const noexcept { return s_; }
};
explicit not_null(lazy_zstring_view s) -> not_null<lazy_zstring_view>;

} // namespace routemon::util