include/boost/corosio/endpoint.hpp

100.0% Lines (66/0/66) 100.0% List of functions (13/0/13)
endpoint.hpp
f(x) Functions (13)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Michael Vandeberg
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #ifndef BOOST_COROSIO_ENDPOINT_HPP
12 #define BOOST_COROSIO_ENDPOINT_HPP
13
14 #include <boost/corosio/detail/config.hpp>
15 #include <boost/corosio/detail/except.hpp>
16 #include <boost/corosio/ipv4_address.hpp>
17 #include <boost/corosio/ipv6_address.hpp>
18
19 #include <boost/capy/io_result.hpp>
20
21 #include <compare>
22 #include <cstdint>
23 #include <string_view>
24 #include <system_error>
25
26 namespace boost::corosio {
27
28 /** An IP endpoint (address + port) supporting both IPv4 and IPv6.
29
30 This class represents an endpoint for IP communication,
31 consisting of either an IPv4 or IPv6 address and a port number.
32 Endpoints are used to specify connection targets and bind addresses.
33
34 The endpoint holds both address types as separate members (not a union),
35 with a discriminator to track which address type is active.
36
37 @par Thread Safety
38 Distinct objects: Safe.@n
39 Shared objects: Safe.
40
41 @par Example
42 @par !example endpoint
43 */
44 class endpoint
45 {
46 ipv4_address v4_address_;
47 ipv6_address v6_address_;
48 std::uint16_t port_ = 0;
49 bool is_v4_ = true;
50
51 public:
52 /** Default constructor.
53
54 Creates an endpoint with the IPv4 any address (0.0.0.0) and port 0.
55 */
56 150283x endpoint() noexcept
57 150283x : v4_address_(ipv4_address::any())
58 150283x , v6_address_{}
59 150283x , port_(0)
60 150283x , is_v4_(true)
61 {
62 150283x }
63
64 /** Construct from IPv4 address and port.
65
66 @param addr The IPv4 address.
67 @param p The port number in host byte order.
68 */
69 15176x endpoint(ipv4_address addr, std::uint16_t p) noexcept
70 15176x : v4_address_(addr)
71 15176x , v6_address_{}
72 15176x , port_(p)
73 15176x , is_v4_(true)
74 {
75 15176x }
76
77 /** Construct from IPv6 address and port.
78
79 @param addr The IPv6 address.
80 @param p The port number in host byte order.
81 */
82 154x endpoint(ipv6_address addr, std::uint16_t p) noexcept
83 154x : v4_address_(ipv4_address::any())
84 154x , v6_address_(addr)
85 154x , port_(p)
86 154x , is_v4_(false)
87 {
88 154x }
89
90 /** Construct from port only.
91
92 Uses the IPv4 any address (0.0.0.0), which binds to all
93 available network interfaces.
94
95 @param p The port number in host byte order.
96 */
97 22x explicit endpoint(std::uint16_t p) noexcept
98 22x : v4_address_(ipv4_address::any())
99 22x , v6_address_{}
100 22x , port_(p)
101 22x , is_v4_(true)
102 {
103 22x }
104
105 /** Construct from an endpoint's address with a different port.
106
107 Creates a new endpoint using the address from an existing
108 endpoint but with a different port number.
109
110 @param ep The endpoint whose address to use.
111 @param p The port number in host byte order.
112 */
113 2x endpoint(endpoint const& ep, std::uint16_t p) noexcept
114 2x : v4_address_(ep.v4_address_)
115 2x , v6_address_(ep.v6_address_)
116 2x , port_(p)
117 2x , is_v4_(ep.is_v4_)
118 {
119 2x }
120
121 /** Construct from a string.
122
123 Parses an endpoint string in one of the following formats:
124 @li IPv4 without port: `192.168.1.1`
125 @li IPv4 with port: `192.168.1.1:8080`
126 @li IPv6 without port: `::1` or `2001:db8::1`
127 @li IPv6 with port (bracketed): `[::1]:8080`
128
129 @param s The string to parse.
130
131 @throws std::system_error on parse failure.
132
133 @see make_endpoint for the non-throwing form.
134 */
135 explicit endpoint(std::string_view s);
136
137 /** Check if this endpoint uses an IPv4 address.
138
139 @return `true` if the endpoint uses IPv4, `false` if IPv6.
140 */
141 10357x bool is_v4() const noexcept
142 {
143 10357x return is_v4_;
144 }
145
146 /** Check if this endpoint uses an IPv6 address.
147
148 @return `true` if the endpoint uses IPv6, `false` if IPv4.
149 */
150 255x bool is_v6() const noexcept
151 {
152 255x return !is_v4_;
153 }
154
155 /** Get the IPv4 address.
156
157 @return The IPv4 address. The value is valid even if
158 the endpoint is using IPv6 (it will be the default any address).
159 */
160 5446x ipv4_address v4_address() const noexcept
161 {
162 5446x return v4_address_;
163 }
164
165 /** Get the IPv6 address.
166
167 @return The IPv6 address. The value is valid even if
168 the endpoint is using IPv4 (it will be the default any address).
169 */
170 64x ipv6_address v6_address() const noexcept
171 {
172 64x return v6_address_;
173 }
174
175 /** Get the port number.
176
177 @return The port number in host byte order.
178 */
179 6101x std::uint16_t port() const noexcept
180 {
181 6101x return port_;
182 }
183
184 /** Compare endpoints for equality.
185
186 Two endpoints are equal if they have the same address type,
187 the same address value, and the same port.
188
189 @return `true` if both endpoints are equal.
190 */
191 99x friend bool operator==(endpoint const& a, endpoint const& b) noexcept
192 {
193 99x if (a.is_v4_ != b.is_v4_)
194 1x return false;
195 98x if (a.port_ != b.port_)
196 3x return false;
197 95x if (a.is_v4_)
198 93x return a.v4_address_ == b.v4_address_;
199 else
200 2x return a.v6_address_ == b.v6_address_;
201 }
202
203 /** Order two endpoints.
204
205 Establishes a strict total ordering consistent with
206 @ref operator==: equal endpoints compare equivalent.
207 Endpoints are ordered first by address family (IPv4
208 before IPv6), then by address value, then by port. This
209 makes `endpoint` usable as a key in ordered containers
210 such as `std::map` and `std::set`.
211
212 @return The relative order of @p a and @p b.
213 */
214 friend std::strong_ordering
215 25x operator<=>(endpoint const& a, endpoint const& b) noexcept
216 {
217 25x if (a.is_v4_ != b.is_v4_)
218 9x return a.is_v4_ ? std::strong_ordering::less
219 9x : std::strong_ordering::greater;
220 16x if (a.is_v4_)
221 {
222 13x if (auto c = a.v4_address_.to_uint() <=> b.v4_address_.to_uint();
223 13x c != 0)
224 2x return c;
225 }
226 else
227 {
228 3x if (auto c = a.v6_address_.to_bytes() <=> b.v6_address_.to_bytes();
229 3x c != 0)
230 1x return c;
231 }
232 13x return a.port_ <=> b.port_;
233 }
234 };
235
236 /** Endpoint format detection result.
237
238 Used internally by make_endpoint to determine
239 the format of an endpoint string.
240 */
241 enum class endpoint_format
242 {
243 ipv4_no_port, ///< "192.168.1.1"
244 ipv4_with_port, ///< "192.168.1.1:8080"
245 ipv6_no_port, ///< "::1" or "1:2:3:4:5:6:7:8"
246 ipv6_bracketed ///< "[::1]" or "[::1]:8080"
247 };
248
249 /** Detect the format of an endpoint string.
250
251 This helper function determines the endpoint format
252 based on simple rules:
253 1. Starts with `[` -> `ipv6_bracketed`
254 2. Else count `:` characters:
255 - 0 colons -> `ipv4_no_port`
256 - 1 colon -> `ipv4_with_port`
257 - 2+ colons -> `ipv6_no_port`
258
259 @param s The string to analyze.
260 @return The detected endpoint format.
261 */
262 BOOST_COROSIO_DECL
263 endpoint_format detect_endpoint_format(std::string_view s) noexcept;
264
265 /** Create an endpoint from a string.
266
267 This function parses an endpoint string in one of
268 the following formats:
269
270 @li IPv4 without port: `192.168.1.1`
271 @li IPv4 with port: `192.168.1.1:8080`
272 @li IPv6 without port: `::1` or `2001:db8::1`
273 @li IPv6 with port (bracketed): `[::1]:8080`
274
275 @par Example
276 @par !example make_endpoint
277
278 @param s The string to parse.
279 @return The error code, empty on success, and the parsed
280 endpoint — default-constructed on failure.
281 */
282 [[nodiscard]] BOOST_COROSIO_DECL capy::io_result<endpoint>
283 make_endpoint(std::string_view s) noexcept;
284
285 25x inline endpoint::endpoint(std::string_view s)
286 {
287 25x auto [ec, ep] = make_endpoint(s);
288 25x if (ec)
289 16x detail::throw_system_error(ec);
290 9x *this = ep;
291 9x }
292
293 } // namespace boost::corosio
294
295 #endif
296