GridFormat 0.4.0
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
ascii.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2// SPDX-License-Identifier: MIT
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
29namespace 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
41namespace GridFormat {
42
45
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;
52
53 friend bool operator==(const AsciiFormatOptions& a, const AsciiFormatOptions& b) {
54 return a.delimiter == b.delimiter
55 && a.line_prefix == b.line_prefix
56 && a.entries_per_line == b.entries_per_line
58 }
59};
60
62template<typename OStream>
63class AsciiOutputStream : public OutputStreamWrapperBase<OStream> {
64 class Buffer {
65 public:
66 Buffer(std::streamsize precision)
67 : _precision{precision} {
68 _init_stream_buf();
69 }
70
71 template<typename V, typename D>
72 void push(V&& value, D&& delimiter) {
73#if __cpp_lib_format
74 if constexpr (std::floating_point<std::remove_cvref_t<V>>)
75 std::format_to(std::back_inserter(_string_buf),
76 "{:.{}g}{}", std::forward<V>(value), _precision, std::forward<D>(delimiter)
77 );
78 else
79 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 }
86
87 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 }
95
96 auto data() const {
97 return std::span{_string_buf.data(), _string_buf.size()};
98 }
99
100 void clear() {
101 _string_buf.clear();
102 }
103
104 private:
105 void _init_stream_buf() {
106 _stream_buf = {};
107 _stream_buf.precision(_precision);
108 }
109
110 std::streamsize _precision;
111 std::string _string_buf;
112 std::ostringstream _stream_buf;
113 };
114
115 public:
116 AsciiOutputStream(OStream& s, AsciiFormatOptions opts = {})
117 : OutputStreamWrapperBase<OStream>(s)
118 , _opts{std::move(opts)}
119 {}
120
121 template<typename T, std::size_t size>
122 void write(std::span<T, size> data) {
123 std::size_t count_entries = 0;
124 std::size_t count_buffer_lines = 0;
125
126 using PrintType = typename Encoding::Detail::AsciiPrintType<T>::type;
127 Buffer buffer(std::numeric_limits<PrintType>::digits10);
128 while (count_entries < data.size()) {
129 buffer.push(count_entries > 0 ? "\n" : "", _opts.line_prefix);
130
131 using std::min;
132 const auto num_entries = min(_opts.entries_per_line, data.size() - count_entries);
133 for (const auto& value : data.subspan(count_entries, num_entries))
134 buffer.push(static_cast<PrintType>(value), _opts.delimiter);
135
136 // update counters
137 count_entries += num_entries;
138 ++count_buffer_lines;
139
140 // flush and reset buffer
141 if (count_buffer_lines >= _opts.num_cached_lines) {
142 buffer.prepare_readout();
143 this->_write_raw(buffer.data());
144 buffer.clear();
145 count_buffer_lines = 0;
146 }
147 }
148
149 // flush remaining buffer content
150 buffer.prepare_readout();
151 this->_write_raw(buffer.data());
152 }
153
154 AsciiFormatOptions _opts;
155};
156
158
159} // namespace GridFormat
160
161namespace GridFormat::Encoding {
162
165
167struct Ascii {
168 constexpr Ascii() = default;
169 constexpr explicit Ascii(AsciiFormatOptions opts)
170 : _opts{std::move(opts)}
171 {}
172
174 template<typename S>
175 constexpr auto operator()(S& s) const noexcept {
176 return AsciiOutputStream{s, options()};
177 }
178
180 static constexpr auto with(AsciiFormatOptions opts) {
181 return Ascii{std::move(opts)};
182 }
183
185 constexpr AsciiFormatOptions options() const {
186 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
196inline constexpr Ascii ascii;
197
199
200} // namespace GridFormat::Encoding
201
202#endif // GRIDFORMAT_COMMON_ENCODING_ASCII_HPP_
Wrapper around a given stream to write formatted ascii output.
Definition: ascii.hpp:63
constexpr Ascii ascii
Instance of the ascii encoder.
Definition: ascii.hpp:196
Options for fomatted output of ranges with ascii encoding.
Definition: ascii.hpp:47
std::size_t num_cached_lines
Number of line cached between flushing the buffer.
Definition: ascii.hpp:51
Ascii encoder.
Definition: ascii.hpp:167
constexpr auto operator()(S &s) const noexcept
Create an ascii stream with the defined options.
Definition: ascii.hpp:175
constexpr AsciiFormatOptions options() const
Return the current options.
Definition: ascii.hpp:185
static constexpr auto with(AsciiFormatOptions opts)
Return a new instance with different options.
Definition: ascii.hpp:180