uvcc
libuv C++ bindings
buffer.hpp
1 
2 #ifndef UVCC_BUFFER__HPP
3 #define UVCC_BUFFER__HPP
4 
5 #include "uvcc/debug.hpp"
6 #include "uvcc/utility.hpp"
7 
8 #include <cstddef> // size_t offsetof max_align_t
9 #include <uv.h>
10 
11 #include <type_traits> // is_standard_layout
12 #include <utility> // swap()
13 #include <initializer_list> // initializer_list
14 #include <functional> // function
15 
16 
17 namespace uv
18 {
19 
20 
21 class handle;
22 
23 
24 /*! \defgroup doxy_group__buffer Buffer for I/O operations */
25 
26 /*! \ingroup doxy_group__buffer
27  \brief Encapsulates `uv_buf_t` data type and provides `uv_buf_t[]` functionality. */
28 class buffer
29 {
30  //! \cond
31  friend class io;
32  friend class write;
33  friend class udp;
34  friend class udp_send;
35  friend class fs;
36  //! \endcond
37 
38 public: /*types*/
39  using uv_t = ::uv_buf_t;
40  using sink_cb_t = std::function< void(buffer&) >;
41  /*!< \brief The function type of the callback called when the reference count of the buffer using within
42  the program becomes zero and the buffer instance is going to be destroyed. uvcc creates a new variable
43  referencing the buffer instance (so its usage count gets equal to one) and passes a reference to this variable
44  to the sink callback. If the client code wish to store this free buffer for further reuse, it must _move_ (or _copy_)
45  the variable into some designated storage structure, otherwise no any action is required, and the buffer will be
46  destroyed. */
47 
48 private: /*types*/
49  class instance
50  {
51  public: /*data*/
52  ref_count refs;
53  type_storage< sink_cb_t > sink_cb_storage;
54  std::size_t buf_count;
55  uv_t uv_buf_struct;
56 
57  private: /*new/delete*/
58  static void* operator new(std::size_t _size, const std::initializer_list< std::size_t > &_len_values)
59  {
60  auto extra_buf_count = _len_values.size();
61  if (extra_buf_count > 0) --extra_buf_count;
62  std::size_t total_buf_len = 0;
63  for (auto len : _len_values) total_buf_len += len;
64  return ::operator new(_size + extra_buf_count*sizeof(uv_t) + alignment_padding(extra_buf_count) + total_buf_len);
65  }
66  static void operator delete(void *_ptr, const std::initializer_list< std::size_t >&) { ::operator delete(_ptr); }
67  static void operator delete(void *_ptr) { ::operator delete(_ptr); }
68 
69  private: /*constructors*/
70  instance(const std::initializer_list< std::size_t > &_len_values) : buf_count(_len_values.size())
71  {
72  if (buf_count == 0)
73  {
74  buf_count = 1;
75  uv_buf_struct.base = nullptr;
76  uv_buf_struct.len = 0;
77  }
78  else
79  {
80  uv_t *buf = &uv_buf_struct;
81  std::size_t total_buf_len = 0;
82  for (auto len : _len_values) total_buf_len += ((buf++)->len = len);
83  if (total_buf_len == 0)
84  {
85  buf = &uv_buf_struct;
86  for (decltype(buf_count) i = 0; i < buf_count; ++i) { buf[i].base = nullptr; buf[i].len = 0; }
87  }
88  else
89  {
90  uv_buf_struct.base = reinterpret_cast< char* >(buf) + alignment_padding(buf_count - 1);
91  buf = &uv_buf_struct;
92  for (decltype(buf_count) i = 1; i < buf_count; ++i) buf[i].base = &buf[i-1].base[buf[i-1].len];
93  }
94  }
95  }
96 
97  public: /*constructors*/
98  ~instance() = default;
99 
100  instance(const instance&) = delete;
101  instance& operator =(const instance&) = delete;
102 
103  instance(instance&&) = delete;
104  instance& operator =(instance&&) = delete;
105 
106  private: /*functions*/
107  static std::size_t alignment_padding(const std::size_t _extra_buf_count) noexcept
108  {
109  const std::size_t base_size = sizeof(instance) + _extra_buf_count*sizeof(uv_t);
110  const std::size_t proper_size = (base_size + alignof(std::max_align_t) - 1) & ~(alignof(std::max_align_t) - 1);
111  return proper_size - base_size;
112  }
113 
114  void destroy()
115  {
116  auto &sink_cb = sink_cb_storage.value();
117  if (sink_cb)
118  {
119  refs.set_value(1);
120  buffer b(&uv_buf_struct, adopt_ref);
121 
122  sink_cb(b);
123  if (b.uv_buf) // if not moved-form
124  {
125  b.uv_buf = nullptr;
126  if (refs.dec() == 0) delete this;
127  }
128  }
129  else
130  delete this;
131  }
132 
133  public: /*interface*/
134  static uv_t* create(const std::initializer_list< std::size_t > &_len_values)
135  { return &(new(_len_values) instance(_len_values))->uv_buf_struct; }
136  static uv_t* create() { return create({}); }
137 
138  constexpr static instance* from(uv_t *_uv_buf) noexcept
139  {
140  static_assert(std::is_standard_layout< instance >::value, "not a standard layout type");
141  return reinterpret_cast< instance* >(reinterpret_cast< char* >(_uv_buf) - offsetof(instance, uv_buf_struct));
142  }
143 
144  struct uv_buf
145  {
146  static uv_t* from(decltype(uv_t::base) _base) noexcept
147  {
148  return reinterpret_cast< uv_t* >(reinterpret_cast< char* >(_base) - alignment_padding(0) - sizeof(uv_t));
149  }
150  };
151 
152  void ref() { refs.inc(); }
153  void unref() noexcept { if (refs.dec() == 0) destroy(); }
154  };
155  //! \cond
156  friend typename buffer::instance* debug::instance<>(buffer&) noexcept;
157  //! \endcond
158 
159 private: /*data*/
160  uv_t *uv_buf;
161 
162 private: /*constructors*/
163  explicit buffer(uv_t *_uv_buf)
164  {
165  if (_uv_buf) instance::from(_uv_buf)->ref();
166  uv_buf = _uv_buf;
167  }
168 
169  explicit buffer(uv_t *_uv_buf, const adopt_ref_t) : uv_buf(_uv_buf) {}
170 
171 public: /*constructors*/
172  ~buffer() { if (uv_buf) instance::from(uv_buf)->unref(); }
173 
174  /*! \brief Create a single `uv_buf_t` _null-initialized_ buffer structure.
175  \details That is:
176  ```
177  char* uv_buf_t.base = nullptr;
178  size_t uv_buf_t.len = 0;
179  ``` */
181 
182  /*! \brief Create an array of initialized `uv_buf_t` buffer describing structures.
183  \details Each `uv_buf_t` structure in the array is effectively initialized with an allocated
184  memory chunk of the specified length. The number of structures in the array is equal to the
185  number of elements in the initializer list. The value of the `.len` field and the length of
186  the each allocated chunk pointed by the `.base` field is equal to the corresponding value
187  from the initializer list.
188 
189  All chunks are located seamlessly one after the next within a single continuous memory block.
190  Therefore the `.base` field of the next buffer just points to the byte following the end
191  of the previous buffer and the `.base` field of the first buffer in the array points to the
192  whole memory area of the total length of all buffers.
193 
194  If some of the initializing values are zeros, the `.base` field of the such a buffer is not a `nullptr`.
195  Instead it keeps pointing inside the continuous memory block and is considered as a zero-length chunk.
196 
197  All of the initializing values being zeros results in creating an array of _null-initialized_
198  `uv_buf_t` structures. */
199  explicit buffer(const std::initializer_list< std::size_t > &_len_values) : uv_buf(instance::create(_len_values)) {}
200 
201  buffer(const buffer &_that) : buffer(_that.uv_buf) {}
202  buffer& operator =(const buffer &_that)
203  {
204  if (this != &_that)
205  {
206  if (_that.uv_buf) instance::from(_that.uv_buf)->ref();
207  auto t = uv_buf;
208  uv_buf = _that.uv_buf;
209  if (t) instance::from(t)->unref();
210  }
211  return *this;
212  }
213 
214  buffer(buffer &&_that) noexcept : uv_buf(_that.uv_buf) { _that.uv_buf = nullptr; }
215  buffer& operator =(buffer &&_that) noexcept
216  {
217  if (this != &_that)
218  {
219  auto t = uv_buf;
220  uv_buf = _that.uv_buf;
221  _that.uv_buf = nullptr;
222  if (t) instance::from(t)->unref();
223  }
224  return *this;
225  }
226 
227 public: /*interface*/
228  void swap(buffer &_that) noexcept { std::swap(uv_buf, _that.uv_buf); }
229  /*! \brief The current number of existing references to the same buffer as this variable refers to. */
230  long nrefs() const noexcept { return instance::from(uv_buf)->refs.get_value(); }
231 
232  sink_cb_t& sink_cb() const noexcept { return instance::from(uv_buf)->sink_cb_storage.value(); }
233 
234  /*! \brief The number of the `uv_buf_t` structures in the array. */
235  std::size_t count() const noexcept { return instance::from(uv_buf)->buf_count; }
236 
237  /*! \brief Access to the `_i`-th `uv_buf_t` buffer structure in the array. */
238  uv_t& operator [](const std::size_t _i) const noexcept { return uv_buf[_i]; }
239  /*! \brief The `.base` field of the `_i`-th buffer structure. */
240  decltype(uv_t::base)& base(const std::size_t _i = 0) const noexcept { return uv_buf[_i].base; }
241  /*! \brief The `.len` field of the `_i`-th buffer structure. */
242  decltype(uv_t::len)& len(const std::size_t _i = 0) const noexcept { return uv_buf[_i].len; }
243 
244 public: /*conversion operators*/
245  explicit operator const uv_t*() const noexcept { return uv_buf; }
246  explicit operator uv_t*() noexcept { return uv_buf; }
247 
248  explicit operator bool() const noexcept { return base(); } /*!< \brief Equivalent to `(base() != nullptr)`. */
249 };
250 
251 
252 /*! \ingroup doxy_group__buffer
253  \brief The function type of the callback called by `io::read_start()` and `udp::recv_start()`...
254  \details ...to supply the input operation with a preallocated buffer. The callback should return a `uv::buffer`
255  instance initialized with a `_suggested_size` (the value provided by libuv API is a constant of _65536_ bytes)
256  or with whatever size, as long as it’s > 0.
257  \sa libuv API documentation: [`uv_alloc_cb`](http://docs.libuv.org/en/v1.x/handle.html#c.uv_alloc_cb).
258  \details The following is an example of the trivial callback that is ready for general use:
259  ```
260  buffer alloc_cb(handle, std::size_t _suggested_size)
261  {
262  return buffer{ _suggested_size };
263  }
264  ```
265  */
266 using on_buffer_alloc_t = std::function< buffer(handle _handle, std::size_t _suggested_size) >;
267 
268 
269 }
270 
271 
272 namespace std
273 {
274 
275 //! \ingroup doxy_buffer
276 template<> inline void swap(uv::buffer &_this, uv::buffer &_that) noexcept { _this.swap(_that); }
277 
278 }
279 
280 
281 #endif
Namespace for all uvcc definitions.
Definition: buffer.hpp:17
operator bool() const noexcept
Equivalent to (base() != nullptr).
Definition: buffer.hpp:248
A wrapper providing the feature of being a standard layout type for the given type _T_...
Definition: utility.hpp:475
buffer(const std::initializer_list< std::size_t > &_len_values)
Create an array of initialized uv_buf_t buffer describing structures.
Definition: buffer.hpp:199
std::size_t count() const noexcept
The number of the uv_buf_t structures in the array.
Definition: buffer.hpp:235
decltype(uv_t::len) & len(const std::size_t _i=0) const noexcept
The .len field of the _i-th buffer structure.
Definition: buffer.hpp:242
uv_t & operator[](const std::size_t _i) const noexcept
Access to the _i-th uv_buf_t buffer structure in the array.
Definition: buffer.hpp:238
The base class for the libuv handles.
Definition: handle-base.hpp:33
long nrefs() const noexcept
The current number of existing references to the same buffer as this variable refers to...
Definition: buffer.hpp:230
A reference counter with atomic increment/decrement.
Definition: utility.hpp:229
decltype(uv_t::base) & base(const std::size_t _i=0) const noexcept
The .base field of the _i-th buffer structure.
Definition: buffer.hpp:240
buffer()
Create a single uv_buf_t null-initialized buffer structure.
Definition: buffer.hpp:180