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 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   160314 ipv6_address() = default; 104   160763 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   161  
162   /** Construct from a string. 162   /** Construct from a string.
163   163  
164   This function constructs an address from 164   This function constructs an address from
165   the string `s`, which must contain a valid 165   the string `s`, which must contain a valid
166   IPv6 address string or else an exception 166   IPv6 address string or else an exception
167   is thrown. 167   is thrown.
168   168  
169   @par Exception Safety 169   @par Exception Safety
170   Strong guarantee. 170   Strong guarantee.
171   171  
172   @throws std::system_error `errc::invalid_argument` if the input 172   @throws std::system_error `errc::invalid_argument` if the input
173   failed to parse correctly. 173   failed to parse correctly.
174   174  
175   @note For a non-throwing parse function, 175   @note For a non-throwing parse function,
176   use @ref make_ipv6_address. 176   use @ref make_ipv6_address.
177   177  
178   @param s The string to parse. 178   @param s The string to parse.
179   179  
180   @par Specification 180   @par Specification
181   @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"
182   >3.2.2. Host (rfc3986)</a> 182   >3.2.2. Host (rfc3986)</a>
183   183  
184   @see 184   @see
185   @ref make_ipv6_address. 185   @ref make_ipv6_address.
186   */ 186   */
187   explicit ipv6_address(std::string_view s); 187   explicit ipv6_address(std::string_view s);
188   188  
189   /** Return the address as bytes, in network byte order. 189   /** Return the address as bytes, in network byte order.
190   190  
191   The 16 bytes cannot carry the zone: for a scoped address 191   The 16 bytes cannot carry the zone: for a scoped address
192   the result identifies the value only together with 192   the result identifies the value only together with
193   @ref scope_id. 193   @ref scope_id.
194   194  
195   @return The address as an array of bytes. 195   @return The address as an array of bytes.
196   */ 196   */
HITCBC 197   261 bytes_type to_bytes() const noexcept 197   261 bytes_type to_bytes() const noexcept
198   { 198   {
HITCBC 199   261 return addr_; 199   261 return addr_;
200   } 200   }
201   201  
202   /** Return the address as a string. 202   /** Return the address as a string.
203   203  
204   The returned string does not 204   The returned string does not
205   contain surrounding square brackets. 205   contain surrounding square brackets.
206   206  
207   @par Example 207   @par Example
208   @par !example to_string 208   @par !example to_string
209   209  
210   @return The address as a string. 210   @return The address as a string.
211   211  
212   @par Specification 212   @par Specification
213   @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">
214   2.2. Text Representation of Addresses (rfc4291)</a> 214   2.2. Text Representation of Addresses (rfc4291)</a>
215   */ 215   */
216   std::string to_string() const; 216   std::string to_string() const;
217   217  
218   /** Write a string representing the address to a buffer. 218   /** Write a string representing the address to a buffer.
219   219  
220   The resulting buffer is not null-terminated. 220   The resulting buffer is not null-terminated.
221   221  
222   @throws std::length_error `dest_size < ipv6_address::max_str_len` 222   @throws std::length_error `dest_size < ipv6_address::max_str_len`
223   223  
224   @param dest The buffer in which to write, 224   @param dest The buffer in which to write,
225   which must have at least `dest_size` space. 225   which must have at least `dest_size` space.
226   226  
227   @param dest_size The size of the output buffer. 227   @param dest_size The size of the output buffer.
228   228  
229   @return The formatted string view. 229   @return The formatted string view.
230   */ 230   */
231   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;
232   232  
233   /** Return true if the address is unspecified. 233   /** Return true if the address is unspecified.
234   234  
235   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
236   unspecified address. It indicates the 236   unspecified address. It indicates the
237   absence of an address. 237   absence of an address.
238   238  
239   @return `true` if the address is unspecified. 239   @return `true` if the address is unspecified.
240   240  
241   @par Specification 241   @par Specification
242   @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">
243   2.5.2. The Unspecified Address (rfc4291)</a> 243   2.5.2. The Unspecified Address (rfc4291)</a>
244   */ 244   */
245   bool is_unspecified() const noexcept; 245   bool is_unspecified() const noexcept;
246   246  
247   /** Return true if the address is a loopback address. 247   /** Return true if the address is a loopback address.
248   248  
249   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
250   the loopback address. It may be used by a node 250   the loopback address. It may be used by a node
251   to send an IPv6 packet to itself. 251   to send an IPv6 packet to itself.
252   252  
253   @return `true` if the address is a loopback address. 253   @return `true` if the address is a loopback address.
254   254  
255   @par Specification 255   @par Specification
256   @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">
257   2.5.3. The Loopback Address (rfc4291)</a> 257   2.5.3. The Loopback Address (rfc4291)</a>
258   */ 258   */
259   bool is_loopback() const noexcept; 259   bool is_loopback() const noexcept;
260   260  
261   /** Return true if the address is a mapped IPv4 address. 261   /** Return true if the address is a mapped IPv4 address.
262   262  
263   This address type is used to represent the 263   This address type is used to represent the
264   addresses of IPv4 nodes as IPv6 addresses. 264   addresses of IPv4 nodes as IPv6 addresses.
265   265  
266   @return `true` if the address is a mapped IPv4 address. 266   @return `true` if the address is a mapped IPv4 address.
267   267  
268   @par Specification 268   @par Specification
269   @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">
270   2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a> 270   2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a>
271   */ 271   */
272   bool is_v4_mapped() const noexcept; 272   bool is_v4_mapped() const noexcept;
273   273  
274   /** Convert a v4-mapped address to the IPv4 address it maps. 274   /** Convert a v4-mapped address to the IPv4 address it maps.
275   275  
276   This is the inverse of the mapping constructor 276   This is the inverse of the mapping constructor
277   `ipv6_address(ipv4_address const&)`: it extracts the low 277   `ipv6_address(ipv4_address const&)`: it extracts the low
278   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`)
279   as an `ipv4_address`. 279   as an `ipv4_address`.
280   280  
281   @throws std::system_error `errc::address_family_not_supported` 281   @throws std::system_error `errc::address_family_not_supported`
282   if the address is not v4-mapped. 282   if the address is not v4-mapped.
283   283  
284   @return The mapped IPv4 address. 284   @return The mapped IPv4 address.
285   285  
286   @par Specification 286   @par Specification
287   @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">
288   2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a> 288   2.5.5.2. IPv4-Mapped IPv6 Address (rfc4291)</a>
289   289  
290   @see 290   @see
291   @ref is_v4_mapped. 291   @ref is_v4_mapped.
292   */ 292   */
293   ipv4_address to_v4() const; 293   ipv4_address to_v4() const;
294   294  
295   /** Return true if the address is a multicast address. 295   /** Return true if the address is a multicast address.
296   296  
297   IPv6 multicast addresses have the prefix ff00::/8. 297   IPv6 multicast addresses have the prefix ff00::/8.
298   298  
299   @return `true` if the address is a multicast address. 299   @return `true` if the address is a multicast address.
300   300  
301   @par Specification 301   @par Specification
302   @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">
303   2.7. Multicast Addresses (rfc4291)</a> 303   2.7. Multicast Addresses (rfc4291)</a>
304   */ 304   */
305   bool is_multicast() const noexcept; 305   bool is_multicast() const noexcept;
306   306  
307   /** Return true if two addresses are equal. 307   /** Return true if two addresses are equal.
308   308  
309   Addresses are equal if they have the same bytes and the 309   Addresses are equal if they have the same bytes and the
310   same zone: the same link-local bits on different links are 310   same zone: the same link-local bits on different links are
311   different destinations. 311   different destinations.
312   312  
313   @return `true` if the addresses are equal. 313   @return `true` if the addresses are equal.
314   */ 314   */
315   friend bool 315   friend bool
HITCBC 316   50 operator==(ipv6_address const& a1, ipv6_address const& a2) noexcept 316   50 operator==(ipv6_address const& a1, ipv6_address const& a2) noexcept
317   { 317   {
HITCBC 318   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_;
319   } 319   }
320   320  
321   /** Order two addresses. 321   /** Order two addresses.
322   322  
323   Establishes a strict total ordering consistent with 323   Establishes a strict total ordering consistent with
324   `operator==`: addresses are ordered lexicographically by 324   `operator==`: addresses are ordered lexicographically by
325   their bytes in network order, then by zone. This makes 325   their bytes in network order, then by zone. This makes
326   `ipv6_address` usable as a key in ordered containers such 326   `ipv6_address` usable as a key in ordered containers such
327   as `std::map` and `std::set`. 327   as `std::map` and `std::set`.
328   328  
329   @return The relative order of `a1` and `a2`. 329   @return The relative order of `a1` and `a2`.
330   */ 330   */
331   friend std::strong_ordering 331   friend std::strong_ordering
HITCBC 332   17 operator<=>(ipv6_address const& a1, ipv6_address const& a2) noexcept 332   17 operator<=>(ipv6_address const& a1, ipv6_address const& a2) noexcept
333   { 333   {
HITCBC 334   17 if (auto c = a1.addr_ <=> a2.addr_; c != 0) 334   17 if (auto c = a1.addr_ <=> a2.addr_; c != 0)
HITCBC 335   6 return c; 335   6 return c;
HITCBC 336   11 return a1.scope_id_ <=> a2.scope_id_; 336   11 return a1.scope_id_ <=> a2.scope_id_;
337   } 337   }
338   338  
339   /** Return an address object that represents the unspecified address. 339   /** Return an address object that represents the unspecified address.
340   340  
341   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
342   to all available interfaces. 342   to all available interfaces.
343   343  
344   @return The unspecified address (::). 344   @return The unspecified address (::).
345   */ 345   */
HITCBC 346   19 static ipv6_address any() noexcept 346   19 static ipv6_address any() noexcept
347   { 347   {
HITCBC 348   19 return ipv6_address(); 348   19 return ipv6_address();
349   } 349   }
350   350  
351   /** Return an address object that represents the loopback address. 351   /** Return an address object that represents the loopback address.
352   352  
353   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
354   the loopback address. It may be used by a node 354   the loopback address. It may be used by a node
355   to send an IPv6 packet to itself. 355   to send an IPv6 packet to itself.
356   356  
357   @par Specification 357   @par Specification
358   @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">
359   2.5.3. The Loopback Address (rfc4291)</a> 359   2.5.3. The Loopback Address (rfc4291)</a>
360   360  
361   @return The loopback address (::1). 361   @return The loopback address (::1).
362   */ 362   */
363   static ipv6_address loopback() noexcept; 363   static ipv6_address loopback() noexcept;
364   364  
365   /** Format the address to an output stream. 365   /** Format the address to an output stream.
366   366  
367   This function writes the address to an 367   This function writes the address to an
368   output stream using standard notation. 368   output stream using standard notation.
369   369  
370   @return The output stream, for chaining. 370   @return The output stream, for chaining.
371   371  
372   @param os The output stream to write to. 372   @param os The output stream to write to.
373   373  
374   @param addr The address to write. 374   @param addr The address to write.
375   */ 375   */
376   friend BOOST_COROSIO_DECL std::ostream& 376   friend BOOST_COROSIO_DECL std::ostream&
377   operator<<(std::ostream& os, ipv6_address const& addr); 377   operator<<(std::ostream& os, ipv6_address const& addr);
378   378  
379   private: 379   private:
380   std::size_t print_impl(char* dest) const noexcept; 380   std::size_t print_impl(char* dest) const noexcept;
381   }; 381   };
382   382  
383   /** Create an IPv6 address from a string. 383   /** Create an IPv6 address from a string.
384   384  
385   This function attempts to parse the string as an IPv6 address. It 385   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. 386   returns an error code if the string holds no valid IPv6 address.
387   387  
388   @par Exception Safety 388   @par Exception Safety
389   Throws nothing. 389   Throws nothing.
390   390  
391   @param s The string to parse. 391   @param s The string to parse.
392   @return The error code, empty on success, and the parsed 392   @return The error code, empty on success, and the parsed
393   address — default-constructed on failure. 393   address — default-constructed on failure.
394   */ 394   */
395   [[nodiscard]] BOOST_COROSIO_DECL capy::io_result<ipv6_address> 395   [[nodiscard]] BOOST_COROSIO_DECL capy::io_result<ipv6_address>
396   make_ipv6_address(std::string_view s) noexcept; 396   make_ipv6_address(std::string_view s) noexcept;
397   397  
398   } // namespace boost::corosio 398   } // namespace boost::corosio
399   399  
400   namespace std { 400   namespace std {
401   401  
402   /// Hash support for `boost::corosio::ipv6_address`. 402   /// Hash support for `boost::corosio::ipv6_address`.
403   template<> 403   template<>
404   struct hash<boost::corosio::ipv6_address> 404   struct hash<boost::corosio::ipv6_address>
405   { 405   {
406   /// Return the hash of `addr`. 406   /// Return the hash of `addr`.
407   std::size_t 407   std::size_t
HITCBC 408   21 operator()(boost::corosio::ipv6_address const& addr) const noexcept 408   21 operator()(boost::corosio::ipv6_address const& addr) const noexcept
409   { 409   {
HITCBC 410   21 auto const bytes = addr.to_bytes(); 410   21 auto const bytes = addr.to_bytes();
HITCBC 411   21 auto const h = hash<std::string_view>()(std::string_view( 411   21 auto const h = hash<std::string_view>()(std::string_view(
HITCBC 412   21 reinterpret_cast<char const*>(bytes.data()), bytes.size())); 412   21 reinterpret_cast<char const*>(bytes.data()), bytes.size()));
413   // The zone participates in equality, so it must feed the 413   // The zone participates in equality, so it must feed the
414   // hash; combine so it cannot cancel the byte entropy 414   // hash; combine so it cannot cancel the byte entropy
HITCBC 415   21 auto const z = hash<std::uint32_t>()(addr.scope_id()); 415   21 auto const z = hash<std::uint32_t>()(addr.scope_id());
HITCBC 416   21 return h ^ (z + 0x9e3779b9u + (h << 6) + (h >> 2)); 416   21 return h ^ (z + 0x9e3779b9u + (h << 6) + (h >> 2));
417   } 417   }
418   }; 418   };
419   419  
420   } // namespace std 420   } // namespace std
421   421  
422   #endif 422   #endif