TLA Line data 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/ip_address.hpp>
17 :
18 : #include <boost/capy/io_result.hpp>
19 :
20 : #include <compare>
21 : #include <cstdint>
22 : #include <string_view>
23 : #include <system_error>
24 :
25 : namespace boost::corosio {
26 :
27 : /** Pairs an IP address with a port for either IPv4 or IPv6.
28 :
29 : This class represents an endpoint for IP communication,
30 : consisting of an IP address of either family and a port number.
31 : Use an endpoint to specify a connection target or bind address.
32 :
33 : @par Thread Safety
34 : Distinct objects: Safe.@n
35 : Shared objects: Safe.
36 :
37 : @par Example
38 : @par !example endpoint
39 : */
40 : class endpoint
41 : {
42 : ip_address addr_;
43 : std::uint16_t port_ = 0;
44 :
45 : public:
46 : /** Creates an endpoint with the IPv4 any address (0.0.0.0) and port 0.
47 : */
48 HIT 145289 : endpoint() noexcept = default;
49 :
50 : /** Construct from an IP address and port.
51 :
52 : `ipv4_address` and `ipv6_address` arguments convert
53 : implicitly, so both families construct directly:
54 : `endpoint(ipv4_address::loopback(), 80)`.
55 :
56 : @param addr The IP address.
57 : @param p The port number in host byte order.
58 : */
59 15334 : endpoint(ip_address addr, std::uint16_t p) noexcept : addr_(addr), port_(p)
60 : {
61 15334 : }
62 :
63 : /** Construct from port only.
64 :
65 : Uses the IPv4 any address (0.0.0.0), which binds to all
66 : available network interfaces.
67 :
68 : @param p The port number in host byte order.
69 : */
70 22 : explicit endpoint(std::uint16_t p) noexcept : port_(p) {}
71 :
72 : /** Construct from an endpoint's address with a different port.
73 :
74 : Creates a new endpoint using the address from an existing
75 : endpoint but with a different port number.
76 :
77 : @param ep The endpoint whose address to use.
78 : @param p The port number in host byte order.
79 : */
80 2 : endpoint(endpoint const& ep, std::uint16_t p) noexcept
81 2 : : addr_(ep.addr_)
82 2 : , port_(p)
83 : {
84 2 : }
85 :
86 : /** Construct from a string.
87 :
88 : Parses an endpoint string in one of the following formats:
89 : @li IPv4 without port: `192.168.1.1`
90 : @li IPv4 with port: `192.168.1.1:8080`
91 : @li IPv6 without port: `::1` or `2001:db8::1`
92 : @li IPv6 with port (bracketed): `[::1]:8080`
93 :
94 : @param s The string to parse.
95 :
96 : @throws std::system_error on parse failure.
97 :
98 : @see make_endpoint for the non-throwing form.
99 : */
100 : explicit endpoint(std::string_view s);
101 :
102 : /** Check if this endpoint uses an IPv4 address.
103 :
104 : @return `true` if the endpoint uses IPv4, `false` if IPv6.
105 : */
106 10326 : bool is_v4() const noexcept
107 : {
108 10326 : return addr_.is_v4();
109 : }
110 :
111 : /** Check if this endpoint uses an IPv6 address.
112 :
113 : @return `true` if the endpoint uses IPv6, `false` if IPv4.
114 : */
115 61 : bool is_v6() const noexcept
116 : {
117 61 : return addr_.is_v6();
118 : }
119 :
120 : /** Return the IP address.
121 :
122 : @return The endpoint's address.
123 : */
124 5724 : ip_address address() const noexcept
125 : {
126 5724 : return addr_;
127 : }
128 :
129 : /** Return the port number.
130 :
131 : @return The port number in host byte order.
132 : */
133 6123 : std::uint16_t port() const noexcept
134 : {
135 6123 : return port_;
136 : }
137 :
138 : /** Compare endpoints for equality.
139 :
140 : Two endpoints are equal if they have the same address type,
141 : the same address value, and the same port.
142 :
143 : @return `true` if both endpoints are equal.
144 : */
145 102 : friend bool operator==(endpoint const& a, endpoint const& b) noexcept
146 : {
147 102 : return a.port_ == b.port_ && a.addr_ == b.addr_;
148 : }
149 :
150 : /** Order two endpoints.
151 :
152 : Establishes a strict total ordering consistent with
153 : @ref operator==: equal endpoints compare equivalent.
154 : `operator<=>` orders endpoints first by address family
155 : (IPv4 before IPv6), then by address value, then by port. This
156 : makes `endpoint` usable as a key in ordered containers
157 : such as `std::map` and `std::set`.
158 :
159 : @return The relative order of @p a and @p b.
160 : */
161 : friend std::strong_ordering
162 25 : operator<=>(endpoint const& a, endpoint const& b) noexcept
163 : {
164 25 : if (auto c = a.addr_ <=> b.addr_; c != 0)
165 12 : return c;
166 13 : return a.port_ <=> b.port_;
167 : }
168 : };
169 :
170 : /** Identifies which of the four supported endpoint string formats a string is in.
171 :
172 : Used internally by `make_endpoint` to determine
173 : the format of an endpoint string.
174 : */
175 : enum class endpoint_format
176 : {
177 : ipv4_no_port, ///< "192.168.1.1"
178 : ipv4_with_port, ///< "192.168.1.1:8080"
179 : ipv6_no_port, ///< "::1" or "1:2:3:4:5:6:7:8"
180 : ipv6_bracketed ///< "[::1]" or "[::1]:8080"
181 : };
182 :
183 : /** Detect the format of an endpoint string.
184 :
185 : This helper function determines the endpoint format
186 : based on simple rules:
187 : 1. Starts with `[` -> `ipv6_bracketed`
188 : 2. Else count `:` characters:
189 : - 0 colons -> `ipv4_no_port`
190 : - 1 colon -> `ipv4_with_port`
191 : - 2+ colons -> `ipv6_no_port`
192 :
193 : @param s The string to analyze.
194 : @return The detected endpoint format.
195 : */
196 : BOOST_COROSIO_DECL
197 : endpoint_format detect_endpoint_format(std::string_view s) noexcept;
198 :
199 : /** Create an endpoint from a string.
200 :
201 : This function parses an endpoint string in one of
202 : the following formats:
203 :
204 : @li IPv4 without port: `192.168.1.1`
205 : @li IPv4 with port: `192.168.1.1:8080`
206 : @li IPv6 without port: `::1` or `2001:db8::1`
207 : @li IPv6 with port (bracketed): `[::1]:8080`
208 :
209 : @par Example
210 : @par !example make_endpoint
211 :
212 : @param s The string to parse.
213 : @return The error code, empty on success, and the parsed
214 : endpoint — default-constructed on failure.
215 : */
216 : [[nodiscard]] BOOST_COROSIO_DECL capy::io_result<endpoint>
217 : make_endpoint(std::string_view s) noexcept;
218 :
219 27 : inline endpoint::endpoint(std::string_view s)
220 : {
221 27 : auto [ec, ep] = make_endpoint(s);
222 27 : if (ec)
223 16 : detail::throw_system_error(ec);
224 11 : *this = ep;
225 11 : }
226 :
227 : } // namespace boost::corosio
228 :
229 : namespace std {
230 :
231 : /// Hash support for `boost::corosio::endpoint`.
232 : template<>
233 : struct hash<boost::corosio::endpoint>
234 : {
235 : /// Return the hash of `ep`.
236 12 : std::size_t operator()(boost::corosio::endpoint const& ep) const noexcept
237 : {
238 12 : std::size_t const h1 = hash<boost::corosio::ip_address>()(ep.address());
239 12 : std::size_t const h2 = hash<std::uint16_t>()(ep.port());
240 12 : return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
241 : }
242 : };
243 :
244 : } // namespace std
245 :
246 : #endif
|