GCC Code Coverage Report


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