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