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