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