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 |