GCC Code Coverage Report


Directory: gridformat/
File: gridformat/encoding/ascii.hpp
Date: 2024-11-10 16:24:00
Exec Total Coverage
Lines: 56 56 100.0%
Functions: 31 37 83.8%
Branches: 23 38 60.5%

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