100.00% Lines (19/19) 100.00% Functions (7/7)
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_IPV6_ADDRESS_HPP 11   #ifndef BOOST_COROSIO_IPV6_ADDRESS_HPP
12   #define BOOST_COROSIO_IPV6_ADDRESS_HPP 12   #define BOOST_COROSIO_IPV6_ADDRESS_HPP
13   13  
14   #include <boost/corosio/detail/config.hpp> 14   #include <boost/corosio/detail/config.hpp>
15   #include <boost/corosio/ipv4_address.hpp> 15   #include <boost/corosio/ipv4_address.hpp>
16   16  
17   #include <boost/capy/results.hpp> 17   #include <boost/capy/results.hpp>
18   18  
19   #include <array> 19   #include <array>
20   #include <compare> 20   #include <compare>
21   #include <cstdint> 21   #include <cstdint>
22   #include <functional> 22   #include <functional>
23   #include <iosfwd> 23   #include <iosfwd>
24   #include <string> 24   #include <string>
25   #include <string_view> 25   #include <string_view>
26   #include <system_error> 26   #include <system_error>
27   27  
28   namespace boost::corosio { 28   namespace boost::corosio {
29   29  
30   /** An IP version 6 style address. 30   /** An IP version 6 style address.
31   31  
32   Objects of this type are used to construct, 32   Objects of this type are used to construct,
33   parse, and manipulate IP version 6 addresses. 33   parse, and manipulate IP version 6 addresses.
34   34  
35   @par BNF 35   @par BNF
36   @code 36   @code
37   IPv6address = 6( h16 ":" ) ls32 37   IPv6address = 6( h16 ":" ) ls32
38   / "::" 5( h16 ":" ) ls32 38   / "::" 5( h16 ":" ) ls32
39   / [ h16 ] "::" 4( h16 ":" ) ls32 39   / [ h16 ] "::" 4( h16 ":" ) ls32
40   / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 40   / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
41   / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 41   / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
42   / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 42   / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
43   / [ *4( h16 ":" ) h16 ] "::" ls32 43   / [ *4( h16 ":" ) h16 ] "::" ls32
44   / [ *5( h16 ":" ) h16 ] "::" h16 44   / [ *5( h16 ":" ) h16 ] "::" h16
45   / [ *6( h16 ":" ) h16 ] "::" 45   / [ *6( h16 ":" ) h16 ] "::"
46   46  
47   ls32 = ( h16 ":" h16 ) / IPv4address 47   ls32 = ( h16 ":" h16 ) / IPv4address
48   ; least-significant 32 bits of address 48   ; least-significant 32 bits of address
49   49  
50   h16 = 1*4HEXDIG 50   h16 = 1*4HEXDIG
51   ; 16 bits of address represented in hexadecimal 51   ; 16 bits of address represented in hexadecimal
52   52  
53   IPv6addrz = IPv6address "%" ZoneID 53   IPv6addrz = IPv6address "%" ZoneID
54   ; rfc6874: an address qualified by its zone 54   ; rfc6874: an address qualified by its zone
55   @endcode 55   @endcode
56   56  
57   The zone accepts a strict decimal interface index on every 57   The zone accepts a strict decimal interface index on every
58   platform; where the platform names interfaces (POSIX), an 58   platform; where the platform names interfaces (POSIX), an
59   interface name maps through `if_nametoindex`. An unknown name 59   interface name maps through `if_nametoindex`. An unknown name
60   or malformed index is a parse error, never a silent zone 0. 60   or malformed index is a parse error, never a silent zone 0.
61   Formatting always emits the numeric form (`%2`). 61   Formatting always emits the numeric form (`%2`).
62   62  
63   @par Specification 63   @par Specification
64   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291" 64   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291"
65   >IP Version 6 Addressing Architecture (rfc4291)</a> 65   >IP Version 6 Addressing Architecture (rfc4291)</a>
66   @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2" 66   @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
67   >3.2.2. Host (rfc3986)</a> 67   >3.2.2. Host (rfc3986)</a>
68   68  
69   @see 69   @see
70   @ref ipv4_address, 70   @ref ipv4_address,
71   @ref make_ipv6_address 71   @ref make_ipv6_address
72   */ 72   */
73   class BOOST_COROSIO_DECL ipv6_address 73   class BOOST_COROSIO_DECL ipv6_address
74   { 74   {
75   std::array<unsigned char, 16> addr_{}; 75   std::array<unsigned char, 16> addr_{};
76   std::uint32_t scope_id_ = 0; 76   std::uint32_t scope_id_ = 0;
77   77  
78   public: 78   public:
79   /** The number of characters in the longest possible IPv6 string. 79   /** The number of characters in the longest possible IPv6 string.
80   80  
81   The longest address body is the IPv4-mapped form 81   The longest address body is the IPv4-mapped form
82   `ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255` (45 characters). A 82   `ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255` (45 characters). A
83   numeric zone suffix adds up to eleven more (`%4294967295`), for a 83   numeric zone suffix adds up to eleven more (`%4294967295`), for a
84   worst case of 56; the constant carries a little slack. 84   worst case of 56; the constant carries a little slack.
85   */ 85   */
86   static constexpr std::size_t max_str_len = 60; 86   static constexpr std::size_t max_str_len = 60;
87   87  
88   /** The type used to represent an address as an array of bytes. 88   /** The type used to represent an address as an array of bytes.
89   89  
90   Octets are stored in network byte order. 90   Octets are stored in network byte order.
91   */ 91   */
92   using bytes_type = std::array<unsigned char, 16>; 92   using bytes_type = std::array<unsigned char, 16>;
93   93  
94   /** Default constructor. 94   /** Default constructor.
95   95  
96   Constructs the unspecified address (::). 96   Constructs the unspecified address (::).
97   97  
98   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.2" 98   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.2"
99   >2.5.2. The Unspecified Address</a> 99   >2.5.2. The Unspecified Address</a>
100   100  
101   @see 101   @see
102   @ref is_unspecified 102   @ref is_unspecified
103   */ 103   */
HITCBC 104   296807 ipv6_address() = default; 104   299492 ipv6_address() = default;
105   105  
106   /** Copy constructor. 106   /** Copy constructor.
107   */ 107   */
108   ipv6_address(ipv6_address const&) = default; 108   ipv6_address(ipv6_address const&) = default;
109   109  
110   /** Copy assignment. 110   /** Copy assignment.
111   111  
112   @return A reference to this object. 112   @return A reference to this object.
113   */ 113   */
114   ipv6_address& operator=(ipv6_address const&) = default; 114   ipv6_address& operator=(ipv6_address const&) = default;
115   115  
116   /** Construct from an array of bytes. 116   /** Construct from an array of bytes.
117   117  
118   This function constructs an address 118   This function constructs an address
119   from the array in `bytes`, which is 119   from the array in `bytes`, which is
120   interpreted in big-endian. 120   interpreted in big-endian.
121   121  
122   @param bytes The value to construct from. 122   @param bytes The value to construct from.
123   @param scope_id The zone the address belongs to, as an 123   @param scope_id The zone the address belongs to, as an
124   interface index; 0 means unscoped. 124   interface index; 0 means unscoped.
125   */ 125   */
126   explicit ipv6_address( 126   explicit ipv6_address(
127   bytes_type const& bytes, std::uint32_t scope_id = 0) noexcept; 127   bytes_type const& bytes, std::uint32_t scope_id = 0) noexcept;
128   128  
129   /** Return the zone the address belongs to. 129   /** Return the zone the address belongs to.
130   130  
131   Link-local addresses (`fe80::/10`) are unique only per network link, 131   Link-local addresses (`fe80::/10`) are unique only per network link,
132   so the address bits alone do not identify a destination. The zone — 132   so the address bits alone do not identify a destination. The zone —
133   an interface index, written with a `%` suffix in text form — 133   an interface index, written with a `%` suffix in text form —
134   disambiguates. For global addresses the zone is 0 and has no 134   disambiguates. For global addresses the zone is 0 and has no
135   meaning. 135   meaning.
136   136  
137   @return The zone as an interface index; 0 if unscoped. 137   @return The zone as an interface index; 0 if unscoped.
138   138  
139   @par Specification 139   @par Specification
140   @li <a href="https://datatracker.ietf.org/doc/html/rfc4007" 140   @li <a href="https://datatracker.ietf.org/doc/html/rfc4007"
141   >IPv6 Scoped Address Architecture (rfc4007)</a> 141   >IPv6 Scoped Address Architecture (rfc4007)</a>
142   */ 142   */
HITCBC 143   177 std::uint32_t scope_id() const noexcept 143   177 std::uint32_t scope_id() const noexcept
144   { 144   {
HITCBC 145   177 return scope_id_; 145   177 return scope_id_;
146   } 146   }
147   147  
148   /** Construct from an IPv4 address. 148   /** Construct from an IPv4 address.
149   149  
150   This function constructs an IPv6 address 150   This function constructs an IPv6 address
151   from the IPv4 address `addr`. The resulting 151   from the IPv4 address `addr`. The resulting
152   address is an IPv4-Mapped IPv6 Address. 152   address is an IPv4-Mapped IPv6 Address.
153   153  
154   @param addr The address to construct from. 154   @param addr The address to construct from.
155   155  
156   @par Specification 156   @par Specification
157   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2" 157   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2"
158   >2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a> 158   >2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a>
159   */ 159   */
160   explicit ipv6_address(ipv4_address const& addr) noexcept; 160   explicit ipv6_address(ipv4_address const& addr) noexcept;
161 - /** Construct from a string.  
162 -  
163 - This function constructs an address from  
164 - the string `s`, which must contain a valid  
165 - IPv6 address string or else an exception  
166 - is thrown.  
167 -  
168 - @par Exception Safety  
169 - Strong guarantee.  
170 -  
171 - @throws std::system_error `errc::invalid_argument` if the input  
172 - failed to parse correctly.  
173 -  
174 - @note For a non-throwing parse function,  
175 - use @ref make_ipv6_address.  
176 -  
177 - @param s The string to parse.  
178 -  
179 - @par Specification  
180 - @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"  
181 - >3.2.2. Host (rfc3986)</a>  
182 -  
183 - @see  
184 - @ref make_ipv6_address  
185 - */  
186 - explicit ipv6_address(std::string_view s);  
187 -  
188   161  
189   /** Return the address as bytes, in network byte order. 162   /** Return the address as bytes, in network byte order.
190   163  
191   The 16 bytes cannot carry the zone: for a scoped address 164   The 16 bytes cannot carry the zone: for a scoped address
192   the result identifies the value only together with 165   the result identifies the value only together with
193   @ref scope_id. 166   @ref scope_id.
194   167  
195   @return The address as an array of bytes. 168   @return The address as an array of bytes.
196   */ 169   */
HITCBC 197   261 bytes_type to_bytes() const noexcept 170   261 bytes_type to_bytes() const noexcept
198   { 171   {
HITCBC 199   261 return addr_; 172   261 return addr_;
200   } 173   }
201   174  
202   /** Return the address as a string. 175   /** Return the address as a string.
203   176  
204   The returned string does not 177   The returned string does not
205   contain surrounding square brackets. 178   contain surrounding square brackets.
206   179  
207   @par Example 180   @par Example
208   @par !example to_string 181   @par !example to_string
209   182  
210   @return The address as a string. 183   @return The address as a string.
211   184  
212   @par Specification 185   @par Specification
213   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.2"> 186   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.2">
214   2.2. Text Representation of Addresses (rfc4291)</a> 187   2.2. Text Representation of Addresses (rfc4291)</a>
215   */ 188   */
216   std::string to_string() const; 189   std::string to_string() const;
217   190  
218   /** Write a string representing the address to a buffer. 191   /** Write a string representing the address to a buffer.
219   192  
220   The resulting buffer is not null-terminated. 193   The resulting buffer is not null-terminated.
221   194  
222   @throws std::length_error `dest_size < ipv6_address::max_str_len` 195   @throws std::length_error `dest_size < ipv6_address::max_str_len`
223   196  
224   @param dest The buffer in which to write, 197   @param dest The buffer in which to write,
225   which must have at least `dest_size` space. 198   which must have at least `dest_size` space.
226   199  
227   @param dest_size The size of the output buffer. 200   @param dest_size The size of the output buffer.
228   201  
229   @return The formatted string view. 202   @return The formatted string view.
230   */ 203   */
231   std::string_view to_buffer(char* dest, std::size_t dest_size) const; 204   std::string_view to_buffer(char* dest, std::size_t dest_size) const;
232   205  
233   /** Return true if the address is unspecified. 206   /** Return true if the address is unspecified.
234   207  
235   The address 0:0:0:0:0:0:0:0 is called the 208   The address 0:0:0:0:0:0:0:0 is called the
236   unspecified address. It indicates the 209   unspecified address. It indicates the
237   absence of an address. 210   absence of an address.
238   211  
239   @return `true` if the address is unspecified. 212   @return `true` if the address is unspecified.
240   213  
241   @par Specification 214   @par Specification
242   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.2"> 215   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.2">
243   2.5.2. The Unspecified Address (rfc4291)</a> 216   2.5.2. The Unspecified Address (rfc4291)</a>
244   */ 217   */
245   bool is_unspecified() const noexcept; 218   bool is_unspecified() const noexcept;
246   219  
247   /** Return true if the address is a loopback address. 220   /** Return true if the address is a loopback address.
248   221  
249   The unicast address 0:0:0:0:0:0:0:1 is called 222   The unicast address 0:0:0:0:0:0:0:1 is called
250   the loopback address. It may be used by a node 223   the loopback address. It may be used by a node
251   to send an IPv6 packet to itself. 224   to send an IPv6 packet to itself.
252   225  
253   @return `true` if the address is a loopback address. 226   @return `true` if the address is a loopback address.
254   227  
255   @par Specification 228   @par Specification
256   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.3"> 229   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.3">
257   2.5.3. The Loopback Address (rfc4291)</a> 230   2.5.3. The Loopback Address (rfc4291)</a>
258   */ 231   */
259   bool is_loopback() const noexcept; 232   bool is_loopback() const noexcept;
260   233  
261   /** Return true if the address is a mapped IPv4 address. 234   /** Return true if the address is a mapped IPv4 address.
262   235  
263   This address type is used to represent the 236   This address type is used to represent the
264   addresses of IPv4 nodes as IPv6 addresses. 237   addresses of IPv4 nodes as IPv6 addresses.
265   238  
266   @return `true` if the address is a mapped IPv4 address. 239   @return `true` if the address is a mapped IPv4 address.
267   240  
268   @par Specification 241   @par Specification
269   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2"> 242   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2">
270   2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a> 243   2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a>
271   */ 244   */
272   bool is_v4_mapped() const noexcept; 245   bool is_v4_mapped() const noexcept;
273   246  
274   /** Convert a v4-mapped address to the IPv4 address it maps. 247   /** Convert a v4-mapped address to the IPv4 address it maps.
275   248  
276   This is the inverse of the mapping constructor 249   This is the inverse of the mapping constructor
277   `ipv6_address(ipv4_address const&)`: it extracts the low 250   `ipv6_address(ipv4_address const&)`: it extracts the low
278   32 bits of an IPv4-Mapped IPv6 Address (`::ffff:a.b.c.d`) 251   32 bits of an IPv4-Mapped IPv6 Address (`::ffff:a.b.c.d`)
279   as an `ipv4_address`. 252   as an `ipv4_address`.
280   253  
281   @throws std::system_error `errc::address_family_not_supported` 254   @throws std::system_error `errc::address_family_not_supported`
282   if the address is not v4-mapped. 255   if the address is not v4-mapped.
283   256  
284   @return The mapped IPv4 address. 257   @return The mapped IPv4 address.
285   258  
286   @par Specification 259   @par Specification
287   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2"> 260   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2">
288   2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a> 261   2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a>
289   262  
290   @see 263   @see
291   @ref is_v4_mapped 264   @ref is_v4_mapped
292   */ 265   */
293   ipv4_address to_v4() const; 266   ipv4_address to_v4() const;
294   267  
295   /** Return true if the address is a multicast address. 268   /** Return true if the address is a multicast address.
296   269  
297   IPv6 multicast addresses have the prefix ff00::/8. 270   IPv6 multicast addresses have the prefix ff00::/8.
298   271  
299   @return `true` if the address is a multicast address. 272   @return `true` if the address is a multicast address.
300   273  
301   @par Specification 274   @par Specification
302   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.7"> 275   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.7">
303   2.7. Multicast Addresses (rfc4291)</a> 276   2.7. Multicast Addresses (rfc4291)</a>
304   */ 277   */
305   bool is_multicast() const noexcept; 278   bool is_multicast() const noexcept;
306   279  
307   /** Return true if two addresses are equal. 280   /** Return true if two addresses are equal.
308   281  
309   Addresses are equal if they have the same bytes and the 282   Addresses are equal if they have the same bytes and the
310   same zone: the same link-local bits on different links are 283   same zone: the same link-local bits on different links are
311   different destinations. 284   different destinations.
312   285  
313   @return `true` if the addresses are equal. 286   @return `true` if the addresses are equal.
314   */ 287   */
315   friend bool 288   friend bool
HITCBC 316   50 operator==(ipv6_address const& a1, ipv6_address const& a2) noexcept 289   52 operator==(ipv6_address const& a1, ipv6_address const& a2) noexcept
317   { 290   {
HITCBC 318   50 return a1.addr_ == a2.addr_ && a1.scope_id_ == a2.scope_id_; 291   52 return a1.addr_ == a2.addr_ && a1.scope_id_ == a2.scope_id_;
319   } 292   }
320   293  
321   /** Order two addresses. 294   /** Order two addresses.
322   295  
323   Establishes a strict total ordering consistent with 296   Establishes a strict total ordering consistent with
324   `operator==`: addresses are ordered lexicographically by 297   `operator==`: addresses are ordered lexicographically by
325   their bytes in network order, then by zone. This makes 298   their bytes in network order, then by zone. This makes
326   `ipv6_address` usable as a key in ordered containers such 299   `ipv6_address` usable as a key in ordered containers such
327   as `std::map` and `std::set`. 300   as `std::map` and `std::set`.
328   301  
329   @return The relative order of `a1` and `a2`. 302   @return The relative order of `a1` and `a2`.
330   */ 303   */
331   friend std::strong_ordering 304   friend std::strong_ordering
HITCBC 332   17 operator<=>(ipv6_address const& a1, ipv6_address const& a2) noexcept 305   17 operator<=>(ipv6_address const& a1, ipv6_address const& a2) noexcept
333   { 306   {
HITCBC 334   17 if (auto c = a1.addr_ <=> a2.addr_; c != 0) 307   17 if (auto c = a1.addr_ <=> a2.addr_; c != 0)
HITCBC 335   6 return c; 308   6 return c;
HITCBC 336   11 return a1.scope_id_ <=> a2.scope_id_; 309   11 return a1.scope_id_ <=> a2.scope_id_;
337   } 310   }
338   311  
339   /** Return an address object that represents the unspecified address. 312   /** Return an address object that represents the unspecified address.
340   313  
341   The address 0:0:0:0:0:0:0:0 (::) may be used to bind a socket 314   The address 0:0:0:0:0:0:0:0 (::) may be used to bind a socket
342   to all available interfaces. 315   to all available interfaces.
343   316  
344   @return The unspecified address (::). 317   @return The unspecified address (::).
345   */ 318   */
HITCBC 346   19 static ipv6_address any() noexcept 319   19 static ipv6_address any() noexcept
347   { 320   {
HITCBC 348   19 return ipv6_address(); 321   19 return ipv6_address();
349   } 322   }
350   323  
351   /** Return an address object that represents the loopback address. 324   /** Return an address object that represents the loopback address.
352   325  
353   The unicast address 0:0:0:0:0:0:0:1 is called 326   The unicast address 0:0:0:0:0:0:0:1 is called
354   the loopback address. It may be used by a node 327   the loopback address. It may be used by a node
355   to send an IPv6 packet to itself. 328   to send an IPv6 packet to itself.
356   329  
357   @par Specification 330   @par Specification
358   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.3"> 331   @li <a href="https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.3">
359   2.5.3. The Loopback Address (rfc4291)</a> 332   2.5.3. The Loopback Address (rfc4291)</a>
360   333  
361   @return The loopback address (::1). 334   @return The loopback address (::1).
362   */ 335   */
363   static ipv6_address loopback() noexcept; 336   static ipv6_address loopback() noexcept;
364   337  
365   /** Format the address to an output stream. 338   /** Format the address to an output stream.
366   339  
367   This function writes the address to an 340   This function writes the address to an
368   output stream using standard notation. 341   output stream using standard notation.
369   342  
370   @return The output stream, for chaining. 343   @return The output stream, for chaining.
371   344  
372   @param os The output stream to write to. 345   @param os The output stream to write to.
373   346  
374   @param addr The address to write. 347   @param addr The address to write.
375   */ 348   */
376   friend BOOST_COROSIO_DECL std::ostream& 349   friend BOOST_COROSIO_DECL std::ostream&
377   operator<<(std::ostream& os, ipv6_address const& addr); 350   operator<<(std::ostream& os, ipv6_address const& addr);
378   351  
379   private: 352   private:
380   std::size_t print_impl(char* dest) const noexcept; 353   std::size_t print_impl(char* dest) const noexcept;
381   }; 354   };
382   355  
383   /** Create an IPv6 address from a string. 356   /** Create an IPv6 address from a string.
384   357  
385   This function attempts to parse the string as an IPv6 address. It 358   This function attempts to parse the string as an IPv6 address. It
386   returns an error code if the string holds no valid IPv6 address. 359   returns an error code if the string holds no valid IPv6 address.
387   360  
388   @par Exception Safety 361   @par Exception Safety
389   Throws nothing. 362   Throws nothing.
  363 +
  364 + @par Specification
  365 + @li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
  366 + >3.2.2. Host (rfc3986)</a>
390   367  
391   @param s The string to parse. 368   @param s The string to parse.
392   @return The error code, empty on success, and the parsed 369   @return The error code, empty on success, and the parsed
393   address — default-constructed on failure. 370   address — default-constructed on failure.
394   */ 371   */
395   [[nodiscard]] BOOST_COROSIO_DECL capy::results<ipv6_address> 372   [[nodiscard]] BOOST_COROSIO_DECL capy::results<ipv6_address>
396   make_ipv6_address(std::string_view s) noexcept; 373   make_ipv6_address(std::string_view s) noexcept;
397   374  
398   } // namespace boost::corosio 375   } // namespace boost::corosio
399   376  
400   namespace std { 377   namespace std {
401   378  
402   /// Hash support for `boost::corosio::ipv6_address`. 379   /// Hash support for `boost::corosio::ipv6_address`.
403   template<> 380   template<>
404   struct hash<boost::corosio::ipv6_address> 381   struct hash<boost::corosio::ipv6_address>
405   { 382   {
406   /// Return the hash of `addr`. 383   /// Return the hash of `addr`.
407   std::size_t 384   std::size_t
HITCBC 408   21 operator()(boost::corosio::ipv6_address const& addr) const noexcept 385   21 operator()(boost::corosio::ipv6_address const& addr) const noexcept
409   { 386   {
HITCBC 410   21 auto const bytes = addr.to_bytes(); 387   21 auto const bytes = addr.to_bytes();
HITCBC 411   21 auto const h = hash<std::string_view>()(std::string_view( 388   21 auto const h = hash<std::string_view>()(std::string_view(
HITCBC 412   21 reinterpret_cast<char const*>(bytes.data()), bytes.size())); 389   21 reinterpret_cast<char const*>(bytes.data()), bytes.size()));
413   // The zone participates in equality, so it must feed the 390   // The zone participates in equality, so it must feed the
414   // hash; combine so it cannot cancel the byte entropy 391   // hash; combine so it cannot cancel the byte entropy
HITCBC 415   21 auto const z = hash<std::uint32_t>()(addr.scope_id()); 392   21 auto const z = hash<std::uint32_t>()(addr.scope_id());
HITCBC 416   21 return h ^ (z + 0x9e3779b9u + (h << 6) + (h >> 2)); 393   21 return h ^ (z + 0x9e3779b9u + (h << 6) + (h >> 2));
417   } 394   }
418   }; 395   };
419   396  
420   } // namespace std 397   } // namespace std
421   398  
422   #endif 399   #endif