1//
2// detail/consuming_buffers.hpp
3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4//
5// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6//
7// Distributed under the Boost Software License, Version 1.0. (See accompanying
8// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9//
10
11#ifndef ASIO_DETAIL_CONSUMING_BUFFERS_HPP
12#define ASIO_DETAIL_CONSUMING_BUFFERS_HPP
13
14
15#include "asio/detail/config.hpp"
16#include <cstddef>
17#include <iterator>
18#include "asio/buffer.hpp"
19#include "asio/detail/limits.hpp"
20
21#include "asio/detail/push_options.hpp"
22
23namespace asio {
24namespace detail {
25
26// A proxy iterator for a sub-range in a list of buffers.
27template <typename Buffer, typename Buffer_Iterator>
28class consuming_buffers_iterator
29{
30public:
31  /// The type used for the distance between two iterators.
32  typedef std::ptrdiff_t difference_type;
33
34  /// The type of the value pointed to by the iterator.
35  typedef Buffer value_type;
36
37  /// The type of the result of applying operator->() to the iterator.
38  typedef const Buffer* pointer;
39
40  /// The type of the result of applying operator*() to the iterator.
41  typedef const Buffer& reference;
42
43  /// The iterator category.
44  typedef std::forward_iterator_tag iterator_category;
45
46  // Default constructor creates an end iterator.
47  consuming_buffers_iterator()
48    : at_end_(true)
49  {
50  }
51
52  // Construct with a buffer for the first entry and an iterator
53  // range for the remaining entries.
54  consuming_buffers_iterator(bool at_end, const Buffer& first,
55      Buffer_Iterator begin_remainder, Buffer_Iterator end_remainder,
56      std::size_t max_size)
57    : at_end_(max_size > 0 ? at_end : true),
58      first_(buffer(first, max_size)),
59      begin_remainder_(begin_remainder),
60      end_remainder_(end_remainder),
61      offset_(0),
62      max_size_(max_size)
63  {
64  }
65
66  // Dereference an iterator.
67  const Buffer& operator*() const
68  {
69    return dereference();
70  }
71
72  // Dereference an iterator.
73  const Buffer* operator->() const
74  {
75    return &dereference();
76  }
77
78  // Increment operator (prefix).
79  consuming_buffers_iterator& operator++()
80  {
81    increment();
82    return *this;
83  }
84
85  // Increment operator (postfix).
86  consuming_buffers_iterator operator++(int)
87  {
88    consuming_buffers_iterator tmp(*this);
89    ++*this;
90    return tmp;
91  }
92
93  // Test two iterators for equality.
94  friend bool operator==(const consuming_buffers_iterator& a,
95      const consuming_buffers_iterator& b)
96  {
97    return a.equal(b);
98  }
99
100  // Test two iterators for inequality.
101  friend bool operator!=(const consuming_buffers_iterator& a,
102      const consuming_buffers_iterator& b)
103  {
104    return !a.equal(b);
105  }
106
107private:
108  void increment()
109  {
110    if (!at_end_)
111    {
112      if (begin_remainder_ == end_remainder_
113          || offset_ + buffer_size(first_) >= max_size_)
114      {
115        at_end_ = true;
116      }
117      else
118      {
119        offset_ += buffer_size(first_);
120        first_ = buffer(*begin_remainder_++, max_size_ - offset_);
121      }
122    }
123  }
124
125  bool equal(const consuming_buffers_iterator& other) const
126  {
127    if (at_end_ && other.at_end_)
128      return true;
129    return !at_end_ && !other.at_end_
130      && buffer_cast<const void*>(first_)
131        == buffer_cast<const void*>(other.first_)
132      && buffer_size(first_) == buffer_size(other.first_)
133      && begin_remainder_ == other.begin_remainder_
134      && end_remainder_ == other.end_remainder_;
135  }
136
137  const Buffer& dereference() const
138  {
139    return first_;
140  }
141
142  bool at_end_;
143  Buffer first_;
144  Buffer_Iterator begin_remainder_;
145  Buffer_Iterator end_remainder_;
146  std::size_t offset_;
147  std::size_t max_size_;
148};
149
150// A proxy for a sub-range in a list of buffers.
151template <typename Buffer, typename Buffers>
152class consuming_buffers
153{
154public:
155  // The type for each element in the list of buffers.
156  typedef Buffer value_type;
157
158  // A forward-only iterator type that may be used to read elements.
159  typedef consuming_buffers_iterator<Buffer, typename Buffers::const_iterator>
160    const_iterator;
161
162  // Construct to represent the entire list of buffers.
163  consuming_buffers(const Buffers& buffers)
164    : buffers_(buffers),
165      at_end_(buffers_.begin() == buffers_.end()),
166      begin_remainder_(buffers_.begin()),
167      max_size_((std::numeric_limits<std::size_t>::max)())
168  {
169    if (!at_end_)
170    {
171      first_ = *buffers_.begin();
172      ++begin_remainder_;
173    }
174  }
175
176  // Copy constructor.
177  consuming_buffers(const consuming_buffers& other)
178    : buffers_(other.buffers_),
179      at_end_(other.at_end_),
180      first_(other.first_),
181      begin_remainder_(buffers_.begin()),
182      max_size_(other.max_size_)
183  {
184    typename Buffers::const_iterator first = other.buffers_.begin();
185    typename Buffers::const_iterator second = other.begin_remainder_;
186    std::advance(begin_remainder_, std::distance(first, second));
187  }
188
189  // Assignment operator.
190  consuming_buffers& operator=(const consuming_buffers& other)
191  {
192    buffers_ = other.buffers_;
193    at_end_ = other.at_end_;
194    first_ = other.first_;
195    begin_remainder_ = buffers_.begin();
196    typename Buffers::const_iterator first = other.buffers_.begin();
197    typename Buffers::const_iterator second = other.begin_remainder_;
198    std::advance(begin_remainder_, std::distance(first, second));
199    max_size_ = other.max_size_;
200    return *this;
201  }
202
203  // Get a forward-only iterator to the first element.
204  const_iterator begin() const
205  {
206    return const_iterator(at_end_, first_,
207        begin_remainder_, buffers_.end(), max_size_);
208  }
209
210  // Get a forward-only iterator for one past the last element.
211  const_iterator end() const
212  {
213    return const_iterator();
214  }
215
216  // Set the maximum size for a single transfer.
217  void prepare(std::size_t max_size)
218  {
219    max_size_ = max_size;
220  }
221
222  // Consume the specified number of bytes from the buffers.
223  void consume(std::size_t size)
224  {
225    // Remove buffers from the start until the specified size is reached.
226    while (size > 0 && !at_end_)
227    {
228      if (buffer_size(first_) <= size)
229      {
230        size -= buffer_size(first_);
231        if (begin_remainder_ == buffers_.end())
232          at_end_ = true;
233        else
234          first_ = *begin_remainder_++;
235      }
236      else
237      {
238        first_ = first_ + size;
239        size = 0;
240      }
241    }
242
243    // Remove any more empty buffers at the start.
244    while (!at_end_ && buffer_size(first_) == 0)
245    {
246      if (begin_remainder_ == buffers_.end())
247        at_end_ = true;
248      else
249        first_ = *begin_remainder_++;
250    }
251  }
252
253private:
254  Buffers buffers_;
255  bool at_end_;
256  Buffer first_;
257  typename Buffers::const_iterator begin_remainder_;
258  std::size_t max_size_;
259};
260
261// Specialisation for null_buffers to ensure that the null_buffers type is
262// always passed through to the underlying read or write operation.
263template <typename Buffer>
264class consuming_buffers<Buffer, asio::null_buffers>
265  : public asio::null_buffers
266{
267public:
268  consuming_buffers(const asio::null_buffers&)
269  {
270    // No-op.
271  }
272
273  void prepare(std::size_t)
274  {
275    // No-op.
276  }
277
278  void consume(std::size_t)
279  {
280    // No-op.
281  }
282};
283
284} // namespace detail
285} // namespace asio
286
287#include "asio/detail/pop_options.hpp"
288
289#endif // ASIO_DETAIL_CONSUMING_BUFFERS_HPP
290