SeqAn3  3.2.0
The Modern C++ library for sequence analysis.
fast_ostreambuf_iterator.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2022, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2022, Knut Reinert & MPI für molekulare Genetik
4 // This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5 // shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6 // -----------------------------------------------------------------------------------------------------
7 
14 #pragma once
15 
16 #include <algorithm>
17 #include <cassert>
18 #include <iterator>
19 #include <ranges>
20 #include <seqan3/std/charconv>
21 
23 
24 namespace seqan3::detail
25 {
38 template <typename char_t, typename traits_t = std::char_traits<char_t>>
39 class fast_ostreambuf_iterator
40 {
41 private:
43  stream_buffer_exposer<char_t, traits_t> * stream_buf = nullptr;
44 
45 public:
49  using difference_type = ptrdiff_t;
50  using value_type = char_t;
51  using reference = char_t;
52  using pointer = void;
53  using iterator_category = std::output_iterator_tag;
55 
59  fast_ostreambuf_iterator() noexcept = default;
60  fast_ostreambuf_iterator(fast_ostreambuf_iterator const &) noexcept = default;
61  fast_ostreambuf_iterator(fast_ostreambuf_iterator &&) noexcept = default;
62  fast_ostreambuf_iterator & operator=(fast_ostreambuf_iterator const &) noexcept = default;
63  fast_ostreambuf_iterator & operator=(fast_ostreambuf_iterator &&) noexcept = default;
64  ~fast_ostreambuf_iterator() noexcept = default;
65 
67  explicit fast_ostreambuf_iterator(std::basic_streambuf<char_t, traits_t> & ibuf) :
68  stream_buf{reinterpret_cast<stream_buffer_exposer<char_t, traits_t> *>(&ibuf)}
69  {
70  assert(stream_buf != nullptr);
71  if (stream_buf->pptr() == stream_buf->epptr())
72  stream_buf->overflow(); // ensures that put area has space available
73  }
75 
80  fast_ostreambuf_iterator & operator++()
81  {
82  return *this;
83  }
85  fast_ostreambuf_iterator & operator++(int)
86  {
87  return *this;
88  }
90 
92  fast_ostreambuf_iterator & operator*()
93  {
94  return *this;
95  }
96 
98  fast_ostreambuf_iterator & operator=(char_t const c)
99  {
100  assert(stream_buf != nullptr);
101  if (stream_buf->pptr() == stream_buf->epptr())
102  {
103  if (stream_buf->sputc(c) == traits_t::eof()) // overflow() [virtual], then write character
104  {
105  // LCOV_EXCL_START
106  throw std::ios_base::failure{"Cannot write to output stream (reached traits::eof() condition)."};
107  // LCOV_EXCL_STOP
108  }
109  }
110  else
111  {
112  *stream_buf->pptr() = c;
113  stream_buf->pbump(1); // advance pptr() in put area without any checks
114  }
115  return *this;
116  }
117 
119  bool failed() const noexcept
120  {
121  return stream_buf->overflow() == traits_t::eof();
122  }
123 
142  template <std::ranges::forward_range range_type>
143  requires std::ranges::borrowed_range<range_type>
144  auto write_range(range_type && rng)
145  {
146  using sen_t = std::ranges::sentinel_t<range_type>;
147  using it_t = std::ranges::iterator_t<range_type>;
148 
149  it_t it = std::ranges::begin(rng);
150  sen_t end = std::ranges::end(rng);
151 
152  while (it != end)
153  {
154  size_t const buffer_space = stream_buf->epptr() - stream_buf->pptr();
155 
156  if constexpr (std::ranges::sized_range<range_type>)
157  {
158  size_t const characters_to_write = std::min<size_t>(std::ranges::distance(it, end), buffer_space);
159  auto copy_res = std::ranges::copy_n(it, characters_to_write, stream_buf->pptr());
160  it = copy_res.in;
161  stream_buf->pbump(characters_to_write);
162  }
163  else
164  {
165  size_t i = 0;
166  for (; it != end && i < buffer_space; ++it, ++i)
167  *stream_buf->pptr() = *it;
168  stream_buf->pbump(i);
169  }
170 
171  if (it == end) // no more characters to write
172  return it;
173 
174  // Push one more character and flush
175  if (stream_buf->overflow(*it) == traits_t::eof())
176  {
177  // LCOV_EXCL_START
178  throw std::ios_base::failure{"Cannot write to output stream (reached traits::eof() condition)."};
179  // LCOV_EXCL_STOP
180  }
181 
182  ++it; // drop 1 character that has been written in overflow()
183  }
184 
185  return it;
186  }
187 
189  // overload for non-std::ranges::borrowed_range types that return void
190  template <std::ranges::forward_range range_type>
191  void write_range(range_type && rng)
192  {
193  write_range(rng); // lvalue is always a safe range. return value is ignored because iterator would be dangling
194  }
196 
201  template <typename number_type>
202  requires std::is_arithmetic_v<number_type>
203  auto write_number(number_type num)
204  {
205  if (stream_buf->epptr() - stream_buf->pptr() > 300) // enough space for any number, should be likely
206  {
207  auto res = std::to_chars(stream_buf->pptr(), stream_buf->epptr(), num);
208  stream_buf->pbump(res.ptr - stream_buf->pptr()); // advance pptr
209  }
210  else
211  {
212  std::array<char, 300> arithmetic_buffer{};
213  auto res = std::to_chars(&arithmetic_buffer[0], &arithmetic_buffer[0] + sizeof(arithmetic_buffer), num);
214  write_range(std::ranges::subrange<char *, char *>(&arithmetic_buffer[0], res.ptr));
215  }
216  }
217 
222  void write_end_of_line(bool const add_cr)
223  {
224  if (add_cr)
225  *this = '\r';
226  *this = '\n';
227  }
228 };
229 
230 } // namespace seqan3::detail
The <charconv> header from C++17's standard library.
T end(T... args)
requires requires
The rank_type of the semi-alphabet; defined as the return type of seqan3::to_rank....
Definition: alphabet/concept.hpp:164
SeqAn specific customisations in the standard namespace.
The <ranges> header from C++20's standard library.
Provides seqan3::detail::stream_buffer_exposer.