GCC Code Coverage Report


Directory: gridformat/
File: gridformat/vtk/appendix.hpp
Date: 2026-10-12 17:50:57
Exec Total Coverage
Lines: 93 96 96.9%
Functions: 197 468 42.1%
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 102168 void register_offset(std::size_t offset) {
38 102168 _offsets.push_back(offset);
39 102168 }
40
41 6158 const std::vector<std::size_t>& offsets() const {
42 6158 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 408672 virtual ~DataArrayModel() = default;
56
57 102168 DataArrayModel() = default;
58 102168 DataArrayModel(DataArrayModel&&) = default;
59 DataArrayModel(const DataArrayModel&) = delete;
60 DataArrayModel& operator=(DataArrayModel&&) = default;
61 DataArrayModel& operator=(const DataArrayModel&) = delete;
62
63 102168 friend std::ostream& operator<<(std::ostream& s, const DataArrayModel& arr) {
64 102168 arr.stream(s);
65 102168 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 204336 explicit DataArrayImpl(DataArray&& arr)
76 204336 : _data_array(std::move(arr))
77 204336 {}
78
79 private:
80 DataArray _data_array;
81
82 204336 void stream(std::ostream& s) const override {
83 204336 s << _data_array;
84 204336 }
85 };
86
87 public:
88 template<typename... Args>
89 204336 void add(DataArray<Args...>&& data_array) {
90
1/2
✓ Branch 3 taken 102168 times.
✗ Branch 4 not taken.
204336 _emplace_back(DataArrayImpl{std::move(data_array)});
91 204336 }
92
93 6158 friend std::ostream& operator<<(std::ostream& s, const Appendix& app) {
94
1/2
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
6158 const auto start_pos = s.tellp();
95
2/2
✓ Branch 5 taken 102168 times.
✓ Branch 6 taken 6158 times.
108326 for (const auto& array_ptr : app._content) {
96
1/2
✓ Branch 1 taken 102168 times.
✗ Branch 2 not taken.
102168 const auto pos_before = s.tellp();
97
1/2
✓ Branch 2 taken 102168 times.
✗ Branch 3 not taken.
102168 s << *array_ptr;
98
1/2
✓ Branch 0 taken 102168 times.
✗ Branch 1 not taken.
102168 if (app._observer)
99
1/2
✓ Branch 2 taken 102168 times.
✗ Branch 3 not taken.
102168 app._observer->register_offset(pos_before - start_pos);
100 }
101 6158 return s;
102 }
103
104 6158 void set_observer(AppendixStreamObserver* observer) {
105 6158 _observer = observer;
106 6158 }
107
108 private:
109 template<typename DA>
110 204336 void _emplace_back(DataArrayImpl<DA>&& impl) {
111
1/2
✓ Branch 1 taken 102168 times.
✗ Branch 2 not taken.
204336 _content.emplace_back(
112
1/2
✓ Branch 2 taken 102168 times.
✗ Branch 3 not taken.
408672 std::make_unique<DataArrayImpl<DA>>(std::move(impl))
113 );
114 204336 }
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 6158 friend std::ostream& operator<<(std::ostream& s, const XMLAppendixContent& c) {
128 6158 s << " _"; // prefix required by vtk
129 6158 s << c.appendix;
130 6158 s << "\n"; // newline such that closing xml element is easily visible
131 6158 return s;
132 }
133 };
134
135 149813 inline 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 149813 times.
✗ Branch 3 not taken.
299626 std::to_string(std::numeric_limits<std::size_t>::max()).size(),
141 ' '
142
1/2
✓ Branch 2 taken 149813 times.
✗ Branch 3 not taken.
149813 );
143
144 102168 const auto cache_offset_xml_pos = [&] () {
145 102168 s << " offset=\"";
146
2/4
✓ Branch 1 taken 102168 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 102168 times.
✗ Branch 6 not taken.
102168 offset_positions.push_back(s.tellp());
147 102168 s << empty_string_for_max_header;
148 102168 s << "\"";
149 102168 };
150
151
6/6
✓ Branch 1 taken 143655 times.
✓ Branch 2 taken 6158 times.
✓ Branch 4 taken 102320 times.
✓ Branch 5 taken 41335 times.
✓ Branch 6 taken 102320 times.
✓ Branch 7 taken 47493 times.
149813 if (!e.has_content() && e.number_of_children() == 0) {
152
1/2
✓ Branch 1 taken 102320 times.
✗ Branch 2 not taken.
102320 s << ind;
153
1/2
✓ Branch 2 taken 102320 times.
✗ Branch 3 not taken.
102320 GridFormat::XML::Detail::write_xml_tag_open(e, s, "");
154
3/4
✓ Branch 2 taken 102320 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 102168 times.
✓ Branch 5 taken 152 times.
102320 if (e.name() == "DataArray")
155
1/2
✓ Branch 1 taken 102168 times.
✗ Branch 2 not taken.
102168 cache_offset_xml_pos();
156
1/2
✓ Branch 1 taken 102320 times.
✗ Branch 2 not taken.
102320 s << "/>";
157 } else {
158
1/2
✓ Branch 1 taken 47493 times.
✗ Branch 2 not taken.
47493 s << ind;
159
1/2
✓ Branch 1 taken 47493 times.
✗ Branch 2 not taken.
47493 GridFormat::XML::Detail::write_xml_tag_open(e, s);
160
2/4
✓ Branch 2 taken 47493 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 47493 times.
47493 if (e.name() == "DataArray")
161 ✗ cache_offset_xml_pos();
162
1/2
✓ Branch 1 taken 47493 times.
✗ Branch 2 not taken.
47493 s << "\n";
163
164
2/2
✓ Branch 1 taken 6158 times.
✓ Branch 2 taken 41335 times.
47493 if (e.has_content()) {
165
1/2
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
6158 e.stream_content(s);
166
1/2
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
6158 s << "\n";
167 }
168
169
1/2
✓ Branch 1 taken 47493 times.
✗ Branch 2 not taken.
47493 ++ind;
170
2/2
✓ Branch 6 taken 143655 times.
✓ Branch 7 taken 47493 times.
191148 for (const auto& c : children(e)) {
171
1/2
✓ Branch 1 taken 143655 times.
✗ Branch 2 not taken.
143655 write_xml_element_with_offsets(c, s, ind, offset_positions);
172
1/2
✓ Branch 1 taken 143655 times.
✗ Branch 2 not taken.
143655 s << "\n";
173 }
174
1/2
✓ Branch 1 taken 47493 times.
✗ Branch 2 not taken.
47493 --ind;
175
176
1/2
✓ Branch 1 taken 47493 times.
✗ Branch 2 not taken.
47493 s << ind;
177
1/2
✓ Branch 1 taken 47493 times.
✗ Branch 2 not taken.
47493 GridFormat::XML::Detail::write_xml_tag_close(e, s);
178 }
179 149813 }
180
181 6158 inline std::vector<std::size_t> write_xml_element_with_offsets(const XMLElement& e,
182 std::ostream& s,
183 Indentation& ind) {
184 6158 std::vector<std::size_t> offset_positions;
185
1/2
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
6158 write_xml_element_with_offsets(e, s, ind, offset_positions);
186 6158 return offset_positions;
187 ✗ }
188
189 template<typename Context, typename Encoder>
190 requires(!std::is_const_v<std::remove_reference_t<Context>>)
191 12316 inline void write_with_appendix(Context&& context,
192 std::ostream& s,
193 const Encoder& encoder,
194 Indentation indentation = {}) {
195
2/2
✓ Branch 1 taken 4383 times.
✓ Branch 2 taken 1775 times.
12316 if (produces_valid_xml(encoder))
196
1/2
✓ Branch 1 taken 4383 times.
✗ Branch 2 not taken.
8766 s << "<?xml version=\"1.0\"?>\n";
197
198 12316 AppendixStreamObserver observer;
199 12316 context.appendix.set_observer(&observer);
200
201
2/4
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6158 times.
✗ Branch 5 not taken.
24632 auto& app_element = context.xml_representation.add_child("AppendedData");
202
3/6
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6158 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6158 times.
✗ Branch 8 not taken.
36948 app_element.set_attribute("encoding", attribute_name(encoder));
203
1/2
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
12316 app_element.set_content(Detail::XMLAppendixContent{context.appendix});
204
205 12316 const auto offset_positions = write_xml_element_with_offsets(
206
1/2
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
12316 context.xml_representation, s, indentation
207 );
208 12316 const auto& offsets = observer.offsets();
209
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6158 times.
12316 if (offsets.size() != offset_positions.size())
210 ✗ throw SizeError("Number of written & registered offsets does not match");
211
212
1/2
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
12316 const auto cur_pos = s.tellp();
213
2/2
✓ Branch 2 taken 102168 times.
✓ Branch 3 taken 6158 times.
216652 for (std::size_t i = 0; i < offsets.size(); ++i) {
214
1/2
✓ Branch 3 taken 102168 times.
✗ Branch 4 not taken.
204336 s.seekp(offset_positions[i]);
215
1/2
✓ Branch 2 taken 102168 times.
✗ Branch 3 not taken.
204336 const std::string offset_str = std::to_string(offsets[i]);
216
1/2
✓ Branch 3 taken 102168 times.
✗ Branch 4 not taken.
204336 s.write(offset_str.data(), offset_str.size());
217 }
218
1/2
✓ Branch 1 taken 6158 times.
✗ Branch 2 not taken.
12316 s.seekp(cur_pos);
219 12316 }
220
221 } // namespace XML::Detail
222 #endif // DOXYGEN
223
224 } // namespace GridFormat::VTK
225
226 #endif // GRIDFORMAT_VTK_APPENDIX_HPP_
227