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