8#ifndef GRIDFORMAT_VTK_APPENDIX_HPP_
9#define GRIDFORMAT_VTK_APPENDIX_HPP_
20#include <gridformat/common/concepts.hpp>
21#include <gridformat/common/indentation.hpp>
22#include <gridformat/xml/element.hpp>
27namespace GridFormat::VTK {
37 void register_offset(std::size_t offset) {
38 _offsets.push_back(offset);
41 const std::vector<std::size_t>& offsets()
const {
46 std::vector<std::size_t> _offsets;
54 struct DataArrayModel {
55 virtual ~DataArrayModel() =
default;
57 DataArrayModel() =
default;
58 DataArrayModel(DataArrayModel&&) =
default;
59 DataArrayModel(
const DataArrayModel&) =
delete;
60 DataArrayModel& operator=(DataArrayModel&&) =
default;
61 DataArrayModel& operator=(
const DataArrayModel&) =
delete;
63 friend std::ostream& operator<<(std::ostream& s,
const DataArrayModel& arr) {
69 virtual void stream(std::ostream& s)
const = 0;
72 template<Concepts::StreamableWith<std::ostream> DataArray>
73 class DataArrayImpl :
public DataArrayModel {
76 : _data_array(std::move(arr))
82 void stream(std::ostream& s)
const override {
88 template<
typename... Args>
90 _emplace_back(DataArrayImpl{std::move(data_array)});
93 friend std::ostream& operator<<(std::ostream& s,
const Appendix& app) {
94 const auto start_pos = s.tellp();
95 for (
const auto& array_ptr : app._content) {
96 const auto pos_before = s.tellp();
99 app._observer->register_offset(pos_before - start_pos);
105 _observer = observer;
109 template<
typename DA>
110 void _emplace_back(DataArrayImpl<DA>&& impl) {
111 _content.emplace_back(
112 std::make_unique<DataArrayImpl<DA>>(std::move(impl))
116 std::vector<std::unique_ptr<const DataArrayModel>> _content;
122namespace XML::Detail {
124 struct XMLAppendixContent {
127 friend std::ostream& operator<<(std::ostream& s,
const XMLAppendixContent& c) {
135 void write_xml_element_with_offsets(
const XMLElement& e,
138 std::vector<std::size_t>& offset_positions) {
139 const std::string empty_string_for_max_header(
140 std::to_string(std::numeric_limits<std::size_t>::max()).size(),
144 const auto cache_offset_xml_pos = [&] () {
146 offset_positions.push_back(s.tellp());
147 s << empty_string_for_max_header;
151 if (!e.has_content() && e.number_of_children() == 0) {
153 GridFormat::XML::Detail::write_xml_tag_open(e, s,
"");
154 if (e.name() ==
"DataArray")
155 cache_offset_xml_pos();
159 GridFormat::XML::Detail::write_xml_tag_open(e, s);
160 if (e.name() ==
"DataArray")
161 cache_offset_xml_pos();
164 if (e.has_content()) {
170 for (
const auto& c : children(e)) {
171 write_xml_element_with_offsets(c, s, ind, offset_positions);
177 GridFormat::XML::Detail::write_xml_tag_close(e, s);
181 std::vector<std::size_t> write_xml_element_with_offsets(
const XMLElement& e,
184 std::vector<std::size_t> offset_positions;
185 write_xml_element_with_offsets(e, s, ind, offset_positions);
186 return offset_positions;
189 template<
typename Context,
typename Encoder>
190 requires(!std::is_const_v<std::remove_reference_t<Context>>)
191 inline void write_with_appendix(Context&& context,
193 const Encoder& encoder,
194 Indentation indentation = {}) {
195 if (produces_valid_xml(encoder))
196 s <<
"<?xml version=\"1.0\"?>\n";
198 AppendixStreamObserver observer;
199 context.appendix.set_observer(&observer);
201 auto& app_element = context.xml_representation.add_child(
"AppendedData");
202 app_element.set_attribute(
"encoding", attribute_name(encoder));
203 app_element.set_content(Detail::XMLAppendixContent{context.appendix});
205 const auto offset_positions = write_xml_element_with_offsets(
206 context.xml_representation, s, indentation
208 const auto& offsets = observer.offsets();
209 if (offsets.size() != offset_positions.size())
210 throw SizeError(
"Number of written & registered offsets does not match");
212 const auto cur_pos = s.tellp();
213 for (std::size_t i = 0; i < offsets.size(); ++i) {
214 s.seekp(offset_positions[i]);
215 const std::string offset_str = std::to_string(offsets[i]);
216 s.write(offset_str.data(), offset_str.size());
Helper functions to get the VTK-specific names of things.
Common functionality for VTK writers.