98.18% Lines (162/165) 100.00% Functions (78/78)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   /** @file native_socket_option.hpp 10   /** @file native_socket_option.hpp
11   11  
12   Inline socket option types using platform-specific constants. 12   Inline socket option types using platform-specific constants.
13   All methods are `constexpr` or trivially inlined, giving zero 13   All methods are `constexpr` or trivially inlined, giving zero
14   overhead compared to hand-written `setsockopt` calls. 14   overhead compared to hand-written `setsockopt` calls.
15   15  
16   This header includes platform socket headers 16   This header includes platform socket headers
17   (`<sys/socket.h>`, `<netinet/tcp.h>`, etc.). 17   (`<sys/socket.h>`, `<netinet/tcp.h>`, etc.).
18   For a version that avoids platform includes, use 18   For a version that avoids platform includes, use
19   `<boost/corosio/socket_option.hpp>` 19   `<boost/corosio/socket_option.hpp>`
20   (`boost::corosio::socket_option`). 20   (`boost::corosio::socket_option`).
21   21  
22   Both variants satisfy the same option-type interface and work 22   Both variants satisfy the same option-type interface and work
23   interchangeably with `tcp_socket::set_option` / 23   interchangeably with `tcp_socket::set_option` /
24   `tcp_socket::get_option` and the corresponding acceptor methods. 24   `tcp_socket::get_option` and the corresponding acceptor methods.
25   25  
26   @see boost::corosio::socket_option 26   @see boost::corosio::socket_option
27   */ 27   */
28   28  
29   #ifndef BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP 29   #ifndef BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
30   #define BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP 30   #define BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP
31   31  
32   #ifdef _WIN32 32   #ifdef _WIN32
33   #include <winsock2.h> 33   #include <winsock2.h>
34   #include <ws2tcpip.h> 34   #include <ws2tcpip.h>
35   #else 35   #else
36   #include <netinet/in.h> 36   #include <netinet/in.h>
37   #include <netinet/tcp.h> 37   #include <netinet/tcp.h>
38   #include <sys/socket.h> 38   #include <sys/socket.h>
39   #endif 39   #endif
40   40  
41   // Some older systems define only the legacy names 41   // Some older systems define only the legacy names
42   #ifndef IPV6_JOIN_GROUP 42   #ifndef IPV6_JOIN_GROUP
43   #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP 43   #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP
44   #endif 44   #endif
45   #ifndef IPV6_LEAVE_GROUP 45   #ifndef IPV6_LEAVE_GROUP
46   #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP 46   #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP
47   #endif 47   #endif
48   48  
49   #include <boost/corosio/ipv4_address.hpp> 49   #include <boost/corosio/ipv4_address.hpp>
50   #include <boost/corosio/ipv6_address.hpp> 50   #include <boost/corosio/ipv6_address.hpp>
51   51  
52   #include <cstddef> 52   #include <cstddef>
53   #include <cstring> 53   #include <cstring>
54   54  
55   namespace boost::corosio::native_socket_option { 55   namespace boost::corosio::native_socket_option {
56   56  
57   /** A socket option with a boolean value. 57   /** A socket option with a boolean value.
58   58  
59   Models socket options whose underlying representation is an `int` 59   Models socket options whose underlying representation is an `int`
60   where 0 means disabled and non-zero means enabled. The option's 60   where 0 means disabled and non-zero means enabled. The option's
61   protocol level and name are encoded as template parameters. 61   protocol level and name are encoded as template parameters.
62   62  
63   This is the native (inline) variant that includes platform 63   This is the native (inline) variant that includes platform
64   headers. For a type-erased version that avoids platform 64   headers. For a type-erased version that avoids platform
65   includes, use `boost::corosio::socket_option` instead. 65   includes, use `boost::corosio::socket_option` instead.
66   66  
67   @par Example 67   @par Example
68   @code 68   @code
69   sock.set_option( native_socket_option::no_delay( true ) ); 69   sock.set_option( native_socket_option::no_delay( true ) );
70   auto nd = sock.get_option<native_socket_option::no_delay>(); 70   auto nd = sock.get_option<native_socket_option::no_delay>();
71   if ( nd.value() ) 71   if ( nd.value() )
72   // Nagle's algorithm is disabled 72   // Nagle's algorithm is disabled
73   @endcode 73   @endcode
74   74  
75   @tparam Level The protocol level (e.g. `SOL_SOCKET`, `IPPROTO_TCP`). 75   @tparam Level The protocol level (e.g. `SOL_SOCKET`, `IPPROTO_TCP`).
76   @tparam Name The option name (e.g. `TCP_NODELAY`, `SO_KEEPALIVE`). 76   @tparam Name The option name (e.g. `TCP_NODELAY`, `SO_KEEPALIVE`).
77   */ 77   */
78   template<int Level, int Name> 78   template<int Level, int Name>
79   class boolean 79   class boolean
80   { 80   {
81   int value_ = 0; 81   int value_ = 0;
82   82  
83   public: 83   public:
84   /// Construct with default value (disabled). 84   /// Construct with default value (disabled).
85   boolean() = default; 85   boolean() = default;
86   86  
87   /** Construct with an explicit value. 87   /** Construct with an explicit value.
88   88  
89   @param v `true` to enable the option, `false` to disable. 89   @param v `true` to enable the option, `false` to disable.
90   */ 90   */
HITCBC 91   22 explicit boolean(bool v) noexcept : value_(v ? 1 : 0) {} 91   22 explicit boolean(bool v) noexcept : value_(v ? 1 : 0) {}
92   92  
93   /// Assign a new value. 93   /// Assign a new value.
94   boolean& operator=(bool v) noexcept 94   boolean& operator=(bool v) noexcept
95   { 95   {
96   value_ = v ? 1 : 0; 96   value_ = v ? 1 : 0;
97   return *this; 97   return *this;
98   } 98   }
99   99  
100   /// Return the option value. 100   /// Return the option value.
HITCBC 101   10 bool value() const noexcept 101   10 bool value() const noexcept
102   { 102   {
HITCBC 103   10 return value_ != 0; 103   10 return value_ != 0;
104   } 104   }
105   105  
106   /// Return the option value. 106   /// Return the option value.
107   explicit operator bool() const noexcept 107   explicit operator bool() const noexcept
108   { 108   {
109   return value_ != 0; 109   return value_ != 0;
110   } 110   }
111   111  
112   /// Return the negated option value. 112   /// Return the negated option value.
113   bool operator!() const noexcept 113   bool operator!() const noexcept
114   { 114   {
115   return value_ == 0; 115   return value_ == 0;
116   } 116   }
117   117  
118   /// Return the protocol level for `setsockopt`/`getsockopt`. 118   /// Return the protocol level for `setsockopt`/`getsockopt`.
HITCBC 119   335 static constexpr int level() noexcept 119   335 static constexpr int level() noexcept
120   { 120   {
HITCBC 121   335 return Level; 121   335 return Level;
122   } 122   }
123   123  
124   /// Return the option name for `setsockopt`/`getsockopt`. 124   /// Return the option name for `setsockopt`/`getsockopt`.
HITCBC 125   335 static constexpr int name() noexcept 125   335 static constexpr int name() noexcept
126   { 126   {
HITCBC 127   335 return Name; 127   335 return Name;
128   } 128   }
129   129  
130   /// Return a pointer to the underlying storage. 130   /// Return a pointer to the underlying storage.
HITCBC 131   10 void* data() noexcept 131   10 void* data() noexcept
132   { 132   {
HITCBC 133   10 return &value_; 133   10 return &value_;
134   } 134   }
135   135  
136   /// Return a pointer to the underlying storage. 136   /// Return a pointer to the underlying storage.
HITCBC 137   22 void const* data() const noexcept 137   22 void const* data() const noexcept
138   { 138   {
HITCBC 139   22 return &value_; 139   22 return &value_;
140   } 140   }
141   141  
142   /// Return the size of the underlying storage. 142   /// Return the size of the underlying storage.
HITCBC 143   30 std::size_t size() const noexcept 143   30 std::size_t size() const noexcept
144   { 144   {
HITCBC 145   30 return sizeof(value_); 145   30 return sizeof(value_);
146   } 146   }
147   147  
148   /** Normalize after `getsockopt` returns fewer bytes than expected. 148   /** Normalize after `getsockopt` returns fewer bytes than expected.
149   149  
150   Windows Vista+ may write only 1 byte for boolean options. 150   Windows Vista+ may write only 1 byte for boolean options.
151   151  
152   @param s The number of bytes actually written by `getsockopt`. 152   @param s The number of bytes actually written by `getsockopt`.
153   */ 153   */
HITCBC 154   8 void resize(std::size_t s) noexcept 154   8 void resize(std::size_t s) noexcept
155   { 155   {
HITCBC 156   8 if (s == sizeof(char)) 156   8 if (s == sizeof(char))
MISUBC 157   value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0; 157   value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
HITCBC 158   8 } 158   8 }
159   }; 159   };
160   160  
161   /** A socket option with an integer value. 161   /** A socket option with an integer value.
162   162  
163   Models socket options whose underlying representation is a 163   Models socket options whose underlying representation is a
164   plain `int`. The option's protocol level and name are encoded 164   plain `int`. The option's protocol level and name are encoded
165   as template parameters. 165   as template parameters.
166   166  
167   This is the native (inline) variant that includes platform 167   This is the native (inline) variant that includes platform
168   headers. For a type-erased version that avoids platform 168   headers. For a type-erased version that avoids platform
169   includes, use `boost::corosio::socket_option` instead. 169   includes, use `boost::corosio::socket_option` instead.
170   170  
171   @par Example 171   @par Example
172   @code 172   @code
173   sock.set_option( native_socket_option::receive_buffer_size( 65536 ) ); 173   sock.set_option( native_socket_option::receive_buffer_size( 65536 ) );
174   auto opt = sock.get_option<native_socket_option::receive_buffer_size>(); 174   auto opt = sock.get_option<native_socket_option::receive_buffer_size>();
175   int sz = opt.value(); 175   int sz = opt.value();
176   @endcode 176   @endcode
177   177  
178   @tparam Level The protocol level (e.g. `SOL_SOCKET`). 178   @tparam Level The protocol level (e.g. `SOL_SOCKET`).
179   @tparam Name The option name (e.g. `SO_RCVBUF`). 179   @tparam Name The option name (e.g. `SO_RCVBUF`).
180   */ 180   */
181   template<int Level, int Name> 181   template<int Level, int Name>
182   class integer 182   class integer
183   { 183   {
184   int value_ = 0; 184   int value_ = 0;
185   185  
186   public: 186   public:
187   /// Construct with default value (zero). 187   /// Construct with default value (zero).
188   integer() = default; 188   integer() = default;
189   189  
190   /** Construct with an explicit value. 190   /** Construct with an explicit value.
191   191  
192   @param v The option value. 192   @param v The option value.
193   */ 193   */
HITCBC 194   8 explicit integer(int v) noexcept : value_(v) {} 194   8 explicit integer(int v) noexcept : value_(v) {}
195   195  
196   /// Assign a new value. 196   /// Assign a new value.
197   integer& operator=(int v) noexcept 197   integer& operator=(int v) noexcept
198   { 198   {
199   value_ = v; 199   value_ = v;
200   return *this; 200   return *this;
201   } 201   }
202   202  
203   /// Return the option value. 203   /// Return the option value.
HITCBC 204   6 int value() const noexcept 204   6 int value() const noexcept
205   { 205   {
HITCBC 206   6 return value_; 206   6 return value_;
207   } 207   }
208   208  
209   /// Return the protocol level for `setsockopt`/`getsockopt`. 209   /// Return the protocol level for `setsockopt`/`getsockopt`.
HITCBC 210   66 static constexpr int level() noexcept 210   66 static constexpr int level() noexcept
211   { 211   {
HITCBC 212   66 return Level; 212   66 return Level;
213   } 213   }
214   214  
215   /// Return the option name for `setsockopt`/`getsockopt`. 215   /// Return the option name for `setsockopt`/`getsockopt`.
HITCBC 216   66 static constexpr int name() noexcept 216   66 static constexpr int name() noexcept
217   { 217   {
HITCBC 218   66 return Name; 218   66 return Name;
219   } 219   }
220   220  
221   /// Return a pointer to the underlying storage. 221   /// Return a pointer to the underlying storage.
HITCBC 222   6 void* data() noexcept 222   6 void* data() noexcept
223   { 223   {
HITCBC 224   6 return &value_; 224   6 return &value_;
225   } 225   }
226   226  
227   /// Return a pointer to the underlying storage. 227   /// Return a pointer to the underlying storage.
HITCBC 228   8 void const* data() const noexcept 228   8 void const* data() const noexcept
229   { 229   {
HITCBC 230   8 return &value_; 230   8 return &value_;
231   } 231   }
232   232  
233   /// Return the size of the underlying storage. 233   /// Return the size of the underlying storage.
HITCBC 234   14 std::size_t size() const noexcept 234   14 std::size_t size() const noexcept
235   { 235   {
HITCBC 236   14 return sizeof(value_); 236   14 return sizeof(value_);
237   } 237   }
238   238  
239   /** Normalize after `getsockopt` returns fewer bytes than expected. 239   /** Normalize after `getsockopt` returns fewer bytes than expected.
240   240  
241   @param s The number of bytes actually written by `getsockopt`. 241   @param s The number of bytes actually written by `getsockopt`.
242   */ 242   */
HITCBC 243   6 void resize(std::size_t s) noexcept 243   6 void resize(std::size_t s) noexcept
244   { 244   {
HITCBC 245   6 if (s == sizeof(char)) 245   6 if (s == sizeof(char))
MISUBC 246   value_ = 246   value_ =
MISUBC 247   static_cast<int>(*reinterpret_cast<unsigned char*>(&value_)); 247   static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
HITCBC 248   6 } 248   6 }
249   }; 249   };
250   250  
251   /** A boolean socket option with single-byte storage. 251   /** A boolean socket option with single-byte storage.
252   252  
253   Some BSD-derived kernels (macOS, FreeBSD) require certain IPv4 multicast 253   Some BSD-derived kernels (macOS, FreeBSD) require certain IPv4 multicast
254   options (`IP_MULTICAST_LOOP`) to be set with a one-byte value and return 254   options (`IP_MULTICAST_LOOP`) to be set with a one-byte value and return
255   `EINVAL` for the four-byte form that Linux accepts. This template 255   `EINVAL` for the four-byte form that Linux accepts. This template
256   provides `unsigned char` storage so the option works on every platform. 256   provides `unsigned char` storage so the option works on every platform.
257   257  
258   @tparam Level The protocol level. 258   @tparam Level The protocol level.
259   @tparam Name The option name. 259   @tparam Name The option name.
260   */ 260   */
261   template<int Level, int Name> 261   template<int Level, int Name>
262   class byte_boolean 262   class byte_boolean
263   { 263   {
264   unsigned char value_ = 0; 264   unsigned char value_ = 0;
265   265  
266   public: 266   public:
267   byte_boolean() = default; 267   byte_boolean() = default;
268   268  
HITCBC 269   2 explicit byte_boolean(bool v) noexcept : value_(v ? 1 : 0) {} 269   2 explicit byte_boolean(bool v) noexcept : value_(v ? 1 : 0) {}
270   270  
271   byte_boolean& operator=(bool v) noexcept 271   byte_boolean& operator=(bool v) noexcept
272   { 272   {
273   value_ = v ? 1 : 0; 273   value_ = v ? 1 : 0;
274   return *this; 274   return *this;
275   } 275   }
276   276  
HITCBC 277   2 bool value() const noexcept { return value_ != 0; } 277   2 bool value() const noexcept { return value_ != 0; }
278   explicit operator bool() const noexcept { return value_ != 0; } 278   explicit operator bool() const noexcept { return value_ != 0; }
279   bool operator!() const noexcept { return value_ == 0; } 279   bool operator!() const noexcept { return value_ == 0; }
280   280  
HITCBC 281   14 static constexpr int level() noexcept { return Level; } 281   14 static constexpr int level() noexcept { return Level; }
HITCBC 282   14 static constexpr int name() noexcept { return Name; } 282   14 static constexpr int name() noexcept { return Name; }
283   283  
HITCBC 284   2 void* data() noexcept { return &value_; } 284   2 void* data() noexcept { return &value_; }
HITCBC 285   2 void const* data() const noexcept { return &value_; } 285   2 void const* data() const noexcept { return &value_; }
HITCBC 286   4 std::size_t size() const noexcept { return sizeof(value_); } 286   4 std::size_t size() const noexcept { return sizeof(value_); }
287   287  
HITCBC 288   2 void resize(std::size_t) noexcept {} 288   2 void resize(std::size_t) noexcept {}
289   }; 289   };
290   290  
291   /** An integer socket option with single-byte storage. 291   /** An integer socket option with single-byte storage.
292   292  
293   Same rationale as `byte_boolean`: BSD-derived kernels require 293   Same rationale as `byte_boolean`: BSD-derived kernels require
294   `IP_MULTICAST_TTL` to be set with a one-byte value. Linux accepts 294   `IP_MULTICAST_TTL` to be set with a one-byte value. Linux accepts
295   one byte too, so single-byte storage is portable. Values are 295   one byte too, so single-byte storage is portable. Values are
296   truncated to the 0–255 range. 296   truncated to the 0–255 range.
297   297  
298   @tparam Level The protocol level. 298   @tparam Level The protocol level.
299   @tparam Name The option name. 299   @tparam Name The option name.
300   */ 300   */
301   template<int Level, int Name> 301   template<int Level, int Name>
302   class byte_integer 302   class byte_integer
303   { 303   {
304   unsigned char value_ = 0; 304   unsigned char value_ = 0;
305   305  
306   public: 306   public:
307   byte_integer() = default; 307   byte_integer() = default;
308   308  
HITCBC 309   2 explicit byte_integer(int v) noexcept 309   2 explicit byte_integer(int v) noexcept
HITCBC 310   2 : value_(static_cast<unsigned char>(v)) 310   2 : value_(static_cast<unsigned char>(v))
HITCBC 311   2 {} 311   2 {}
312   312  
313   byte_integer& operator=(int v) noexcept 313   byte_integer& operator=(int v) noexcept
314   { 314   {
315   value_ = static_cast<unsigned char>(v); 315   value_ = static_cast<unsigned char>(v);
316   return *this; 316   return *this;
317   } 317   }
318   318  
HITCBC 319   2 int value() const noexcept { return value_; } 319   2 int value() const noexcept { return value_; }
320   320  
HITCBC 321   8 static constexpr int level() noexcept { return Level; } 321   8 static constexpr int level() noexcept { return Level; }
HITCBC 322   8 static constexpr int name() noexcept { return Name; } 322   8 static constexpr int name() noexcept { return Name; }
323   323  
HITCBC 324   2 void* data() noexcept { return &value_; } 324   2 void* data() noexcept { return &value_; }
HITCBC 325   2 void const* data() const noexcept { return &value_; } 325   2 void const* data() const noexcept { return &value_; }
HITCBC 326   4 std::size_t size() const noexcept { return sizeof(value_); } 326   4 std::size_t size() const noexcept { return sizeof(value_); }
327   327  
HITCBC 328   2 void resize(std::size_t) noexcept {} 328   2 void resize(std::size_t) noexcept {}
329   }; 329   };
330   330  
331   /** The SO_LINGER socket option (native variant). 331   /** The SO_LINGER socket option (native variant).
332   332  
333   Controls behavior when closing a socket with unsent data. 333   Controls behavior when closing a socket with unsent data.
334   When enabled, `close()` blocks until pending data is sent 334   When enabled, `close()` blocks until pending data is sent
335   or the timeout expires. 335   or the timeout expires.
336   336  
337   This variant stores the platform's `struct linger` directly, 337   This variant stores the platform's `struct linger` directly,
338   avoiding the opaque-storage indirection of the type-erased 338   avoiding the opaque-storage indirection of the type-erased
339   version. 339   version.
340   340  
341   @par Example 341   @par Example
342   @code 342   @code
343   sock.set_option( native_socket_option::linger( true, 5 ) ); 343   sock.set_option( native_socket_option::linger( true, 5 ) );
344   auto opt = sock.get_option<native_socket_option::linger>(); 344   auto opt = sock.get_option<native_socket_option::linger>();
345   if ( opt.enabled() ) 345   if ( opt.enabled() )
346   std::cout << "linger timeout: " << opt.timeout() << "s\n"; 346   std::cout << "linger timeout: " << opt.timeout() << "s\n";
347   @endcode 347   @endcode
348   */ 348   */
349   class linger 349   class linger
350   { 350   {
351   struct ::linger value_{}; 351   struct ::linger value_{};
352   352  
353   public: 353   public:
354   /// Construct with default values (disabled, zero timeout). 354   /// Construct with default values (disabled, zero timeout).
HITCBC 355   44 linger() = default; 355   44 linger() = default;
356   356  
357   /** Construct with explicit values. 357   /** Construct with explicit values.
358   358  
359   @param enabled `true` to enable linger behavior on close. 359   @param enabled `true` to enable linger behavior on close.
360   @param timeout The linger timeout in seconds. 360   @param timeout The linger timeout in seconds.
361   */ 361   */
HITCBC 362   34 linger(bool enabled, int timeout) noexcept 362   34 linger(bool enabled, int timeout) noexcept
HITCBC 363   34 { 363   34 {
HITCBC 364   34 value_.l_onoff = enabled ? 1 : 0; 364   34 value_.l_onoff = enabled ? 1 : 0;
HITCBC 365   34 value_.l_linger = static_cast<decltype(value_.l_linger)>(timeout); 365   34 value_.l_linger = static_cast<decltype(value_.l_linger)>(timeout);
HITCBC 366   34 } 366   34 }
367   367  
368   /// Return whether linger is enabled. 368   /// Return whether linger is enabled.
HITCBC 369   20 bool enabled() const noexcept 369   20 bool enabled() const noexcept
370   { 370   {
HITCBC 371   20 return value_.l_onoff != 0; 371   20 return value_.l_onoff != 0;
372   } 372   }
373   373  
374   /// Set whether linger is enabled. 374   /// Set whether linger is enabled.
HITCBC 375   4 void enabled(bool v) noexcept 375   4 void enabled(bool v) noexcept
376   { 376   {
HITCBC 377   4 value_.l_onoff = v ? 1 : 0; 377   4 value_.l_onoff = v ? 1 : 0;
HITCBC 378   4 } 378   4 }
379   379  
380   /// Return the linger timeout in seconds. 380   /// Return the linger timeout in seconds.
HITCBC 381   18 int timeout() const noexcept 381   18 int timeout() const noexcept
382   { 382   {
HITCBC 383   18 return static_cast<int>(value_.l_linger); 383   18 return static_cast<int>(value_.l_linger);
384   } 384   }
385   385  
386   /// Set the linger timeout in seconds. 386   /// Set the linger timeout in seconds.
HITCBC 387   4 void timeout(int v) noexcept 387   4 void timeout(int v) noexcept
388   { 388   {
HITCBC 389   4 value_.l_linger = static_cast<decltype(value_.l_linger)>(v); 389   4 value_.l_linger = static_cast<decltype(value_.l_linger)>(v);
HITCBC 390   4 } 390   4 }
391   391  
392   /// Return the protocol level for `setsockopt`/`getsockopt`. 392   /// Return the protocol level for `setsockopt`/`getsockopt`.
HITCBC 393   46 static constexpr int level() noexcept 393   46 static constexpr int level() noexcept
394   { 394   {
HITCBC 395   46 return SOL_SOCKET; 395   46 return SOL_SOCKET;
396   } 396   }
397   397  
398   /// Return the option name for `setsockopt`/`getsockopt`. 398   /// Return the option name for `setsockopt`/`getsockopt`.
HITCBC 399   46 static constexpr int name() noexcept 399   46 static constexpr int name() noexcept
400   { 400   {
HITCBC 401   46 return SO_LINGER; 401   46 return SO_LINGER;
402   } 402   }
403   403  
404   /// Return a pointer to the underlying storage. 404   /// Return a pointer to the underlying storage.
HITCBC 405   68 void* data() noexcept 405   68 void* data() noexcept
406   { 406   {
HITCBC 407   68 return &value_; 407   68 return &value_;
408   } 408   }
409   409  
410   /// Return a pointer to the underlying storage. 410   /// Return a pointer to the underlying storage.
HITCBC 411   2 void const* data() const noexcept 411   2 void const* data() const noexcept
412   { 412   {
HITCBC 413   2 return &value_; 413   2 return &value_;
414   } 414   }
415   415  
416   /// Return the size of the underlying storage. 416   /// Return the size of the underlying storage.
HITCBC 417   112 std::size_t size() const noexcept 417   112 std::size_t size() const noexcept
418   { 418   {
HITCBC 419   112 return sizeof(value_); 419   112 return sizeof(value_);
420   } 420   }
421   421  
422   /** Normalize after `getsockopt`. 422   /** Normalize after `getsockopt`.
423   423  
424   No-op — `struct linger` is always returned at full size. 424   No-op — `struct linger` is always returned at full size.
425   425  
426   @param s The number of bytes actually written by `getsockopt`. 426   @param s The number of bytes actually written by `getsockopt`.
427   */ 427   */
428   void resize(std::size_t) noexcept {} 428   void resize(std::size_t) noexcept {}
429   }; 429   };
430   430  
431   /// Disable Nagle's algorithm (TCP_NODELAY). 431   /// Disable Nagle's algorithm (TCP_NODELAY).
432   using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>; 432   using no_delay = boolean<IPPROTO_TCP, TCP_NODELAY>;
433   433  
434   /// Enable periodic keepalive probes (SO_KEEPALIVE). 434   /// Enable periodic keepalive probes (SO_KEEPALIVE).
435   using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>; 435   using keep_alive = boolean<SOL_SOCKET, SO_KEEPALIVE>;
436   436  
437   /// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY). 437   /// Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
438   using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>; 438   using v6_only = boolean<IPPROTO_IPV6, IPV6_V6ONLY>;
439   439  
440   /// Allow local address reuse (SO_REUSEADDR). 440   /// Allow local address reuse (SO_REUSEADDR).
441   using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>; 441   using reuse_address = boolean<SOL_SOCKET, SO_REUSEADDR>;
442   442  
443   /// Allow sending to broadcast addresses (SO_BROADCAST). 443   /// Allow sending to broadcast addresses (SO_BROADCAST).
444   using broadcast = boolean<SOL_SOCKET, SO_BROADCAST>; 444   using broadcast = boolean<SOL_SOCKET, SO_BROADCAST>;
445   445  
446   /// Set the receive buffer size (SO_RCVBUF). 446   /// Set the receive buffer size (SO_RCVBUF).
447   using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>; 447   using receive_buffer_size = integer<SOL_SOCKET, SO_RCVBUF>;
448   448  
449   /// Set the send buffer size (SO_SNDBUF). 449   /// Set the send buffer size (SO_SNDBUF).
450   using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>; 450   using send_buffer_size = integer<SOL_SOCKET, SO_SNDBUF>;
451   451  
452   #ifdef SO_REUSEPORT 452   #ifdef SO_REUSEPORT
453   /// Allow multiple sockets to bind to the same port (SO_REUSEPORT). 453   /// Allow multiple sockets to bind to the same port (SO_REUSEPORT).
454   using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>; 454   using reuse_port = boolean<SOL_SOCKET, SO_REUSEPORT>;
455   #endif 455   #endif
456   456  
457   /// Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP). 457   /// Enable loopback of outgoing multicast on IPv4 (IP_MULTICAST_LOOP).
458   using multicast_loop_v4 = byte_boolean<IPPROTO_IP, IP_MULTICAST_LOOP>; 458   using multicast_loop_v4 = byte_boolean<IPPROTO_IP, IP_MULTICAST_LOOP>;
459   459  
460   /// Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP). 460   /// Enable loopback of outgoing multicast on IPv6 (IPV6_MULTICAST_LOOP).
461   using multicast_loop_v6 = boolean<IPPROTO_IPV6, IPV6_MULTICAST_LOOP>; 461   using multicast_loop_v6 = boolean<IPPROTO_IPV6, IPV6_MULTICAST_LOOP>;
462   462  
463   /// Set the multicast TTL for IPv4 (IP_MULTICAST_TTL). 463   /// Set the multicast TTL for IPv4 (IP_MULTICAST_TTL).
464   using multicast_hops_v4 = byte_integer<IPPROTO_IP, IP_MULTICAST_TTL>; 464   using multicast_hops_v4 = byte_integer<IPPROTO_IP, IP_MULTICAST_TTL>;
465   465  
466   /// Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS). 466   /// Set the multicast hop limit for IPv6 (IPV6_MULTICAST_HOPS).
467   using multicast_hops_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_HOPS>; 467   using multicast_hops_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_HOPS>;
468   468  
469   /// Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF). 469   /// Set the outgoing interface for IPv6 multicast (IPV6_MULTICAST_IF).
470   using multicast_interface_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_IF>; 470   using multicast_interface_v6 = integer<IPPROTO_IPV6, IPV6_MULTICAST_IF>;
471   471  
472   /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP). 472   /** Join an IPv4 multicast group (IP_ADD_MEMBERSHIP).
473   473  
474   @par Example 474   @par Example
475   @code 475   @code
476   sock.set_option( native_socket_option::join_group_v4( 476   sock.set_option( native_socket_option::join_group_v4(
477   ipv4_address( "239.255.0.1" ) ) ); 477   ipv4_address( "239.255.0.1" ) ) );
478   @endcode 478   @endcode
479   */ 479   */
480   class join_group_v4 480   class join_group_v4
481   { 481   {
482   struct ip_mreq value_{}; 482   struct ip_mreq value_{};
483   483  
484   public: 484   public:
485   /// Construct with default values. 485   /// Construct with default values.
HITCBC 486   4 join_group_v4() = default; 486   4 join_group_v4() = default;
487   487  
488   /** Construct with a group and optional interface address. 488   /** Construct with a group and optional interface address.
489   489  
490   @param group The multicast group address to join. 490   @param group The multicast group address to join.
491   @param iface The local interface to use (default: any). 491   @param iface The local interface to use (default: any).
492   */ 492   */
HITCBC 493   6 join_group_v4( 493   6 join_group_v4(
494   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept 494   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
HITCBC 495   6 { 495   6 {
HITCBC 496   6 auto gb = group.to_bytes(); 496   6 auto gb = group.to_bytes();
HITCBC 497   6 auto ib = iface.to_bytes(); 497   6 auto ib = iface.to_bytes();
HITCBC 498   6 std::memcpy(&value_.imr_multiaddr, gb.data(), 4); 498   6 std::memcpy(&value_.imr_multiaddr, gb.data(), 4);
HITCBC 499   6 std::memcpy(&value_.imr_interface, ib.data(), 4); 499   6 std::memcpy(&value_.imr_interface, ib.data(), 4);
HITCBC 500   6 } 500   6 }
501   501  
502   /// Return the protocol level for `setsockopt`/`getsockopt`. 502   /// Return the protocol level for `setsockopt`/`getsockopt`.
HITCBC 503   8 static constexpr int level() noexcept 503   8 static constexpr int level() noexcept
504   { 504   {
HITCBC 505   8 return IPPROTO_IP; 505   8 return IPPROTO_IP;
506   } 506   }
507   507  
508   /// Return the option name for `setsockopt`/`getsockopt`. 508   /// Return the option name for `setsockopt`/`getsockopt`.
HITCBC 509   8 static constexpr int name() noexcept 509   8 static constexpr int name() noexcept
510   { 510   {
HITCBC 511   8 return IP_ADD_MEMBERSHIP; 511   8 return IP_ADD_MEMBERSHIP;
512   } 512   }
513   513  
514   /// Return a pointer to the underlying storage. 514   /// Return a pointer to the underlying storage.
HITCBC 515   6 void* data() noexcept 515   6 void* data() noexcept
516   { 516   {
HITCBC 517   6 return &value_; 517   6 return &value_;
518   } 518   }
519   519  
520   /// Return a pointer to the underlying storage. 520   /// Return a pointer to the underlying storage.
HITCBC 521   2 void const* data() const noexcept 521   2 void const* data() const noexcept
522   { 522   {
HITCBC 523   2 return &value_; 523   2 return &value_;
524   } 524   }
525   525  
526   /// Return the size of the underlying storage. 526   /// Return the size of the underlying storage.
HITCBC 527   12 std::size_t size() const noexcept 527   12 std::size_t size() const noexcept
528   { 528   {
HITCBC 529   12 return sizeof(value_); 529   12 return sizeof(value_);
530   } 530   }
531   531  
532   /// No-op resize. 532   /// No-op resize.
533   void resize(std::size_t) noexcept {} 533   void resize(std::size_t) noexcept {}
534   }; 534   };
535   535  
536   /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP). 536   /** Leave an IPv4 multicast group (IP_DROP_MEMBERSHIP).
537   537  
538   @par Example 538   @par Example
539   @code 539   @code
540   sock.set_option( native_socket_option::leave_group_v4( 540   sock.set_option( native_socket_option::leave_group_v4(
541   ipv4_address( "239.255.0.1" ) ) ); 541   ipv4_address( "239.255.0.1" ) ) );
542   @endcode 542   @endcode
543   */ 543   */
544   class leave_group_v4 544   class leave_group_v4
545   { 545   {
546   struct ip_mreq value_{}; 546   struct ip_mreq value_{};
547   547  
548   public: 548   public:
549   /// Construct with default values. 549   /// Construct with default values.
HITCBC 550   2 leave_group_v4() = default; 550   2 leave_group_v4() = default;
551   551  
552   /** Construct with a group and optional interface address. 552   /** Construct with a group and optional interface address.
553   553  
554   @param group The multicast group address to leave. 554   @param group The multicast group address to leave.
555   @param iface The local interface (default: any). 555   @param iface The local interface (default: any).
556   */ 556   */
HITCBC 557   4 leave_group_v4( 557   4 leave_group_v4(
558   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept 558   ipv4_address group, ipv4_address iface = ipv4_address()) noexcept
HITCBC 559   4 { 559   4 {
HITCBC 560   4 auto gb = group.to_bytes(); 560   4 auto gb = group.to_bytes();
HITCBC 561   4 auto ib = iface.to_bytes(); 561   4 auto ib = iface.to_bytes();
HITCBC 562   4 std::memcpy(&value_.imr_multiaddr, gb.data(), 4); 562   4 std::memcpy(&value_.imr_multiaddr, gb.data(), 4);
HITCBC 563   4 std::memcpy(&value_.imr_interface, ib.data(), 4); 563   4 std::memcpy(&value_.imr_interface, ib.data(), 4);
HITCBC 564   4 } 564   4 }
565   565  
566   /// Return the protocol level for `setsockopt`/`getsockopt`. 566   /// Return the protocol level for `setsockopt`/`getsockopt`.
HITCBC 567   6 static constexpr int level() noexcept 567   6 static constexpr int level() noexcept
568   { 568   {
HITCBC 569   6 return IPPROTO_IP; 569   6 return IPPROTO_IP;
570   } 570   }
571   571  
572   /// Return the option name for `setsockopt`/`getsockopt`. 572   /// Return the option name for `setsockopt`/`getsockopt`.
HITCBC 573   6 static constexpr int name() noexcept 573   6 static constexpr int name() noexcept
574   { 574   {
HITCBC 575   6 return IP_DROP_MEMBERSHIP; 575   6 return IP_DROP_MEMBERSHIP;
576   } 576   }
577   577  
578   /// Return a pointer to the underlying storage. 578   /// Return a pointer to the underlying storage.
HITCBC 579   4 void* data() noexcept 579   4 void* data() noexcept
580   { 580   {
HITCBC 581   4 return &value_; 581   4 return &value_;
582   } 582   }
583   583  
584   /// Return a pointer to the underlying storage. 584   /// Return a pointer to the underlying storage.
HITCBC 585   2 void const* data() const noexcept 585   2 void const* data() const noexcept
586   { 586   {
HITCBC 587   2 return &value_; 587   2 return &value_;
588   } 588   }
589   589  
590   /// Return the size of the underlying storage. 590   /// Return the size of the underlying storage.
HITCBC 591   8 std::size_t size() const noexcept 591   8 std::size_t size() const noexcept
592   { 592   {
HITCBC 593   8 return sizeof(value_); 593   8 return sizeof(value_);
594   } 594   }
595   595  
596   /// No-op resize. 596   /// No-op resize.
597   void resize(std::size_t) noexcept {} 597   void resize(std::size_t) noexcept {}
598   }; 598   };
599   599  
600   /** Join an IPv6 multicast group (IPV6_JOIN_GROUP). 600   /** Join an IPv6 multicast group (IPV6_JOIN_GROUP).
601   601  
602   @par Example 602   @par Example
603   @code 603   @code
604   sock.set_option( native_socket_option::join_group_v6( 604   sock.set_option( native_socket_option::join_group_v6(
605   ipv6_address( "ff02::1" ), 0 ) ); 605   ipv6_address( "ff02::1" ), 0 ) );
606   @endcode 606   @endcode
607   */ 607   */
608   class join_group_v6 608   class join_group_v6
609   { 609   {
610   struct ipv6_mreq value_{}; 610   struct ipv6_mreq value_{};
611   611  
612   public: 612   public:
613   /// Construct with default values. 613   /// Construct with default values.
HITCBC 614   2 join_group_v6() = default; 614   2 join_group_v6() = default;
615   615  
616   /** Construct with a group and optional interface index. 616   /** Construct with a group and optional interface index.
617   617  
618   @param group The multicast group address to join. 618   @param group The multicast group address to join.
619   @param if_index The interface index (0 = kernel chooses). 619   @param if_index The interface index (0 = kernel chooses).
620   */ 620   */
HITCBC 621   4 join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept 621   4 join_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept
HITCBC 622   4 { 622   4 {
HITCBC 623   4 auto gb = group.to_bytes(); 623   4 auto gb = group.to_bytes();
HITCBC 624   4 std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16); 624   4 std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16);
HITCBC 625   4 value_.ipv6mr_interface = if_index; 625   4 value_.ipv6mr_interface = if_index;
HITCBC 626   4 } 626   4 }
627   627  
628   /// Return the protocol level for `setsockopt`/`getsockopt`. 628   /// Return the protocol level for `setsockopt`/`getsockopt`.
HITCBC 629   6 static constexpr int level() noexcept 629   6 static constexpr int level() noexcept
630   { 630   {
HITCBC 631   6 return IPPROTO_IPV6; 631   6 return IPPROTO_IPV6;
632   } 632   }
633   633  
634   /// Return the option name for `setsockopt`/`getsockopt`. 634   /// Return the option name for `setsockopt`/`getsockopt`.
HITCBC 635   6 static constexpr int name() noexcept 635   6 static constexpr int name() noexcept
636   { 636   {
HITCBC 637   6 return IPV6_JOIN_GROUP; 637   6 return IPV6_JOIN_GROUP;
638   } 638   }
639   639  
640   /// Return a pointer to the underlying storage. 640   /// Return a pointer to the underlying storage.
HITCBC 641   4 void* data() noexcept 641   4 void* data() noexcept
642   { 642   {
HITCBC 643   4 return &value_; 643   4 return &value_;
644   } 644   }
645   645  
646   /// Return a pointer to the underlying storage. 646   /// Return a pointer to the underlying storage.
HITCBC 647   2 void const* data() const noexcept 647   2 void const* data() const noexcept
648   { 648   {
HITCBC 649   2 return &value_; 649   2 return &value_;
650   } 650   }
651   651  
652   /// Return the size of the underlying storage. 652   /// Return the size of the underlying storage.
HITCBC 653   8 std::size_t size() const noexcept 653   8 std::size_t size() const noexcept
654   { 654   {
HITCBC 655   8 return sizeof(value_); 655   8 return sizeof(value_);
656   } 656   }
657   657  
658   /// No-op resize. 658   /// No-op resize.
659   void resize(std::size_t) noexcept {} 659   void resize(std::size_t) noexcept {}
660   }; 660   };
661   661  
662   /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP). 662   /** Leave an IPv6 multicast group (IPV6_LEAVE_GROUP).
663   663  
664   @par Example 664   @par Example
665   @code 665   @code
666   sock.set_option( native_socket_option::leave_group_v6( 666   sock.set_option( native_socket_option::leave_group_v6(
667   ipv6_address( "ff02::1" ), 0 ) ); 667   ipv6_address( "ff02::1" ), 0 ) );
668   @endcode 668   @endcode
669   */ 669   */
670   class leave_group_v6 670   class leave_group_v6
671   { 671   {
672   struct ipv6_mreq value_{}; 672   struct ipv6_mreq value_{};
673   673  
674   public: 674   public:
675   /// Construct with default values. 675   /// Construct with default values.
HITCBC 676   2 leave_group_v6() = default; 676   2 leave_group_v6() = default;
677   677  
678   /** Construct with a group and optional interface index. 678   /** Construct with a group and optional interface index.
679   679  
680   @param group The multicast group address to leave. 680   @param group The multicast group address to leave.
681   @param if_index The interface index (0 = kernel chooses). 681   @param if_index The interface index (0 = kernel chooses).
682   */ 682   */
HITCBC 683   4 leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept 683   4 leave_group_v6(ipv6_address group, unsigned int if_index = 0) noexcept
HITCBC 684   4 { 684   4 {
HITCBC 685   4 auto gb = group.to_bytes(); 685   4 auto gb = group.to_bytes();
HITCBC 686   4 std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16); 686   4 std::memcpy(&value_.ipv6mr_multiaddr, gb.data(), 16);
HITCBC 687   4 value_.ipv6mr_interface = if_index; 687   4 value_.ipv6mr_interface = if_index;
HITCBC 688   4 } 688   4 }
689   689  
690   /// Return the protocol level for `setsockopt`/`getsockopt`. 690   /// Return the protocol level for `setsockopt`/`getsockopt`.
HITCBC 691   6 static constexpr int level() noexcept 691   6 static constexpr int level() noexcept
692   { 692   {
HITCBC 693   6 return IPPROTO_IPV6; 693   6 return IPPROTO_IPV6;
694   } 694   }
695   695  
696   /// Return the option name for `setsockopt`/`getsockopt`. 696   /// Return the option name for `setsockopt`/`getsockopt`.
HITCBC 697   6 static constexpr int name() noexcept 697   6 static constexpr int name() noexcept
698   { 698   {
HITCBC 699   6 return IPV6_LEAVE_GROUP; 699   6 return IPV6_LEAVE_GROUP;
700   } 700   }
701   701  
702   /// Return a pointer to the underlying storage. 702   /// Return a pointer to the underlying storage.
HITCBC 703   4 void* data() noexcept 703   4 void* data() noexcept
704   { 704   {
HITCBC 705   4 return &value_; 705   4 return &value_;
706   } 706   }
707   707  
708   /// Return a pointer to the underlying storage. 708   /// Return a pointer to the underlying storage.
HITCBC 709   2 void const* data() const noexcept 709   2 void const* data() const noexcept
710   { 710   {
HITCBC 711   2 return &value_; 711   2 return &value_;
712   } 712   }
713   713  
714   /// Return the size of the underlying storage. 714   /// Return the size of the underlying storage.
HITCBC 715   8 std::size_t size() const noexcept 715   8 std::size_t size() const noexcept
716   { 716   {
HITCBC 717   8 return sizeof(value_); 717   8 return sizeof(value_);
718   } 718   }
719   719  
720   /// No-op resize. 720   /// No-op resize.
721   void resize(std::size_t) noexcept {} 721   void resize(std::size_t) noexcept {}
722   }; 722   };
723   723  
724   /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF). 724   /** Set the outgoing interface for IPv4 multicast (IP_MULTICAST_IF).
725   725  
726   Unlike the integer-based `multicast_interface_v6`, this option 726   Unlike the integer-based `multicast_interface_v6`, this option
727   takes an `ipv4_address` identifying the local interface. 727   takes an `ipv4_address` identifying the local interface.
728   728  
729   @par Example 729   @par Example
730   @code 730   @code
731   sock.set_option( native_socket_option::multicast_interface_v4( 731   sock.set_option( native_socket_option::multicast_interface_v4(
732   ipv4_address( "192.168.1.1" ) ) ); 732   ipv4_address( "192.168.1.1" ) ) );
733   @endcode 733   @endcode
734   */ 734   */
735   class multicast_interface_v4 735   class multicast_interface_v4
736   { 736   {
737   struct in_addr value_{}; 737   struct in_addr value_{};
738   738  
739   public: 739   public:
740   /// Construct with default values (INADDR_ANY). 740   /// Construct with default values (INADDR_ANY).
HITCBC 741   2 multicast_interface_v4() = default; 741   2 multicast_interface_v4() = default;
742   742  
743   /** Construct with an interface address. 743   /** Construct with an interface address.
744   744  
745   @param iface The local interface address. 745   @param iface The local interface address.
746   */ 746   */
HITCBC 747   4 explicit multicast_interface_v4(ipv4_address iface) noexcept 747   4 explicit multicast_interface_v4(ipv4_address iface) noexcept
HITCBC 748   4 { 748   4 {
HITCBC 749   4 auto b = iface.to_bytes(); 749   4 auto b = iface.to_bytes();
HITCBC 750   4 std::memcpy(&value_, b.data(), 4); 750   4 std::memcpy(&value_, b.data(), 4);
HITCBC 751   4 } 751   4 }
752   752  
753   /// Return the protocol level for `setsockopt`/`getsockopt`. 753   /// Return the protocol level for `setsockopt`/`getsockopt`.
HITCBC 754   6 static constexpr int level() noexcept 754   6 static constexpr int level() noexcept
755   { 755   {
HITCBC 756   6 return IPPROTO_IP; 756   6 return IPPROTO_IP;
757   } 757   }
758   758  
759   /// Return the option name for `setsockopt`/`getsockopt`. 759   /// Return the option name for `setsockopt`/`getsockopt`.
HITCBC 760   6 static constexpr int name() noexcept 760   6 static constexpr int name() noexcept
761   { 761   {
HITCBC 762   6 return IP_MULTICAST_IF; 762   6 return IP_MULTICAST_IF;
763   } 763   }
764   764  
765   /// Return a pointer to the underlying storage. 765   /// Return a pointer to the underlying storage.
HITCBC 766   4 void* data() noexcept 766   4 void* data() noexcept
767   { 767   {
HITCBC 768   4 return &value_; 768   4 return &value_;
769   } 769   }
770   770  
771   /// Return a pointer to the underlying storage. 771   /// Return a pointer to the underlying storage.
HITCBC 772   2 void const* data() const noexcept 772   2 void const* data() const noexcept
773   { 773   {
HITCBC 774   2 return &value_; 774   2 return &value_;
775   } 775   }
776   776  
777   /// Return the size of the underlying storage. 777   /// Return the size of the underlying storage.
HITCBC 778   8 std::size_t size() const noexcept 778   8 std::size_t size() const noexcept
779   { 779   {
HITCBC 780   8 return sizeof(value_); 780   8 return sizeof(value_);
781   } 781   }
782   782  
783   /// No-op resize. 783   /// No-op resize.
784   void resize(std::size_t) noexcept {} 784   void resize(std::size_t) noexcept {}
785   }; 785   };
786   786  
787   } // namespace boost::corosio::native_socket_option 787   } // namespace boost::corosio::native_socket_option
788   788  
789   #endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP 789   #endif // BOOST_COROSIO_NATIVE_NATIVE_SOCKET_OPTION_HPP