GCC Code Coverage Report


Directory: gridformat/
File: gridformat/vtk/appendix.hpp
Date: 2025-03-26 17:08:15
Exec Total Coverage
Lines: 93 96 96.9%
Functions: 170 393 43.3%
Branches: 63 112 56.2%

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 VTK
6 * \brief Helper classes for writing VTK appendices of xml formats
7 */
8 #ifndef GRIDFORMAT_VTK_APPENDIX_HPP_
9 #define GRIDFORMAT_VTK_APPENDIX_HPP_
10
11 #include <concepts>
12 #include <utility>
13 #include <ostream>
14 #include <string>
15 #include <vector>
16 #include <memory>
17 #include <limits>
18 #include <type_traits>
19
20 #include <gridformat/common/concepts.hpp>
21 #include <gridformat/common/indentation.hpp>
22 #include <gridformat/xml/element.hpp>
23 #include <gridformat/vtk/common.hpp>
24 #include <gridformat/vtk/data_array.hpp>
25 #include <gridformat/vtk/attributes.hpp>
26
27 namespace GridFormat::VTK {
28
29 /*!
30 * \ingroup VTK
31 * \brief Observer for appendices.
32 * Allows registering the offsets when streaming
33 * all fields that are part of the appendix.
34 */
35 class AppendixStreamObserver {
36 public:
37 95324 void register_offset(std::size_t offset) {
38 95324 _offsets.push_back(offset);
39 95324 }
40
41 6074 const std::vector<std::size_t>& offsets() const {
42 6074 return _offsets;
43 }
44
45 private:
46 std::vector<std::size_t> _offsets;
47 };
48
49 /*!
50 * \ingroup VTK
51 * \brief Stores vtk data arrays to be exported as vtk-xml appendix.
52 */
53 class Appendix {
54 struct DataArrayModel {
55 381296 virtual ~DataArrayModel() = default;
56
57 95324 DataArrayModel() = default;
58 95324 DataArrayModel(DataArrayModel&&) = default;
59 DataArrayModel(const DataArrayModel&) = delete;
60 DataArrayModel& operator=(DataArrayModel&&) = default;
61 DataArrayModel& operator=(const DataArrayModel&) = delete;
62
63 95324 friend std::ostream& operator<<(std::ostream& s, const DataArrayModel& arr) {
64 95324 arr.stream(s);
65 95324 return s;
66 }
67
68 private:
69 virtual void stream(std::ostream& s) const = 0;
70 };
71
72 template<Concepts::StreamableWith<std::ostream> DataArray>
73 class DataArrayImpl : public DataArrayModel {
74 public:
75 190648 explicit DataArrayImpl(DataArray&& arr)
76 190648 : _data_array(std::move(arr))
77 190648 {}
78
79 private:
80 DataArray _data_array;
81
82 190648 void stream(std::ostream& s) const override {
83 190648 s << _data_array;
84 190648 }
85 };
86
87 public:
88 template<typename... Args>
89 190648 void add(DataArray<Args...>&& data_array) {
90
1/2
✓ Branch 3 taken 95324 times.
✗ Branch 4 not taken.
190648 _emplace_back(DataArrayImpl{std::move(data_array)});
91 190648 }
92
93 6074 friend std::ostream& operator<<(std::ostream& s, const Appendix& app) {
94
1/2
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
6074 const auto start_pos = s.tellp();
95
2/2
✓ Branch 5 taken 95324 times.
✓ Branch 6 taken 6074 times.
101398 for (const auto& array_ptr : app._content) {
96
1/2
✓ Branch 1 taken 95324 times.
✗ Branch 2 not taken.
95324 const auto pos_before = s.tellp();
97
1/2
✓ Branch 2 taken 95324 times.
✗ Branch 3 not taken.
95324 s << *array_ptr;
98
1/2
✓ Branch 0 taken 95324 times.
✗ Branch 1 not taken.
95324 if (app._observer)
99
1/2
✓ Branch 2 taken 95324 times.
✗ Branch 3 not taken.
95324 app._observer->register_offset(pos_before - start_pos);
100 }
101 6074 return s;
102 }
103
104 6074 void set_observer(AppendixStreamObserver* observer) {
105 6074 _observer = observer;
106 6074 }
107
108 private:
109 template<typename DA>
110 190648 void _emplace_back(DataArrayImpl<DA>&& impl) {
111
1/2
✓ Branch 1 taken 95324 times.
✗ Branch 2 not taken.
190648 _content.emplace_back(
112
1/2
✓ Branch 2 taken 95324 times.
✗ Branch 3 not taken.
381296 std::make_unique<DataArrayImpl<DA>>(std::move(impl))
113 );
114 190648 }
115
116 std::vector<std::unique_ptr<const DataArrayModel>> _content;
117 AppendixStreamObserver* _observer{nullptr};
118 };
119
120
121 #ifndef DOXYGEN
122 namespace XML::Detail {
123
124 struct XMLAppendixContent {
125 const Appendix& appendix;
126
127 6074 friend std::ostream& operator<<(std::ostream& s, const XMLAppendixContent& c) {
128 6074 s << " _"; // prefix required by vtk
129 6074 s << c.appendix;
130 6074 s << "\n"; // newline such that closing xml element is easily visible
131 6074 return s;
132 }
133 };
134
135 142705 void write_xml_element_with_offsets(const XMLElement& e,
136 std::ostream& s,
137 Indentation& ind,
138 std::vector<std::size_t>& offset_positions) {
139 const std::string empty_string_for_max_header(
140
1/2
✓ Branch 2 taken 142705 times.
✗ Branch 3 not taken.
285410 std::to_string(std::numeric_limits<std::size_t>::max()).size(),
141 ' '
142
1/2
✓ Branch 2 taken 142705 times.
✗ Branch 3 not taken.
142705 );
143
144 95324 const auto cache_offset_xml_pos = [&] () {
145 95324 s << " offset=\"";
146
2/4
✓ Branch 1 taken 95324 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 95324 times.
✗ Branch 6 not taken.
95324 offset_positions.push_back(s.tellp());
147 95324 s << empty_string_for_max_header;
148 95324 s << "\"";
149 95324 };
150
151
6/6
✓ Branch 1 taken 136631 times.
✓ Branch 2 taken 6074 times.
✓ Branch 4 taken 95694 times.
✓ Branch 5 taken 40937 times.
✓ Branch 6 taken 95694 times.
✓ Branch 7 taken 47011 times.
142705 if (!e.has_content() && e.number_of_children() == 0) {
152
1/2
✓ Branch 1 taken 95694 times.
✗ Branch 2 not taken.
95694 s << ind;
153
1/2
✓ Branch 2 taken 95694 times.
✗ Branch 3 not taken.
95694 GridFormat::XML::Detail::write_xml_tag_open(e, s, "");
154
3/4
✓ Branch 2 taken 95694 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 95324 times.
✓ Branch 5 taken 370 times.
95694 if (e.name() == "DataArray")
155
1/2
✓ Branch 1 taken 95324 times.
✗ Branch 2 not taken.
95324 cache_offset_xml_pos();
156
1/2
✓ Branch 1 taken 95694 times.
✗ Branch 2 not taken.
95694 s << "/>";
157 } else {
158
1/2
✓ Branch 1 taken 47011 times.
✗ Branch 2 not taken.
47011 s << ind;
159
1/2
✓ Branch 1 taken 47011 times.
✗ Branch 2 not taken.
47011 GridFormat::XML::Detail::write_xml_tag_open(e, s);
160
2/4
✓ Branch 2 taken 47011 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 47011 times.
47011 if (e.name() == "DataArray")
161 cache_offset_xml_pos();
162
1/2
✓ Branch 1 taken 47011 times.
✗ Branch 2 not taken.
47011 s << "\n";
163
164
2/2
✓ Branch 1 taken 6074 times.
✓ Branch 2 taken 40937 times.
47011 if (e.has_content()) {
165
1/2
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
6074 e.stream_content(s);
166
1/2
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
6074 s << "\n";
167 }
168
169
1/2
✓ Branch 1 taken 47011 times.
✗ Branch 2 not taken.
47011 ++ind;
170
2/2
✓ Branch 6 taken 136631 times.
✓ Branch 7 taken 47011 times.
183642 for (const auto& c : children(e)) {
171
1/2
✓ Branch 1 taken 136631 times.
✗ Branch 2 not taken.
136631 write_xml_element_with_offsets(c, s, ind, offset_positions);
172
1/2
✓ Branch 1 taken 136631 times.
✗ Branch 2 not taken.
136631 s << "\n";
173 }
174
1/2
✓ Branch 1 taken 47011 times.
✗ Branch 2 not taken.
47011 --ind;
175
176
1/2
✓ Branch 1 taken 47011 times.
✗ Branch 2 not taken.
47011 s << ind;
177
1/2
✓ Branch 1 taken 47011 times.
✗ Branch 2 not taken.
47011 GridFormat::XML::Detail::write_xml_tag_close(e, s);
178 }
179 142705 }
180
181 6074 std::vector<std::size_t> write_xml_element_with_offsets(const XMLElement& e,
182 std::ostream& s,
183 Indentation& ind) {
184 6074 std::vector<std::size_t> offset_positions;
185
1/2
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
6074 write_xml_element_with_offsets(e, s, ind, offset_positions);
186 6074 return offset_positions;
187 }
188
189 template<typename Context, typename Encoder>
190 requires(!std::is_const_v<std::remove_reference_t<Context>>)
191 12148 inline void write_with_appendix(Context&& context,
192 std::ostream& s,
193 const Encoder& encoder,
194 Indentation indentation = {}) {
195
2/2
✓ Branch 1 taken 4323 times.
✓ Branch 2 taken 1751 times.
12148 if (produces_valid_xml(encoder))
196
1/2
✓ Branch 1 taken 4323 times.
✗ Branch 2 not taken.
8646 s << "<?xml version=\"1.0\"?>\n";
197
198 12148 AppendixStreamObserver observer;
199 12148 context.appendix.set_observer(&observer);
200
201
2/4
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6074 times.
✗ Branch 5 not taken.
24296 auto& app_element = context.xml_representation.add_child("AppendedData");
202
3/6
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6074 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6074 times.
✗ Branch 8 not taken.
36444 app_element.set_attribute("encoding", attribute_name(encoder));
203
1/2
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
12148 app_element.set_content(Detail::XMLAppendixContent{context.appendix});
204
205 12148 const auto offset_positions = write_xml_element_with_offsets(
206
1/2
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
12148 context.xml_representation, s, indentation
207 );
208 12148 const auto& offsets = observer.offsets();
209
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6074 times.
12148 if (offsets.size() != offset_positions.size())
210 throw SizeError("Number of written & registered offsets does not match");
211
212
1/2
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
12148 const auto cur_pos = s.tellp();
213
2/2
✓ Branch 2 taken 95324 times.
✓ Branch 3 taken 6074 times.
202796 for (std::size_t i = 0; i < offsets.size(); ++i) {
214
1/2
✓ Branch 3 taken 95324 times.
✗ Branch 4 not taken.
190648 s.seekp(offset_positions[i]);
215
1/2
✓ Branch 2 taken 95324 times.
✗ Branch 3 not taken.
190648 const std::string offset_str = std::to_string(offsets[i]);
216
1/2
✓ Branch 3 taken 95324 times.
✗ Branch 4 not taken.
190648 s.write(offset_str.data(), offset_str.size());
217 }
218
1/2
✓ Branch 1 taken 6074 times.
✗ Branch 2 not taken.
12148 s.seekp(cur_pos);
219 12148 }
220
221 } // namespace XML::Detail
222 #endif // DOXYGEN
223
224 } // namespace GridFormat::VTK
225
226 #endif // GRIDFORMAT_VTK_APPENDIX_HPP_
227