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
24namespace seqan3::detail
25{
38template <typename char_t, typename traits_t = std::char_traits<char_t>>
39class fast_ostreambuf_iterator
40{
41private:
43 stream_buffer_exposer<char_t, traits_t> * stream_buf = nullptr;
44
45public:
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
T begin(T... args)
The <charconv> header from C++17's standard library.
T copy_n(T... args)
T end(T... args)
SeqAn specific customisations in the standard namespace.
Provides seqan3::detail::stream_buffer_exposer.
T to_chars(T... args)