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