]> git.proxmox.com Git - ceph.git/blob - ceph/src/Beast/include/beast/core/impl/streambuf.ipp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / Beast / include / beast / core / impl / streambuf.ipp
1 //
2 // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
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)
6 //
7
8 #ifndef BEAST_IMPL_STREAMBUF_IPP
9 #define BEAST_IMPL_STREAMBUF_IPP
10
11 #include <beast/core/detail/type_traits.hpp>
12 #include <beast/core/detail/write_dynabuf.hpp>
13 #include <boost/assert.hpp>
14 #include <algorithm>
15 #include <exception>
16 #include <sstream>
17 #include <string>
18 #include <utility>
19
20 namespace beast {
21
22 /* These diagrams illustrate the layout and state variables.
23
24 1 Input and output contained entirely in one element:
25
26 0 out_
27 |<-------------+------------------------------------------->|
28 in_pos_ out_pos_ out_end_
29
30
31 2 Output contained in first and second elements:
32
33 out_
34 |<------+----------+------->| |<----------+-------------->|
35 in_pos_ out_pos_ out_end_
36
37
38 3 Output contained in the second element:
39
40 out_
41 |<------------+------------>| |<----+-------------------->|
42 in_pos_ out_pos_ out_end_
43
44
45 4 Output contained in second and third elements:
46
47 out_
48 |<-----+-------->| |<-------+------>| |<--------------->|
49 in_pos_ out_pos_ out_end_
50
51
52 5 Input sequence is empty:
53
54 out_
55 |<------+------------------>| |<-----------+------------->|
56 out_pos_ out_end_
57 in_pos_
58
59
60 6 Output sequence is empty:
61
62 out_
63 |<------+------------------>| |<------+------------------>|
64 in_pos_ out_pos_
65 out_end_
66
67
68 7 The end of output can point to the end of an element.
69 But out_pos_ should never point to the end:
70
71 out_
72 |<------+------------------>| |<------+------------------>|
73 in_pos_ out_pos_ out_end_
74
75
76 8 When the input sequence entirely fills the last element and
77 the output sequence is empty, out_ will point to the end of
78 the list of buffers, and out_pos_ and out_end_ will be 0:
79
80
81 |<------+------------------>| out_ == list_.end()
82 in_pos_ out_pos_ == 0
83 out_end_ == 0
84 */
85
86 template<class Allocator>
87 class basic_streambuf<Allocator>::element
88 : public boost::intrusive::list_base_hook<
89 boost::intrusive::link_mode<
90 boost::intrusive::normal_link>>
91 {
92 using size_type = typename std::allocator_traits<Allocator>::size_type;
93
94 size_type const size_;
95
96 public:
97 element(element const&) = delete;
98 element& operator=(element const&) = delete;
99
100 explicit
101 element(size_type n)
102 : size_(n)
103 {
104 }
105
106 size_type
107 size() const
108 {
109 return size_;
110 }
111
112 char*
113 data() const
114 {
115 return const_cast<char*>(
116 reinterpret_cast<char const*>(this+1));
117 }
118 };
119
120 template<class Allocator>
121 class basic_streambuf<Allocator>::const_buffers_type
122 {
123 basic_streambuf const* sb_;
124
125 friend class basic_streambuf;
126
127 explicit
128 const_buffers_type(basic_streambuf const& sb);
129
130 public:
131 // Why?
132 using value_type = boost::asio::const_buffer;
133
134 class const_iterator;
135
136 const_buffers_type() = delete;
137 const_buffers_type(const_buffers_type const&) = default;
138 const_buffers_type& operator=(const_buffers_type const&) = default;
139
140 const_iterator
141 begin() const;
142
143 const_iterator
144 end() const;
145 };
146
147 template<class Allocator>
148 class basic_streambuf<Allocator>::mutable_buffers_type
149 {
150 basic_streambuf const* sb_;
151
152 friend class basic_streambuf;
153
154 explicit
155 mutable_buffers_type(basic_streambuf const& sb);
156
157 public:
158 using value_type = mutable_buffer;
159
160 class const_iterator;
161
162 mutable_buffers_type() = delete;
163 mutable_buffers_type(mutable_buffers_type const&) = default;
164 mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
165
166 const_iterator
167 begin() const;
168
169 const_iterator
170 end() const;
171 };
172
173 //------------------------------------------------------------------------------
174
175 template<class Allocator>
176 class basic_streambuf<Allocator>::const_buffers_type::const_iterator
177 {
178 basic_streambuf const* sb_ = nullptr;
179 typename list_type::const_iterator it_;
180
181 public:
182 using value_type =
183 typename const_buffers_type::value_type;
184 using pointer = value_type const*;
185 using reference = value_type;
186 using difference_type = std::ptrdiff_t;
187 using iterator_category =
188 std::bidirectional_iterator_tag;
189
190 const_iterator() = default;
191 const_iterator(const_iterator&& other) = default;
192 const_iterator(const_iterator const& other) = default;
193 const_iterator& operator=(const_iterator&& other) = default;
194 const_iterator& operator=(const_iterator const& other) = default;
195
196 const_iterator(basic_streambuf const& sb,
197 typename list_type::const_iterator const& it)
198 : sb_(&sb)
199 , it_(it)
200 {
201 }
202
203 bool
204 operator==(const_iterator const& other) const
205 {
206 return sb_ == other.sb_ && it_ == other.it_;
207 }
208
209 bool
210 operator!=(const_iterator const& other) const
211 {
212 return !(*this == other);
213 }
214
215 reference
216 operator*() const
217 {
218 auto const& e = *it_;
219 return value_type{e.data(),
220 (sb_->out_ == sb_->list_.end() ||
221 &e != &*sb_->out_) ? e.size() : sb_->out_pos_} +
222 (&e == &*sb_->list_.begin() ? sb_->in_pos_ : 0);
223 }
224
225 pointer
226 operator->() const = delete;
227
228 const_iterator&
229 operator++()
230 {
231 ++it_;
232 return *this;
233 }
234
235 const_iterator
236 operator++(int)
237 {
238 auto temp = *this;
239 ++(*this);
240 return temp;
241 }
242
243 const_iterator&
244 operator--()
245 {
246 --it_;
247 return *this;
248 }
249
250 const_iterator
251 operator--(int)
252 {
253 auto temp = *this;
254 --(*this);
255 return temp;
256 }
257 };
258
259 template<class Allocator>
260 basic_streambuf<Allocator>::const_buffers_type::const_buffers_type(
261 basic_streambuf const& sb)
262 : sb_(&sb)
263 {
264 }
265
266 template<class Allocator>
267 auto
268 basic_streambuf<Allocator>::const_buffers_type::begin() const ->
269 const_iterator
270 {
271 return const_iterator{*sb_, sb_->list_.begin()};
272 }
273
274 template<class Allocator>
275 auto
276 basic_streambuf<Allocator>::const_buffers_type::end() const ->
277 const_iterator
278 {
279 return const_iterator{*sb_, sb_->out_ ==
280 sb_->list_.end() ? sb_->list_.end() :
281 std::next(sb_->out_)};
282 }
283
284 //------------------------------------------------------------------------------
285
286 template<class Allocator>
287 class basic_streambuf<Allocator>::mutable_buffers_type::const_iterator
288 {
289 basic_streambuf const* sb_ = nullptr;
290 typename list_type::const_iterator it_;
291
292 public:
293 using value_type =
294 typename mutable_buffers_type::value_type;
295 using pointer = value_type const*;
296 using reference = value_type;
297 using difference_type = std::ptrdiff_t;
298 using iterator_category =
299 std::bidirectional_iterator_tag;
300
301 const_iterator() = default;
302 const_iterator(const_iterator&& other) = default;
303 const_iterator(const_iterator const& other) = default;
304 const_iterator& operator=(const_iterator&& other) = default;
305 const_iterator& operator=(const_iterator const& other) = default;
306
307 const_iterator(basic_streambuf const& sb,
308 typename list_type::const_iterator const& it)
309 : sb_(&sb)
310 , it_(it)
311 {
312 }
313
314 bool
315 operator==(const_iterator const& other) const
316 {
317 return sb_ == other.sb_ && it_ == other.it_;
318 }
319
320 bool
321 operator!=(const_iterator const& other) const
322 {
323 return !(*this == other);
324 }
325
326 reference
327 operator*() const
328 {
329 auto const& e = *it_;
330 return value_type{e.data(),
331 &e == &*std::prev(sb_->list_.end()) ?
332 sb_->out_end_ : e.size()} +
333 (&e == &*sb_->out_ ? sb_->out_pos_ : 0);
334 }
335
336 pointer
337 operator->() const = delete;
338
339 const_iterator&
340 operator++()
341 {
342 ++it_;
343 return *this;
344 }
345
346 const_iterator
347 operator++(int)
348 {
349 auto temp = *this;
350 ++(*this);
351 return temp;
352 }
353
354 const_iterator&
355 operator--()
356 {
357 --it_;
358 return *this;
359 }
360
361 const_iterator
362 operator--(int)
363 {
364 auto temp = *this;
365 --(*this);
366 return temp;
367 }
368 };
369
370 template<class Allocator>
371 basic_streambuf<Allocator>::mutable_buffers_type::mutable_buffers_type(
372 basic_streambuf const& sb)
373 : sb_(&sb)
374 {
375 }
376
377 template<class Allocator>
378 auto
379 basic_streambuf<Allocator>::mutable_buffers_type::begin() const ->
380 const_iterator
381 {
382 return const_iterator{*sb_, sb_->out_};
383 }
384
385 template<class Allocator>
386 auto
387 basic_streambuf<Allocator>::mutable_buffers_type::end() const ->
388 const_iterator
389 {
390 return const_iterator{*sb_, sb_->list_.end()};
391 }
392
393 //------------------------------------------------------------------------------
394
395 template<class Allocator>
396 basic_streambuf<Allocator>::~basic_streambuf()
397 {
398 delete_list();
399 }
400
401 template<class Allocator>
402 basic_streambuf<Allocator>::
403 basic_streambuf(basic_streambuf&& other)
404 : detail::empty_base_optimization<allocator_type>(
405 std::move(other.member()))
406 , alloc_size_(other.alloc_size_)
407 , in_size_(other.in_size_)
408 , in_pos_(other.in_pos_)
409 , out_pos_(other.out_pos_)
410 , out_end_(other.out_end_)
411 {
412 auto const at_end =
413 other.out_ == other.list_.end();
414 list_ = std::move(other.list_);
415 out_ = at_end ? list_.end() : other.out_;
416 other.in_size_ = 0;
417 other.out_ = other.list_.end();
418 other.in_pos_ = 0;
419 other.out_pos_ = 0;
420 other.out_end_ = 0;
421 }
422
423 template<class Allocator>
424 basic_streambuf<Allocator>::
425 basic_streambuf(basic_streambuf&& other,
426 allocator_type const& alloc)
427 : basic_streambuf(other.alloc_size_, alloc)
428 {
429 using boost::asio::buffer_copy;
430 if(this->member() != other.member())
431 commit(buffer_copy(prepare(other.size()), other.data()));
432 else
433 move_assign(other, std::true_type{});
434 }
435
436 template<class Allocator>
437 auto
438 basic_streambuf<Allocator>::operator=(
439 basic_streambuf&& other) -> basic_streambuf&
440 {
441 if(this == &other)
442 return *this;
443 // VFALCO If any memory allocated we could use it first?
444 clear();
445 alloc_size_ = other.alloc_size_;
446 move_assign(other, std::integral_constant<bool,
447 alloc_traits::propagate_on_container_move_assignment::value>{});
448 return *this;
449 }
450
451 template<class Allocator>
452 basic_streambuf<Allocator>::
453 basic_streambuf(basic_streambuf const& other)
454 : basic_streambuf(other.alloc_size_,
455 alloc_traits::select_on_container_copy_construction(other.member()))
456 {
457 commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
458 }
459
460 template<class Allocator>
461 basic_streambuf<Allocator>::
462 basic_streambuf(basic_streambuf const& other,
463 allocator_type const& alloc)
464 : basic_streambuf(other.alloc_size_, alloc)
465 {
466 commit(boost::asio::buffer_copy(prepare(other.size()), other.data()));
467 }
468
469 template<class Allocator>
470 auto
471 basic_streambuf<Allocator>::operator=(
472 basic_streambuf const& other) ->
473 basic_streambuf&
474 {
475 if(this == &other)
476 return *this;
477 using boost::asio::buffer_copy;
478 clear();
479 copy_assign(other, std::integral_constant<bool,
480 alloc_traits::propagate_on_container_copy_assignment::value>{});
481 commit(buffer_copy(prepare(other.size()), other.data()));
482 return *this;
483 }
484
485 template<class Allocator>
486 template<class OtherAlloc>
487 basic_streambuf<Allocator>::basic_streambuf(
488 basic_streambuf<OtherAlloc> const& other)
489 : basic_streambuf(other.alloc_size_)
490 {
491 using boost::asio::buffer_copy;
492 commit(buffer_copy(prepare(other.size()), other.data()));
493 }
494
495 template<class Allocator>
496 template<class OtherAlloc>
497 basic_streambuf<Allocator>::basic_streambuf(
498 basic_streambuf<OtherAlloc> const& other,
499 allocator_type const& alloc)
500 : basic_streambuf(other.alloc_size_, alloc)
501 {
502 using boost::asio::buffer_copy;
503 commit(buffer_copy(prepare(other.size()), other.data()));
504 }
505
506 template<class Allocator>
507 template<class OtherAlloc>
508 auto
509 basic_streambuf<Allocator>::operator=(
510 basic_streambuf<OtherAlloc> const& other) ->
511 basic_streambuf&
512 {
513 using boost::asio::buffer_copy;
514 clear();
515 commit(buffer_copy(prepare(other.size()), other.data()));
516 return *this;
517 }
518
519 template<class Allocator>
520 basic_streambuf<Allocator>::basic_streambuf(
521 std::size_t alloc_size, Allocator const& alloc)
522 : detail::empty_base_optimization<allocator_type>(alloc)
523 , out_(list_.end())
524 , alloc_size_(alloc_size)
525 {
526 if(alloc_size <= 0)
527 throw detail::make_exception<std::invalid_argument>(
528 "invalid alloc_size", __FILE__, __LINE__);
529 }
530
531 template<class Allocator>
532 std::size_t
533 basic_streambuf<Allocator>::capacity() const
534 {
535 auto pos = out_;
536 if(pos == list_.end())
537 return in_size_;
538 auto n = pos->size() - out_pos_;
539 while(++pos != list_.end())
540 n += pos->size();
541 return in_size_ + n;
542 }
543
544 template<class Allocator>
545 auto
546 basic_streambuf<Allocator>::
547 data() const ->
548 const_buffers_type
549 {
550 return const_buffers_type(*this);
551 }
552
553 template<class Allocator>
554 auto
555 basic_streambuf<Allocator>::prepare(size_type n) ->
556 mutable_buffers_type
557 {
558 list_type reuse;
559 if(out_ != list_.end())
560 {
561 if(out_ != list_.iterator_to(list_.back()))
562 {
563 out_end_ = out_->size();
564 reuse.splice(reuse.end(), list_,
565 std::next(out_), list_.end());
566 debug_check();
567 }
568 auto const avail = out_->size() - out_pos_;
569 if(n > avail)
570 {
571 out_end_ = out_->size();
572 n -= avail;
573 }
574 else
575 {
576 out_end_ = out_pos_ + n;
577 n = 0;
578 }
579 debug_check();
580 }
581 while(n > 0 && ! reuse.empty())
582 {
583 auto& e = reuse.front();
584 reuse.erase(reuse.iterator_to(e));
585 list_.push_back(e);
586 if(n > e.size())
587 {
588 out_end_ = e.size();
589 n -= e.size();
590 }
591 else
592 {
593 out_end_ = n;
594 n = 0;
595 }
596 debug_check();
597 }
598 while(n > 0)
599 {
600 auto const size = std::max(alloc_size_, n);
601 auto& e = *reinterpret_cast<element*>(static_cast<
602 void*>(alloc_traits::allocate(this->member(),
603 sizeof(element) + size)));
604 alloc_traits::construct(this->member(), &e, size);
605 list_.push_back(e);
606 if(out_ == list_.end())
607 out_ = list_.iterator_to(e);
608 if(n >= e.size())
609 {
610 out_end_ = e.size();
611 n -= e.size();
612 }
613 else
614 {
615 out_end_ = n;
616 n = 0;
617 }
618 debug_check();
619 }
620 for(auto it = reuse.begin(); it != reuse.end();)
621 {
622 auto& e = *it++;
623 reuse.erase(list_.iterator_to(e));
624 auto const len = e.size() + sizeof(e);
625 alloc_traits::destroy(this->member(), &e);
626 alloc_traits::deallocate(this->member(),
627 reinterpret_cast<char*>(&e), len);
628 }
629 return mutable_buffers_type(*this);
630 }
631
632 template<class Allocator>
633 void
634 basic_streambuf<Allocator>::commit(size_type n)
635 {
636 if(list_.empty())
637 return;
638 if(out_ == list_.end())
639 return;
640 auto const back =
641 list_.iterator_to(list_.back());
642 while(out_ != back)
643 {
644 auto const avail =
645 out_->size() - out_pos_;
646 if(n < avail)
647 {
648 out_pos_ += n;
649 in_size_ += n;
650 debug_check();
651 return;
652 }
653 ++out_;
654 n -= avail;
655 out_pos_ = 0;
656 in_size_ += avail;
657 debug_check();
658 }
659
660 n = (std::min)(n, out_end_ - out_pos_);
661 out_pos_ += n;
662 in_size_ += n;
663 if(out_pos_ == out_->size())
664 {
665 ++out_;
666 out_pos_ = 0;
667 out_end_ = 0;
668 }
669 debug_check();
670 }
671
672 template<class Allocator>
673 void
674 basic_streambuf<Allocator>::consume(size_type n)
675 {
676 if(list_.empty())
677 return;
678
679 for(;;)
680 {
681 if(list_.begin() != out_)
682 {
683 auto const avail = list_.front().size() - in_pos_;
684 if(n < avail)
685 {
686 in_size_ -= n;
687 in_pos_ += n;
688 debug_check();
689 break;
690 }
691 n -= avail;
692 in_size_ -= avail;
693 in_pos_ = 0;
694 auto& e = list_.front();
695 list_.erase(list_.iterator_to(e));
696 auto const len = e.size() + sizeof(e);
697 alloc_traits::destroy(this->member(), &e);
698 alloc_traits::deallocate(this->member(),
699 reinterpret_cast<char*>(&e), len);
700 debug_check();
701 }
702 else
703 {
704 auto const avail = out_pos_ - in_pos_;
705 if(n < avail)
706 {
707 in_size_ -= n;
708 in_pos_ += n;
709 }
710 else
711 {
712 in_size_ = 0;
713 if(out_ != list_.iterator_to(list_.back()) ||
714 out_pos_ != out_end_)
715 {
716 in_pos_ = out_pos_;
717 }
718 else
719 {
720 // Input and output sequences are empty, reuse buffer.
721 // Alternatively we could deallocate it.
722 in_pos_ = 0;
723 out_pos_ = 0;
724 out_end_ = 0;
725 }
726 }
727 debug_check();
728 break;
729 }
730 }
731 }
732
733 template<class Allocator>
734 void
735 basic_streambuf<Allocator>::
736 clear()
737 {
738 delete_list();
739 list_.clear();
740 out_ = list_.begin();
741 in_size_ = 0;
742 in_pos_ = 0;
743 out_pos_ = 0;
744 out_end_ = 0;
745 }
746
747 template<class Allocator>
748 void
749 basic_streambuf<Allocator>::
750 move_assign(basic_streambuf& other, std::false_type)
751 {
752 using boost::asio::buffer_copy;
753 if(this->member() != other.member())
754 {
755 commit(buffer_copy(prepare(other.size()), other.data()));
756 other.clear();
757 }
758 else
759 move_assign(other, std::true_type{});
760 }
761
762 template<class Allocator>
763 void
764 basic_streambuf<Allocator>::
765 move_assign(basic_streambuf& other, std::true_type)
766 {
767 this->member() = std::move(other.member());
768 auto const at_end =
769 other.out_ == other.list_.end();
770 list_ = std::move(other.list_);
771 out_ = at_end ? list_.end() : other.out_;
772
773 in_size_ = other.in_size_;
774 in_pos_ = other.in_pos_;
775 out_pos_ = other.out_pos_;
776 out_end_ = other.out_end_;
777
778 other.in_size_ = 0;
779 other.out_ = other.list_.end();
780 other.in_pos_ = 0;
781 other.out_pos_ = 0;
782 other.out_end_ = 0;
783 }
784
785 template<class Allocator>
786 void
787 basic_streambuf<Allocator>::
788 copy_assign(basic_streambuf const& other, std::false_type)
789 {
790 beast::detail::ignore_unused(other);
791 }
792
793 template<class Allocator>
794 void
795 basic_streambuf<Allocator>::
796 copy_assign(basic_streambuf const& other, std::true_type)
797 {
798 this->member() = other.member();
799 }
800
801 template<class Allocator>
802 void
803 basic_streambuf<Allocator>::delete_list()
804 {
805 for(auto iter = list_.begin(); iter != list_.end();)
806 {
807 auto& e = *iter++;
808 auto const n = e.size() + sizeof(e);
809 alloc_traits::destroy(this->member(), &e);
810 alloc_traits::deallocate(this->member(),
811 reinterpret_cast<char*>(&e), n);
812 }
813 }
814
815 template<class Allocator>
816 void
817 basic_streambuf<Allocator>::debug_check() const
818 {
819 #ifndef NDEBUG
820 using boost::asio::buffer_size;
821 BOOST_ASSERT(buffer_size(data()) == in_size_);
822 if(list_.empty())
823 {
824 BOOST_ASSERT(in_pos_ == 0);
825 BOOST_ASSERT(in_size_ == 0);
826 BOOST_ASSERT(out_pos_ == 0);
827 BOOST_ASSERT(out_end_ == 0);
828 BOOST_ASSERT(out_ == list_.end());
829 return;
830 }
831
832 auto const& front = list_.front();
833
834 BOOST_ASSERT(in_pos_ < front.size());
835
836 if(out_ == list_.end())
837 {
838 BOOST_ASSERT(out_pos_ == 0);
839 BOOST_ASSERT(out_end_ == 0);
840 }
841 else
842 {
843 auto const& out = *out_;
844 auto const& back = list_.back();
845
846 BOOST_ASSERT(out_end_ <= back.size());
847 BOOST_ASSERT(out_pos_ < out.size());
848 BOOST_ASSERT(&out != &front || out_pos_ >= in_pos_);
849 BOOST_ASSERT(&out != &front || out_pos_ - in_pos_ == in_size_);
850 BOOST_ASSERT(&out != &back || out_pos_ <= out_end_);
851 }
852 #endif
853 }
854
855 template<class Allocator>
856 std::size_t
857 read_size_helper(basic_streambuf<
858 Allocator> const& streambuf, std::size_t max_size)
859 {
860 BOOST_ASSERT(max_size >= 1);
861 // If we already have an allocated
862 // buffer, try to fill that up first
863 auto const avail = streambuf.capacity() - streambuf.size();
864 if (avail > 0)
865 return (std::min)(avail, max_size);
866 // Try to have just one new block allocated
867 constexpr std::size_t low = 512;
868 if (streambuf.alloc_size_ > low)
869 return (std::min)(max_size, streambuf.alloc_size_);
870 // ...but enforce a 512 byte minimum.
871 return (std::min)(max_size, low);
872 }
873
874 template<class Alloc, class T>
875 basic_streambuf<Alloc>&
876 operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
877 {
878 detail::write_dynabuf(streambuf, t);
879 return streambuf;
880 }
881
882 } // beast
883
884 #endif