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