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