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