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