Line | Branch | Exec | Source |
---|---|---|---|
1 | // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de> | ||
2 | // SPDX-License-Identifier: MIT | ||
3 | /*! | ||
4 | * \file | ||
5 | * \ingroup Encoding | ||
6 | * \brief Encoder and stream using ascii | ||
7 | */ | ||
8 | #ifndef GRIDFORMAT_COMMON_ENCODING_ASCII_HPP_ | ||
9 | #define GRIDFORMAT_COMMON_ENCODING_ASCII_HPP_ | ||
10 | |||
11 | #include <cmath> | ||
12 | #include <limits> | ||
13 | #include <concepts> | ||
14 | #include <algorithm> | ||
15 | #include <optional> | ||
16 | #include <cstdint> | ||
17 | #include <string> | ||
18 | #include <span> | ||
19 | #include <sstream> | ||
20 | |||
21 | #if __has_include(<format>) | ||
22 | #include <format> | ||
23 | #endif | ||
24 | |||
25 | #include <gridformat/common/output_stream.hpp> | ||
26 | #include <gridformat/common/reserved_string.hpp> | ||
27 | |||
28 | #ifndef DOXYGEN | ||
29 | namespace GridFormat::Encoding::Detail { | ||
30 | |||
31 | template<typename T> | ||
32 | struct AsciiPrintType : std::type_identity<T> {}; | ||
33 | template<std::signed_integral T> | ||
34 | struct AsciiPrintType<T> : std::type_identity<std::intmax_t> {}; | ||
35 | template<std::unsigned_integral T> | ||
36 | struct AsciiPrintType<T> : std::type_identity<std::uintmax_t> {}; | ||
37 | |||
38 | } // namespace GridFormat::Encoding::Detail | ||
39 | #endif // DOXYGEN | ||
40 | |||
41 | namespace GridFormat { | ||
42 | |||
43 | //! \addtogroup Encoding | ||
44 | //! \{ | ||
45 | |||
46 | //! Options for fomatted output of ranges with ascii encoding | ||
47 | struct AsciiFormatOptions { | ||
48 | ReservedString<30> delimiter{""}; | ||
49 | ReservedString<30> line_prefix{""}; | ||
50 | std::size_t entries_per_line = std::numeric_limits<std::size_t>::max(); | ||
51 | std::size_t num_cached_lines = 100; //!< Number of line cached between flushing the buffer | ||
52 | |||
53 | 14726 | friend bool operator==(const AsciiFormatOptions& a, const AsciiFormatOptions& b) { | |
54 | 14726 | return a.delimiter == b.delimiter | |
55 |
1/2✓ Branch 1 taken 14726 times.
✗ Branch 2 not taken.
|
14726 | && a.line_prefix == b.line_prefix |
56 |
1/2✓ Branch 0 taken 14726 times.
✗ Branch 1 not taken.
|
14726 | && a.entries_per_line == b.entries_per_line |
57 |
2/4✓ Branch 0 taken 14726 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14726 times.
✗ Branch 3 not taken.
|
29452 | && a.num_cached_lines == b.num_cached_lines; |
58 | } | ||
59 | }; | ||
60 | |||
61 | //! Wrapper around a given stream to write formatted ascii output | ||
62 | template<typename OStream> | ||
63 | class AsciiOutputStream : public OutputStreamWrapperBase<OStream> { | ||
64 | class Buffer { | ||
65 | public: | ||
66 | 14729 | Buffer(std::streamsize precision) | |
67 |
1/2✓ Branch 2 taken 14729 times.
✗ Branch 3 not taken.
|
14729 | : _precision{precision} { |
68 |
1/2✓ Branch 1 taken 14729 times.
✗ Branch 2 not taken.
|
14729 | _init_stream_buf(); |
69 | 14729 | } | |
70 | |||
71 | template<typename V, typename D> | ||
72 | 2702680 | void push(V&& value, D&& delimiter) { | |
73 | #if __cpp_lib_format | ||
74 | if constexpr (std::floating_point<std::remove_cvref_t<V>>) | ||
75 | 4810808 | std::format_to(std::back_inserter(_string_buf), | |
76 | 2405404 | "{:.{}g}{}", std::forward<V>(value), _precision, std::forward<D>(delimiter) | |
77 | ); | ||
78 | else | ||
79 | 297276 | std::format_to(std::back_inserter(_string_buf), | |
80 | "{}{}", std::forward<V>(value), std::forward<D>(delimiter) | ||
81 | ); | ||
82 | #else | ||
83 | _stream_buf << std::forward<V>(value) << std::forward<D>(delimiter); | ||
84 | #endif | ||
85 | 2702680 | } | |
86 | |||
87 | 14780 | void prepare_readout() { | |
88 | #if !__cpp_lib_format | ||
89 | // move internal string buffer out of the stream | ||
90 | // (see https://stackoverflow.com/a/66662433) | ||
91 | _string_buf = std::move(_stream_buf).str(); | ||
92 | _init_stream_buf(); // reinitialize | ||
93 | #endif | ||
94 | 14780 | } | |
95 | |||
96 | 14780 | auto data() const { | |
97 | 14780 | return std::span{_string_buf.data(), _string_buf.size()}; | |
98 | } | ||
99 | |||
100 | 51 | void clear() { | |
101 | 51 | _string_buf.clear(); | |
102 | 51 | } | |
103 | |||
104 | private: | ||
105 | 14729 | void _init_stream_buf() { | |
106 |
2/4✓ Branch 1 taken 14729 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14729 times.
✗ Branch 5 not taken.
|
14729 | _stream_buf = {}; |
107 | 14729 | _stream_buf.precision(_precision); | |
108 | 14729 | } | |
109 | |||
110 | std::streamsize _precision; | ||
111 | std::string _string_buf; | ||
112 | std::ostringstream _stream_buf; | ||
113 | }; | ||
114 | |||
115 | public: | ||
116 | 14729 | AsciiOutputStream(OStream& s, AsciiFormatOptions opts = {}) | |
117 | : OutputStreamWrapperBase<OStream>(s) | ||
118 | 14729 | , _opts{std::move(opts)} | |
119 | 14729 | {} | |
120 | |||
121 | template<typename T, std::size_t size> | ||
122 | 29456 | void write(std::span<T, size> data) { | |
123 | 29456 | std::size_t count_entries = 0; | |
124 | 29456 | std::size_t count_buffer_lines = 0; | |
125 | |||
126 | using PrintType = typename Encoding::Detail::AsciiPrintType<T>::type; | ||
127 |
1/2✓ Branch 1 taken 14729 times.
✗ Branch 2 not taken.
|
29456 | Buffer buffer(std::numeric_limits<PrintType>::digits10); |
128 |
2/2✓ Branch 1 taken 91373 times.
✓ Branch 2 taken 14729 times.
|
212200 | while (count_entries < data.size()) { |
129 |
3/4✓ Branch 0 taken 76784 times.
✓ Branch 1 taken 14589 times.
✓ Branch 3 taken 91373 times.
✗ Branch 4 not taken.
|
182744 | buffer.push(count_entries > 0 ? "\n" : "", _opts.line_prefix); |
130 | |||
131 | using std::min; | ||
132 | 182744 | const auto num_entries = min(_opts.entries_per_line, data.size() - count_entries); | |
133 |
2/2✓ Branch 5 taken 1259967 times.
✓ Branch 6 taken 91373 times.
|
2702670 | for (const auto& value : data.subspan(count_entries, num_entries)) |
134 |
1/2✓ Branch 1 taken 1259967 times.
✗ Branch 2 not taken.
|
2519926 | buffer.push(static_cast<PrintType>(value), _opts.delimiter); |
135 | |||
136 | // update counters | ||
137 | 182744 | count_entries += num_entries; | |
138 | 182744 | ++count_buffer_lines; | |
139 | |||
140 | // flush and reset buffer | ||
141 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 91322 times.
|
182744 | if (count_buffer_lines >= _opts.num_cached_lines) { |
142 | 102 | buffer.prepare_readout(); | |
143 |
1/2✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
|
102 | this->_write_raw(buffer.data()); |
144 | 102 | buffer.clear(); | |
145 | 102 | count_buffer_lines = 0; | |
146 | } | ||
147 | } | ||
148 | |||
149 | // flush remaining buffer content | ||
150 | 29456 | buffer.prepare_readout(); | |
151 |
1/2✓ Branch 2 taken 14729 times.
✗ Branch 3 not taken.
|
29456 | this->_write_raw(buffer.data()); |
152 | 29456 | } | |
153 | |||
154 | AsciiFormatOptions _opts; | ||
155 | }; | ||
156 | |||
157 | //! \} group Encoding | ||
158 | |||
159 | } // namespace GridFormat | ||
160 | |||
161 | namespace GridFormat::Encoding { | ||
162 | |||
163 | //! \addtogroup Encoding | ||
164 | //! \{ | ||
165 | |||
166 | //! Ascii encoder | ||
167 | struct Ascii { | ||
168 | constexpr Ascii() = default; | ||
169 | 14727 | constexpr explicit Ascii(AsciiFormatOptions opts) | |
170 | 14727 | : _opts{std::move(opts)} | |
171 | 14727 | {} | |
172 | |||
173 | //! Create an ascii stream with the defined options | ||
174 | template<typename S> | ||
175 | 14729 | constexpr auto operator()(S& s) const noexcept { | |
176 | 14729 | return AsciiOutputStream{s, options()}; | |
177 | } | ||
178 | |||
179 | //! Return a new instance with different options | ||
180 | 14727 | static constexpr auto with(AsciiFormatOptions opts) { | |
181 | 14727 | return Ascii{std::move(opts)}; | |
182 | } | ||
183 | |||
184 | //! Return the current options | ||
185 | 29455 | constexpr AsciiFormatOptions options() const { | |
186 |
2/4✓ Branch 1 taken 29455 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 29455 times.
✗ Branch 5 not taken.
|
29455 | return _opts.value_or(AsciiFormatOptions{}); |
187 | } | ||
188 | |||
189 | private: | ||
190 | // we use optional here in order to be able to define | ||
191 | // an inline constexpr instance of this class below | ||
192 | std::optional<AsciiFormatOptions> _opts = {}; | ||
193 | }; | ||
194 | |||
195 | //! Instance of the ascii encoder | ||
196 | inline constexpr Ascii ascii; | ||
197 | |||
198 | //! \} group Encoding | ||
199 | |||
200 | } // namespace GridFormat::Encoding | ||
201 | |||
202 | #endif // GRIDFORMAT_COMMON_ENCODING_ASCII_HPP_ | ||
203 |