Gemmi C++ API
Loading...
Searching...
No Matches
to_cif.hpp
Go to the documentation of this file.
1// Copyright 2017 Global Phasing Ltd.
2
3// Writing cif::Document or its parts to std::ostream.
4
5#ifndef GEMMI_TO_CIF_HPP_
6#define GEMMI_TO_CIF_HPP_
7
8#include <ostream>
9#include "cifdoc.hpp"
10
11namespace gemmi {
12namespace cif {
13
15enum class Style {
16 Simple,
18 PreferPairs, // write single-row loops as pairs
19 Pdbx, // PreferPairs + put '#' (empty comments) between categories
20 Indent35, // start values in pairs from 35th column
21 Aligned, // columns in tables are left-aligned
22};
23
26 bool prefer_pairs = false;
28 bool compact = false;
30 bool misuse_hash = false;
32 std::uint16_t align_pairs = 0;
35 std::uint16_t align_loops = 0;
36
38 // implicit conversion from deprecated Style (for backward compatibility)
40 switch (style) {
41 case Style::Simple:
42 break;
44 compact = true;
45 break;
47 prefer_pairs = true;
48 break;
49 case Style::Pdbx:
50 prefer_pairs = true;
51 misuse_hash = true;
52 break;
53 case Style::Indent35:
54 align_pairs = 33;
55 break;
56 case Style::Aligned:
57 align_pairs = 33;
58 align_loops = 30;
59 break;
60 }
61 }
62 std::string str() const {
63 std::string s;
64 if (prefer_pairs)
65 s += "prefer_pairs,";
66 if (compact)
67 s += "compact,";
68 if (misuse_hash)
69 s += "misuse_hash,";
70 if (align_pairs != 0)
71 s += "align_pairs=" + std::to_string(align_pairs) + ",";
72 if (align_loops != 0)
73 s += "align_loops=" + std::to_string(align_loops) + ",";
74 if (!s.empty())
75 s.pop_back();
76 return s;
77 }
78};
79
83public:
84 explicit BufOstream(std::ostream& os_) : os(os_), ptr(buf) {}
86 void flush() {
87 os.write(buf, ptr - buf);
88 ptr = buf;
89 }
90 void write(const char* s, size_t len) {
91 constexpr int margin = sizeof(buf) - 512;
92 if (ptr - buf + len > margin) {
93 flush();
94 if (len > margin) {
95 os.write(s, len);
96 return;
97 }
98 }
99 std::memcpy(ptr, s, len);
100 ptr += len;
101 }
102 void operator<<(const std::string& s) {
103 write(s.c_str(), s.size());
104 }
105 // below we don't check the buffer boundary, these functions add <512 bytes
106 void put(char c) {
107 *ptr++ = c;
108 }
109 void pad(size_t n) {
110 std::memset(ptr, ' ', n);
111 ptr += n;
112 }
113private:
114 std::ostream& os;
115 // increasing buffer to 8kb or 64kb doesn't make significant difference
116 char buf[4096];
117 char* ptr;
118};
119
120// CIF files are read in binary mode. It makes difference only for text fields.
121// If the text field with \r\n would be written as is in text mode on Windows
122// \r would get duplicated. As a workaround, here we convert \r\n to \n.
123// Hopefully \r that gets removed here is never meaningful.
124inline void write_text_field(BufOstream& os, const std::string& value) {
125 for (size_t pos = 0, end = 0; end != std::string::npos; pos = end + 1) {
126 end = value.find("\r\n", pos);
127 size_t len = (end == std::string::npos ? value.size() : end) - pos;
128 os.write(value.c_str() + pos, len);
129 }
130}
131
132inline void write_out_pair(BufOstream& os, const std::string& name,
133 const std::string& value, WriteOptions options) {
134 os << name;
135 if (is_text_field(value)) {
136 os.put('\n');
137 write_text_field(os, value);
138 } else {
139 if (name.size() + value.size() > 120) {
140 os.put('\n');
141 } else {
142 os.put(' ');
143 if (name.size() < options.align_pairs)
144 os.pad(options.align_pairs - name.size());
145 }
146 os << value;
147 }
148 os.put('\n');
149}
150
151inline void write_out_loop(BufOstream& os, const Loop& loop, WriteOptions options) {
152 if (loop.values.empty())
153 return;
154 if (options.prefer_pairs && loop.length() == 1) {
155 for (size_t i = 0; i != loop.tags.size(); ++i)
156 write_out_pair(os, loop.tags[i], loop.values[i], options);
157 return;
158 }
159 // tags
160 os.write("loop_", 5);
161 for (const std::string& tag : loop.tags) {
162 os.put('\n');
163 os << tag;
164 }
165 // values
166 size_t ncol = loop.tags.size();
167
168 std::vector<size_t> col_width(ncol, 0);
169 if (options.align_loops > 0) {
170 size_t col = 0;
171 for (const std::string& val : loop.values) {
172 if (!is_text_field(val))
173 col_width[col] = std::max(col_width[col], val.size());
174 if (++col == ncol)
175 col = 0;
176 }
177 for (size_t& w : col_width)
178 w = std::min(w, (size_t)options.align_loops);
179 }
180
181 size_t col = 0;
182 bool need_new_line = true;
183 for (const std::string& val : loop.values) {
184 bool text_field = is_text_field(val);
185 os.put(need_new_line || text_field ? '\n' : ' ');
187 if (text_field)
188 write_text_field(os, val);
189 else
190 os << val;
191 if (col != ncol - 1) {
192 if (val.size() < col_width[col])
193 os.pad(col_width[col] - val.size());
194 ++col;
195 } else {
196 col = 0;
197 need_new_line = true;
198 }
199 }
200 os.put('\n');
201}
202
203inline void write_out_item(BufOstream& os, const Item& item, WriteOptions options) {
204 switch (item.type) {
205 case ItemType::Pair:
206 write_out_pair(os, item.pair[0], item.pair[1], options);
207 break;
208 case ItemType::Loop:
209 write_out_loop(os, item.loop, options);
210 break;
211 case ItemType::Frame:
212 os.write("save_", 5);
213 os << item.frame.name;
214 os.put('\n');
215 for (const Item& inner_item : item.frame.items)
217 os.write("save_\n", 6);
218 break;
220 os << item.pair[1];
221 os.put('\n');
222 break;
223 case ItemType::Erased:
224 break;
225 }
226}
227
228inline bool should_be_separated_(const Item& a, const Item& b) {
229 if (a.type == ItemType::Comment || b.type == ItemType::Comment)
230 return false;
231 if (a.type != ItemType::Pair || b.type != ItemType::Pair)
232 return true;
233 // check if we have mmcif-like tags from different categories
234 auto adot = a.pair[0].find('.');
235 if (adot == std::string::npos)
236 return false;
237 auto bdot = b.pair[0].find('.');
238 return adot != bdot || a.pair[0].compare(0, adot, b.pair[0], 0, adot) != 0;
239}
240
241inline void write_cif_block_to_stream(std::ostream& os_, const Block& block,
243 BufOstream os(os_);
244 os.write("data_", 5);
245 os << block.name;
246 os.put('\n');
247 if (options.misuse_hash)
248 os.write("#\n", 2);
249 const Item* prev = nullptr;
250 for (const Item& item : block.items) {
251 if (item.type == ItemType::Erased)
252 continue;
253 if (prev && !options.compact && should_be_separated_(*prev, item)) {
254 if (options.misuse_hash)
255 os.put('#');
256 os.put('\n');
257 }
258 write_out_item(os, item, options);
259 prev = &item;
260 }
261 if (options.misuse_hash)
262 os.write("#\n", 2);
263}
264
265inline void write_cif_to_stream(std::ostream& os, const Document& doc,
267 bool first = true;
268 for (const Block& block : doc.blocks) {
269 if (!first)
270 os.put('\n'); // extra blank line for readability
272 first = false;
273 }
274}
275
276} // namespace cif
277} // namespace gemmi
278
279#endif
struct Document that represents the CIF file (but can also be read from a different representation,...
std::ostream with buffering.
Definition to_cif.hpp:82
void operator<<(const std::string &s)
Definition to_cif.hpp:102
void pad(size_t n)
Definition to_cif.hpp:109
void write(const char *s, size_t len)
Definition to_cif.hpp:90
BufOstream(std::ostream &os_)
Definition to_cif.hpp:84
void write_cif_block_to_stream(std::ostream &os_, const Block &block, WriteOptions options=WriteOptions())
Definition to_cif.hpp:241
bool is_text_field(const std::string &val)
Definition cifdoc.hpp:1175
Style
deprecated, use cif::WriteOptions instead
Definition to_cif.hpp:15
void write_out_loop(BufOstream &os, const Loop &loop, WriteOptions options)
Definition to_cif.hpp:151
void write_out_pair(BufOstream &os, const std::string &name, const std::string &value, WriteOptions options)
Definition to_cif.hpp:132
void write_out_item(BufOstream &os, const Item &item, WriteOptions options)
Definition to_cif.hpp:203
void write_cif_to_stream(std::ostream &os, const Document &doc, WriteOptions options=WriteOptions())
Definition to_cif.hpp:265
void write_text_field(BufOstream &os, const std::string &value)
Definition to_cif.hpp:124
std::vector< Item > items
Definition cifdoc.hpp:451
std::string name
Definition cifdoc.hpp:450
ItemType type
Definition cifdoc.hpp:520
std::vector< std::string > tags
Definition cifdoc.hpp:128
std::vector< std::string > values
Definition cifdoc.hpp:129
size_t length() const
Definition cifdoc.hpp:142
WriteOptions(Style style)
Definition to_cif.hpp:39
bool compact
no blank lines between categories, only between blocks
Definition to_cif.hpp:28
bool misuse_hash
put '#' (empty comments) before/after categories
Definition to_cif.hpp:30
std::uint16_t align_pairs
width reserved for tags in pairs (e.g. 34 = value starts at 35th column)
Definition to_cif.hpp:32
bool prefer_pairs
write single-row loops as pairs
Definition to_cif.hpp:26
std::uint16_t align_loops
if non-zero, determines max width of each column in a loop and aligns all values to this width; the w...
Definition to_cif.hpp:35
std::string str() const
Definition to_cif.hpp:62