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