rapidyaml  0.10.0
parse and emit YAML, and do it fast
emit.hpp
Go to the documentation of this file.
1 #ifndef _C4_YML_EMIT_HPP_
2 #define _C4_YML_EMIT_HPP_
3 
4 /** @file emit.hpp Utilities to emit YAML and JSON. */
5 
6 #ifndef _C4_YML_WRITER_HPP_
7 #include "./writer.hpp"
8 #endif
9 
10 #ifndef _C4_YML_TREE_HPP_
11 #include "./tree.hpp"
12 #endif
13 
14 #ifndef _C4_YML_NODE_HPP_
15 #include "./node.hpp"
16 #endif
17 
18 
19 C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
20 
21 
22 //-----------------------------------------------------------------------------
23 //-----------------------------------------------------------------------------
24 //-----------------------------------------------------------------------------
25 
26 namespace c4 {
27 namespace yml {
28 
29 /** @addtogroup doc_emit
30  *
31  * @{
32  */
33 
34 // fwd declarations
35 template<class Writer> class Emitter;
36 template<class OStream>
40 
41 namespace detail {
42 inline bool is_set_(ConstNodeRef n) { return n.tree() && (n.id() != NONE); }
43 }
44 
45 
46 //-----------------------------------------------------------------------------
47 //-----------------------------------------------------------------------------
48 //-----------------------------------------------------------------------------
49 
50 /** Specifies the type of content to emit */
51 typedef enum {
52  EMIT_YAML = 0, ///< emit YAML
53  EMIT_JSON = 1 ///< emit JSON
55 
56 
57 //-----------------------------------------------------------------------------
58 //-----------------------------------------------------------------------------
59 //-----------------------------------------------------------------------------
60 
61 /** A lightweight object containing options to be used when emitting. */
63 {
64  typedef enum : uint32_t {
65  DEFAULT_FLAGS = 0u,
66  JSON_ERR_ON_TAG = 1u << 0u,
67  JSON_ERR_ON_ANCHOR = 1u << 1u,
68  _JSON_ERR_MASK = JSON_ERR_ON_TAG|JSON_ERR_ON_ANCHOR,
69  } EmitOptionFlags_e;
70 
71 public:
72 
73  /** @name option flags
74  *
75  * @{ */
76  C4_ALWAYS_INLINE EmitOptionFlags_e json_error_flags() const noexcept { return m_option_flags; }
77  EmitOptions& json_error_flags(EmitOptionFlags_e d) noexcept { m_option_flags = (EmitOptionFlags_e)(d & _JSON_ERR_MASK); return *this; }
78  /** @} */
79 
80 public:
81 
82  /** @name max depth for the emitted tree
83  *
84  * This makes the emitter fail when emitting trees exceeding the
85  * max_depth.
86  *
87  * @{ */
88  C4_ALWAYS_INLINE id_type max_depth() const noexcept { return m_max_depth; }
89  EmitOptions& max_depth(id_type d) noexcept { m_max_depth = d; return *this; }
90  static constexpr const id_type max_depth_default = 64;
91  /** @} */
92 
93 public:
94 
95  bool operator== (const EmitOptions& that) const noexcept
96  {
97  return m_max_depth == that.m_max_depth &&
98  m_option_flags == that.m_option_flags;
99  }
100 
101 private:
102 
103  /** @cond dev */
104  id_type m_max_depth{max_depth_default};
105  EmitOptionFlags_e m_option_flags{DEFAULT_FLAGS};
106  /** @endcond */
107 };
108 
109 
110 //-----------------------------------------------------------------------------
111 //-----------------------------------------------------------------------------
112 //-----------------------------------------------------------------------------
113 
114 /** A stateful emitter, for use with a writer such as @ref WriterBuf,
115  * @ref WriterFile, or @ref WriterOStream */
116 template<class Writer>
117 class Emitter : public Writer
118 {
119 public:
120 
121  /** Construct the emitter and its internal Writer state, using default emit options.
122  * @param args arguments to be forwarded to the constructor of the writer.
123  * */
124  template<class ...Args>
125  Emitter(Args &&...args) : Writer(std::forward<Args>(args)...), m_tree(), m_opts(), m_flow(false) {}
126 
127  /** Construct the emitter and its internal Writer state.
128  *
129  * @param opts EmitOptions
130  * @param args arguments to be forwarded to the constructor of the writer.
131  * */
132  template<class ...Args>
133  Emitter(EmitOptions const& opts, Args &&...args) : Writer(std::forward<Args>(args)...), m_tree(), m_opts(opts), m_flow(false) {}
134 
135  /** emit!
136  *
137  * When writing to a buffer, returns a substr of the emitted YAML.
138  * If the given buffer has insufficient space, the returned substr
139  * will be null and its size will be the needed space. Whatever
140  * the size of the buffer, it is guaranteed that no writes are
141  * done past its end.
142  *
143  * When writing to a file, the returned substr will be null, but its
144  * length will be set to the number of bytes written.
145  *
146  * @param type specify what to emit
147  * @param t the tree to emit
148  * @param id the id of the node to emit
149  * @param error_on_excess when true, an error is raised when the
150  * output buffer is too small for the emitted YAML/JSON
151  * */
152  substr emit_as(EmitType_e type, Tree const& t, id_type id, bool error_on_excess);
153  /** emit starting at the root node */
154  substr emit_as(EmitType_e type, Tree const& t, bool error_on_excess=true)
155  {
156  if(t.empty())
157  return {};
158  return this->emit_as(type, t, t.root_id(), error_on_excess);
159  }
160  /** emit starting at the given node */
161  substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true)
162  {
163  if(!detail::is_set_(n))
164  return {};
165  _RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
166  return this->emit_as(type, *n.tree(), n.id(), error_on_excess);
167  }
168 
169 public:
170 
171  /** get the emit options for this object */
172  EmitOptions const& options() const noexcept { return m_opts; }
173 
174  /** set the max depth for emitted trees (to prevent a stack overflow) */
175  void max_depth(id_type max_depth) noexcept { m_opts.max_depth(max_depth); }
176  /** get the max depth for emitted trees (to prevent a stack overflow) */
177  id_type max_depth() const noexcept { return m_opts.max_depth(); }
178 
179 private:
180 
181  Tree const* C4_RESTRICT m_tree;
182  EmitOptions m_opts;
183  bool m_flow;
184 
185 private:
186 
187  void _emit_yaml(id_type id);
188  void _do_visit_flow_sl(id_type id, id_type depth, id_type ilevel=0);
189  void _do_visit_flow_ml(id_type id, id_type depth, id_type ilevel=0, id_type do_indent=1);
190  void _do_visit_block(id_type id, id_type depth, id_type ilevel=0, id_type do_indent=1);
191  void _do_visit_block_container(id_type id, id_type depth, id_type next_level, bool do_indent);
192  void _do_visit_json(id_type id, id_type depth);
193 
194 private:
195 
196  void _write(NodeScalar const& C4_RESTRICT sc, NodeType flags, id_type level);
197  void _write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags);
198 
199  void _write_doc(id_type id);
200  void _write_scalar_json_dquo(csubstr s);
201  void _write_scalar_literal(csubstr s, id_type level, bool as_key);
202  void _write_scalar_folded(csubstr s, id_type level, bool as_key);
203  void _write_scalar_squo(csubstr s, id_type level);
204  void _write_scalar_dquo(csubstr s, id_type level);
205  void _write_scalar_plain(csubstr s, id_type level);
206 
207  size_t _write_escaped_newlines(csubstr s, size_t i);
208  size_t _write_indented_block(csubstr s, size_t i, id_type level);
209 
210  void _write_tag(csubstr tag)
211  {
212  if(!tag.begins_with('!'))
213  this->Writer::_do_write('!');
214  this->Writer::_do_write(tag);
215  }
216 
217  enum : type_bits {
220  _keysc_json = (KEY) | ~(VAL),
221  _valsc_json = ~(KEY) | (VAL),
222  };
223 
224  C4_ALWAYS_INLINE void _writek(id_type id, id_type level) { _write(m_tree->keysc(id), (m_tree->_p(id)->m_type.type & ~_valsc), level); }
225  C4_ALWAYS_INLINE void _writev(id_type id, id_type level) { _write(m_tree->valsc(id), (m_tree->_p(id)->m_type.type & ~_keysc), level); }
226 
227  C4_ALWAYS_INLINE void _writek_json(id_type id) { _write_json(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~(VAL)); }
228  C4_ALWAYS_INLINE void _writev_json(id_type id) { _write_json(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~(KEY)); }
229 
230  void _indent(id_type level, bool enabled)
231  {
232  if(enabled)
233  this->Writer::_do_write(' ', 2u * (size_t)level);
234  }
235  void _indent(id_type level)
236  {
237  if(!m_flow)
238  this->Writer::_do_write(' ', 2u * (size_t)level);
239  }
240 };
241 
242 
243 //-----------------------------------------------------------------------------
244 //-----------------------------------------------------------------------------
245 //-----------------------------------------------------------------------------
246 
247 /** @defgroup doc_emit_to_file Emit to file
248  *
249  * @{
250  */
251 
252 
253 // emit from tree and node id -----------------------
254 
255 /** (1) emit YAML to the given file, starting at the given node. A null
256  * file defaults to stdout. Return the number of bytes written. */
257 inline size_t emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, FILE *f)
258 {
259  EmitterFile em(opts, f);
260  return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
261 }
262 /** (2) like (1), but use default emit options */
263 inline size_t emit_yaml(Tree const& t, id_type id, FILE *f)
264 {
265  EmitterFile em(f);
266  return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
267 }
268 /** (1) emit JSON to the given file, starting at the given node. A null
269  * file defaults to stdout. Return the number of bytes written. */
270 inline size_t emit_json(Tree const& t, id_type id, EmitOptions const& opts, FILE *f)
271 {
272  EmitterFile em(opts, f);
273  return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
274 }
275 /** (2) like (1), but use default emit options */
276 inline size_t emit_json(Tree const& t, id_type id, FILE *f)
277 {
278  EmitterFile em(f);
279  return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
280 }
281 
282 
283 // emit from root -------------------------
284 
285 /** (1) emit YAML to the given file, starting at the root node. A null file defaults to stdout.
286  * Return the number of bytes written. */
287 inline size_t emit_yaml(Tree const& t, EmitOptions const& opts, FILE *f=nullptr)
288 {
289  EmitterFile em(opts, f);
290  return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
291 }
292 /** (2) like (1), but use default emit options */
293 inline size_t emit_yaml(Tree const& t, FILE *f=nullptr)
294 {
295  EmitterFile em(f);
296  return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
297 }
298 /** (1) emit JSON to the given file. A null file defaults to stdout.
299  * Return the number of bytes written. */
300 inline size_t emit_json(Tree const& t, EmitOptions const& opts, FILE *f=nullptr)
301 {
302  EmitterFile em(opts, f);
303  return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
304 }
305 /** (2) like (1), but use default emit options */
306 inline size_t emit_json(Tree const& t, FILE *f=nullptr)
307 {
308  EmitterFile em(f);
309  return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
310 }
311 
312 
313 // emit from ConstNodeRef ------------------------
314 
315 /** (1) emit YAML to the given file. A null file defaults to stdout.
316  * Return the number of bytes written. */
317 inline size_t emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, FILE *f=nullptr)
318 {
319  if(!detail::is_set_(r))
320  return {};
321  EmitterFile em(opts, f);
322  return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
323 }
324 /** (2) like (1), but use default emit options */
325 inline size_t emit_yaml(ConstNodeRef const& r, FILE *f=nullptr)
326 {
327  if(!detail::is_set_(r))
328  return {};
329  EmitterFile em(f);
330  return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
331 }
332 /** (1) emit JSON to the given file. A null file defaults to stdout.
333  * Return the number of bytes written. */
334 inline size_t emit_json(ConstNodeRef const& r, EmitOptions const& opts, FILE *f=nullptr)
335 {
336  if(!detail::is_set_(r))
337  return {};
338  EmitterFile em(opts, f);
339  return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
340 }
341 /** (2) like (1), but use default emit options */
342 inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr)
343 {
344  if(!detail::is_set_(r))
345  return {};
346  EmitterFile em(f);
347  return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
348 }
349 
350 /** @} */
351 
352 
353 //-----------------------------------------------------------------------------
354 
355 /** @defgroup doc_emit_to_ostream Emit to an STL-like ostream
356  *
357  * @{
358  */
359 
360 /** emit YAML to an STL-like ostream */
361 template<class OStream>
362 inline OStream& operator<< (OStream& s, Tree const& t)
363 {
365  em.emit_as(EMIT_YAML, t);
366  return s;
367 }
368 
369 /** emit YAML to an STL-like ostream
370  * @overload */
371 template<class OStream>
372 inline OStream& operator<< (OStream& s, ConstNodeRef const& n)
373 {
374  if(!detail::is_set_(n))
375  return s;
377  em.emit_as(EMIT_YAML, n);
378  return s;
379 }
380 
381 /** mark a tree or node to be emitted as yaml when using @ref
382  * operator<<, with options. For example:
383  *
384  * ```cpp
385  * Tree t = parse_in_arena("{foo: bar}");
386  * std::cout << t; // emits YAML
387  * std::cout << as_yaml(t); // emits YAML, same as above
388  * std::cout << as_yaml(t, EmitOptions().max_depth(10)); // emits JSON with a max tree depth
389  * ```
390  *
391  * @see @ref operator<< */
392 struct as_json
393 {
394  Tree const* tree;
395  size_t node;
397  as_json(Tree const& t, EmitOptions const& opts={}) : tree(&t), node(t.empty() ? NONE : t.root_id()), options(opts) {}
398  as_json(Tree const& t, size_t id, EmitOptions const& opts={}) : tree(&t), node(id), options(opts) {}
399  as_json(ConstNodeRef const& n, EmitOptions const& opts={}) : tree(n.tree()), node(n.id()), options(opts) {}
400 };
401 
402 /** mark a tree or node to be emitted as yaml when using @ref
403  * operator<< . For example:
404  *
405  * ```cpp
406  * Tree t = parse_in_arena("{foo: bar}");
407  * std::cout << t; // emits YAML
408  * std::cout << as_json(t); // emits JSON
409  * std::cout << as_json(t, EmitOptions().max_depth(10)); // emits JSON with a max tree depth
410  * ```
411  *
412  * @see @ref operator<< */
413 struct as_yaml
414 {
415  Tree const* tree;
416  size_t node;
418  as_yaml(Tree const& t, EmitOptions const& opts={}) : tree(&t), node(t.empty() ? NONE : t.root_id()), options(opts) {}
419  as_yaml(Tree const& t, size_t id, EmitOptions const& opts={}) : tree(&t), node(id), options(opts) {}
420  as_yaml(ConstNodeRef const& n, EmitOptions const& opts={}) : tree(n.tree()), node(n.id()), options(opts) {}
421 };
422 
423 /** emit json to an STL-like stream */
424 template<class OStream>
425 inline OStream& operator<< (OStream& s, as_json const& j)
426 {
427  if(!j.tree || j.node == NONE)
428  return s;
430  em.emit_as(EMIT_JSON, *j.tree, j.node, true);
431  return s;
432 }
433 
434 /** emit yaml to an STL-like stream */
435 template<class OStream>
436 inline OStream& operator<< (OStream& s, as_yaml const& y)
437 {
438  if(!y.tree || y.node == NONE)
439  return s;
441  em.emit_as(EMIT_YAML, *y.tree, y.node, true);
442  return s;
443 }
444 
445 /** @} */
446 
447 
448 //-----------------------------------------------------------------------------
449 
450 /** @defgroup doc_emit_to_buffer Emit to memory buffer
451  *
452  * @{
453  */
454 
455 // emit from tree and node id -----------------------
456 
457 /** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
458  * @param t the tree to emit.
459  * @param id the node where to start emitting.
460  * @param opts emit options.
461  * @param buf the output buffer.
462  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
463  * @return a substr trimmed to the result in the output buffer. If the buffer is
464  * insufficient (when error_on_excess is false), the string pointer of the
465  * result will be set to null, and the length will report the required buffer size. */
466 inline substr emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true)
467 {
468  EmitterBuf em(opts, buf);
469  return em.emit_as(EMIT_YAML, t, id, error_on_excess);
470 }
471 /** (2) like (1), but use default emit options */
472 inline substr emit_yaml(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
473 {
474  EmitterBuf em(buf);
475  return em.emit_as(EMIT_YAML, t, id, error_on_excess);
476 }
477 /** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
478  * @param t the tree to emit.
479  * @param id the node where to start emitting.
480  * @param opts emit options.
481  * @param buf the output buffer.
482  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
483  * @return a substr trimmed to the result in the output buffer. If the buffer is
484  * insufficient (when error_on_excess is false), the string pointer of the
485  * result will be set to null, and the length will report the required buffer size. */
486 inline substr emit_json(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true)
487 {
488  EmitterBuf em(opts, buf);
489  return em.emit_as(EMIT_JSON, t, id, error_on_excess);
490 }
491 /** (2) like (1), but use default emit options */
492 inline substr emit_json(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
493 {
494  EmitterBuf em(buf);
495  return em.emit_as(EMIT_JSON, t, id, error_on_excess);
496 }
497 
498 
499 // emit from root -------------------------
500 
501 /** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
502  * @param t the tree; will be emitted from the root node.
503  * @param opts emit options.
504  * @param buf the output buffer.
505  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
506  * @return a substr trimmed to the result in the output buffer. If the buffer is
507  * insufficient (when error_on_excess is false), the string pointer of the
508  * result will be set to null, and the length will report the required buffer size. */
509 inline substr emit_yaml(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true)
510 {
511  EmitterBuf em(opts, buf);
512  return em.emit_as(EMIT_YAML, t, error_on_excess);
513 }
514 /** (2) like (1), but use default emit options */
515 inline substr emit_yaml(Tree const& t, substr buf, bool error_on_excess=true)
516 {
517  EmitterBuf em(buf);
518  return em.emit_as(EMIT_YAML, t, error_on_excess);
519 }
520 /** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
521  * @param t the tree; will be emitted from the root node.
522  * @param opts emit options.
523  * @param buf the output buffer.
524  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
525  * @return a substr trimmed to the result in the output buffer. If the buffer is
526  * insufficient (when error_on_excess is false), the string pointer of the
527  * result will be set to null, and the length will report the required buffer size. */
528 inline substr emit_json(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true)
529 {
530  EmitterBuf em(opts, buf);
531  return em.emit_as(EMIT_JSON, t, error_on_excess);
532 }
533 /** (2) like (1), but use default emit options */
534 inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true)
535 {
536  EmitterBuf em(buf);
537  return em.emit_as(EMIT_JSON, t, error_on_excess);
538 }
539 
540 
541 // emit from ConstNodeRef ------------------------
542 
543 /** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
544  * @param r the starting node.
545  * @param buf the output buffer.
546  * @param opts emit options.
547  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
548  * @return a substr trimmed to the result in the output buffer. If the buffer is
549  * insufficient (when error_on_excess is false), the string pointer of the
550  * result will be set to null, and the length will report the required buffer size. */
551 inline substr emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true)
552 {
553  if(!detail::is_set_(r))
554  return {};
555  EmitterBuf em(opts, buf);
556  return em.emit_as(EMIT_YAML, r, error_on_excess);
557 }
558 /** (2) like (1), but use default emit options */
559 inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
560 {
561  if(!detail::is_set_(r))
562  return {};
563  EmitterBuf em(buf);
564  return em.emit_as(EMIT_YAML, r, error_on_excess);
565 }
566 /** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
567  * @param r the starting node.
568  * @param buf the output buffer.
569  * @param opts emit options.
570  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
571  * @return a substr trimmed to the result in the output buffer. If the buffer is
572  * insufficient (when error_on_excess is false), the string pointer of the
573  * result will be set to null, and the length will report the required buffer size. */
574 inline substr emit_json(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true)
575 {
576  if(!detail::is_set_(r))
577  return {};
578  EmitterBuf em(opts, buf);
579  return em.emit_as(EMIT_JSON, r, error_on_excess);
580 }
581 /** (2) like (1), but use default emit options */
582 inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
583 {
584  if(!detail::is_set_(r))
585  return {};
586  EmitterBuf em(buf);
587  return em.emit_as(EMIT_JSON, r, error_on_excess);
588 }
589 
590 
591 //-----------------------------------------------------------------------------
592 
593 /** @defgroup doc_emit_to_container Emit to resizeable container
594  *
595  * @{
596  */
597 
598 // emit from tree and node id ---------------------------
599 
600 /** (1) emit+resize: emit YAML to the given `std::string`/`std::vector`-like
601  * container, resizing it as needed to fit the emitted YAML. If @p append is
602  * set to true, the emitted YAML is appended at the end of the container.
603  *
604  * @return a substr trimmed to the emitted YAML (excluding the initial contents, when appending) */
605 template<class CharOwningContainer>
606 substr emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
607 {
608  size_t startpos = append ? cont->size() : 0u;
609  cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail
610  substr buf = to_substr(*cont).sub(startpos);
611  substr ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/false);
612  if(ret.str == nullptr && ret.len > 0)
613  {
614  cont->resize(startpos + ret.len);
615  buf = to_substr(*cont).sub(startpos);
616  ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/true);
617  }
618  else
619  {
620  cont->resize(startpos + ret.len);
621  }
622  return ret;
623 }
624 /** (2) like (1), but use default emit options */
625 template<class CharOwningContainer>
626 substr emitrs_yaml(Tree const& t, id_type id, CharOwningContainer * cont, bool append=false)
627 {
628  return emitrs_yaml(t, id, EmitOptions{}, cont, append);
629 }
630 /** (1) emit+resize: emit JSON to the given `std::string`/`std::vector`-like
631  * container, resizing it as needed to fit the emitted JSON. If @p append is
632  * set to true, the emitted YAML is appended at the end of the container.
633  *
634  * @return a substr trimmed to the emitted JSON (excluding the initial contents, when appending) */
635 template<class CharOwningContainer>
636 substr emitrs_json(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
637 {
638  const size_t startpos = append ? cont->size() : 0u;
639  cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail
640  substr buf = to_substr(*cont).sub(startpos);
641  EmitterBuf em(opts, buf);
642  substr ret = emit_json(t, id, opts, buf, /*error_on_excess*/false);
643  if(ret.str == nullptr && ret.len > 0)
644  {
645  cont->resize(startpos + ret.len);
646  buf = to_substr(*cont).sub(startpos);
647  ret = emit_json(t, id, opts, buf, /*error_on_excess*/true);
648  }
649  else
650  {
651  cont->resize(startpos + ret.len);
652  }
653  return ret;
654 }
655 /** (2) like (1), but use default emit options */
656 template<class CharOwningContainer>
657 substr emitrs_json(Tree const& t, id_type id, CharOwningContainer * cont, bool append=false)
658 {
659  return emitrs_json(t, id, EmitOptions{}, cont, append);
660 }
661 
662 
663 /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
664 template<class CharOwningContainer>
665 CharOwningContainer emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts={})
666 {
667  CharOwningContainer c;
668  emitrs_yaml(t, id, opts, &c);
669  return c;
670 }
671 /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
672 template<class CharOwningContainer>
673 CharOwningContainer emitrs_json(Tree const& t, id_type id, EmitOptions const& opts={})
674 {
675  CharOwningContainer c;
676  emitrs_json(t, id, opts, &c);
677  return c;
678 }
679 
680 
681 // emit from root -------------------------
682 
683 /** (1) emit+resize: YAML to the given `std::string`/`std::vector`-like
684  * container, resizing it as needed to fit the emitted YAML.
685  * @return a substr trimmed to the new emitted contents. */
686 template<class CharOwningContainer>
687 substr emitrs_yaml(Tree const& t, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
688 {
689  if(t.empty())
690  return {};
691  return emitrs_yaml(t, t.root_id(), opts, cont, append);
692 }
693 /** (2) like (1), but use default emit options */
694 template<class CharOwningContainer>
695 substr emitrs_yaml(Tree const& t, CharOwningContainer * cont, bool append=false)
696 {
697  if(t.empty())
698  return {};
699  return emitrs_yaml(t, t.root_id(), EmitOptions{}, cont, append);
700 }
701 /** (1) emit+resize: JSON to the given `std::string`/`std::vector`-like
702  * container, resizing it as needed to fit the emitted JSON.
703  * @return a substr trimmed to the new emitted contents. */
704 template<class CharOwningContainer>
705 substr emitrs_json(Tree const& t, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
706 {
707  if(t.empty())
708  return {};
709  return emitrs_json(t, t.root_id(), opts, cont, append);
710 }
711 /** (2) like (1), but use default emit options */
712 template<class CharOwningContainer>
713 substr emitrs_json(Tree const& t, CharOwningContainer * cont, bool append=false)
714 {
715  if(t.empty())
716  return {};
717  return emitrs_json(t, t.root_id(), EmitOptions{}, cont, append);
718 }
719 
720 
721 /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
722 template<class CharOwningContainer>
723 CharOwningContainer emitrs_yaml(Tree const& t, EmitOptions const& opts={})
724 {
725  CharOwningContainer c;
726  if(t.empty())
727  return c;
728  emitrs_yaml(t, t.root_id(), opts, &c);
729  return c;
730 }
731 /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
732 template<class CharOwningContainer>
733 CharOwningContainer emitrs_json(Tree const& t, EmitOptions const& opts={})
734 {
735  CharOwningContainer c;
736  if(t.empty())
737  return c;
738  emitrs_json(t, t.root_id(), opts, &c);
739  return c;
740 }
741 
742 
743 // emit from ConstNodeRef ------------------------
744 
745 
746 /** (1) emit+resize: YAML to the given `std::string`/`std::vector`-like container,
747  * resizing it as needed to fit the emitted YAML.
748  * @return a substr trimmed to the new emitted contents */
749 template<class CharOwningContainer>
750 substr emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
751 {
752  if(!detail::is_set_(n))
753  return {};
754  _RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
755  return emitrs_yaml(*n.tree(), n.id(), opts, cont, append);
756 }
757 /** (2) like (1), but use default emit options */
758 template<class CharOwningContainer>
759 substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont, bool append=false)
760 {
761  if(!detail::is_set_(n))
762  return {};
763  _RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
764  return emitrs_yaml(*n.tree(), n.id(), EmitOptions{}, cont, append);
765 }
766 /** (1) emit+resize: JSON to the given `std::string`/`std::vector`-like container,
767  * resizing it as needed to fit the emitted JSON.
768  * @return a substr trimmed to the new emitted contents */
769 template<class CharOwningContainer>
770 substr emitrs_json(ConstNodeRef const& n, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
771 {
772  if(!detail::is_set_(n))
773  return {};
774  _RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
775  return emitrs_json(*n.tree(), n.id(), opts, cont, append);
776 }
777 /** (2) like (1), but use default emit options */
778 template<class CharOwningContainer>
779 substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont, bool append=false)
780 {
781  if(!detail::is_set_(n))
782  return {};
783  _RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
784  return emitrs_json(*n.tree(), n.id(), EmitOptions{}, cont, append);
785 }
786 
787 
788 /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
789 template<class CharOwningContainer>
790 CharOwningContainer emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts={})
791 {
792  if(!detail::is_set_(n))
793  return {};
794  _RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
795  CharOwningContainer c;
796  emitrs_yaml(*n.tree(), n.id(), opts, &c);
797  return c;
798 }
799 /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
800 template<class CharOwningContainer>
801 CharOwningContainer emitrs_json(ConstNodeRef const& n, EmitOptions const& opts={})
802 {
803  if(!detail::is_set_(n))
804  return {};
805  _RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
806  CharOwningContainer c;
807  emitrs_json(*n.tree(), n.id(), opts, &c);
808  return c;
809 }
810 
811 
812 /** @} */
813 
814 
815 //-----------------------------------------------------------------------------
816 
817 /** @cond dev */
818 
819 #define RYML_DEPRECATE_EMIT \
820  RYML_DEPRECATED("use emit_yaml() instead. " \
821  "See https://github.com/biojppm/rapidyaml/issues/120")
822 #define RYML_DEPRECATE_EMITRS \
823  RYML_DEPRECATED("use emitrs_yaml() instead. " \
824  "See https://github.com/biojppm/rapidyaml/issues/120")
825 
826 // workaround for Qt emit which is a macro;
827 // see https://github.com/biojppm/rapidyaml/issues/120.
828 // emit is defined in qobjectdefs.h (as an empty define).
829 #ifdef emit
830 #define RYML_TMP_EMIT_
831 #undef emit
832 #endif
833 
834 RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, id_type id, FILE *f)
835 {
836  return emit_yaml(t, id, f);
837 }
838 RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, FILE *f=nullptr)
839 {
840  return emit_yaml(t, f);
841 }
842 RYML_DEPRECATE_EMIT inline size_t emit(ConstNodeRef const& r, FILE *f=nullptr)
843 {
844  return emit_yaml(r, f);
845 }
846 
847 RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
848 {
849  return emit_yaml(t, id, buf, error_on_excess);
850 }
851 RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, substr buf, bool error_on_excess=true)
852 {
853  return emit_yaml(t, buf, error_on_excess);
854 }
855 RYML_DEPRECATE_EMIT inline substr emit(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
856 {
857  return emit_yaml(r, buf, error_on_excess);
858 }
859 
860 #ifdef RYML_TMP_EMIT_
861 #define emit
862 #undef RYML_TMP_EMIT_
863 #endif
864 
865 template<class CharOwningContainer>
866 RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, id_type id, CharOwningContainer * cont)
867 {
868  return emitrs_yaml(t, id, cont);
869 }
870 template<class CharOwningContainer>
871 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, id_type id)
872 {
873  return emitrs_yaml<CharOwningContainer>(t, id);
874 }
875 template<class CharOwningContainer>
876 RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, CharOwningContainer * cont)
877 {
878  return emitrs_yaml(t, cont);
879 }
880 template<class CharOwningContainer>
881 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t)
882 {
883  return emitrs_yaml<CharOwningContainer>(t);
884 }
885 template<class CharOwningContainer>
886 RYML_DEPRECATE_EMITRS substr emitrs(ConstNodeRef const& n, CharOwningContainer * cont)
887 {
888  return emitrs_yaml(n, cont);
889 }
890 template<class CharOwningContainer>
891 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(ConstNodeRef const& n)
892 {
893  return emitrs_yaml<CharOwningContainer>(n);
894 }
895 
896 /** @endcond */
897 
898 
899 } // namespace yml
900 } // namespace c4
901 
902 C4_SUPPRESS_WARNING_GCC_CLANG_POP
903 
904 #undef RYML_DEPRECATE_EMIT
905 #undef RYML_DEPRECATE_EMITRS
906 
907 #include "c4/yml/emit.def.hpp" // NOLINT
908 
909 #endif /* _C4_YML_EMIT_HPP_ */
Holds a pointer to an existing tree, and a node id.
Definition: node.hpp:855
Tree const * tree() const noexcept
Definition: node.hpp:926
id_type id() const noexcept
Definition: node.hpp:927
bool readable() const noexcept
because a ConstNodeRef cannot be used to write to the tree, readable() has the same meaning as !...
Definition: node.hpp:912
A stateful emitter, for use with a writer such as WriterBuf, WriterFile, or WriterOStream.
Definition: emit.hpp:118
Callbacks const & callbacks() const
Definition: tree.hpp:241
bool empty() const
Definition: tree.hpp:235
id_type root_id()
Get the id of the root node.
Definition: tree.hpp:288
Definitions for emit functions.
EmitOptionFlags_e json_error_flags() const noexcept
Definition: emit.hpp:76
id_type max_depth() const noexcept
get the max depth for emitted trees (to prevent a stack overflow)
Definition: emit.hpp:177
substr emit_as(EmitType_e type, ConstNodeRef const &n, bool error_on_excess=true)
emit starting at the given node
Definition: emit.hpp:161
as_yaml(Tree const &t, size_t id, EmitOptions const &opts={})
Definition: emit.hpp:419
Emitter(EmitOptions const &opts, Args &&...args)
Construct the emitter and its internal Writer state.
Definition: emit.hpp:133
void max_depth(id_type max_depth) noexcept
set the max depth for emitted trees (to prevent a stack overflow)
Definition: emit.hpp:175
as_yaml(Tree const &t, EmitOptions const &opts={})
Definition: emit.hpp:418
Tree const * tree
Definition: emit.hpp:415
substr emit_as(EmitType_e type, Tree const &t, bool error_on_excess=true)
emit starting at the root node
Definition: emit.hpp:154
Emitter(Args &&...args)
Construct the emitter and its internal Writer state, using default emit options.
Definition: emit.hpp:125
id_type max_depth() const noexcept
Definition: emit.hpp:88
size_t node
Definition: emit.hpp:416
Tree const * tree
Definition: emit.hpp:394
EmitOptions options
Definition: emit.hpp:417
substr emit_as(EmitType_e type, Tree const &t, id_type id, bool error_on_excess)
emit!
Definition: emit.def.hpp:17
EmitOptions options
Definition: emit.hpp:396
bool is_set_(ConstNodeRef n)
Definition: emit.hpp:42
as_json(Tree const &t, EmitOptions const &opts={})
Definition: emit.hpp:397
as_yaml(ConstNodeRef const &n, EmitOptions const &opts={})
Definition: emit.hpp:420
size_t node
Definition: emit.hpp:395
substr emit_yaml(ConstNodeRef const &r, substr buf, bool error_on_excess=true)
(2) like (1), but use default emit options
Definition: emit.hpp:559
as_json(ConstNodeRef const &n, EmitOptions const &opts={})
Definition: emit.hpp:399
EmitOptions const & options() const noexcept
get the emit options for this object
Definition: emit.hpp:172
substr emit_json(ConstNodeRef const &r, substr buf, bool error_on_excess=true)
(2) like (1), but use default emit options
Definition: emit.hpp:582
EmitOptions & max_depth(id_type d) noexcept
Definition: emit.hpp:89
EmitOptions & json_error_flags(EmitOptionFlags_e d) noexcept
Definition: emit.hpp:77
as_json(Tree const &t, size_t id, EmitOptions const &opts={})
Definition: emit.hpp:398
CharOwningContainer emitrs_yaml(ConstNodeRef const &n, EmitOptions const &opts={})
(3) emit+resize: YAML to a newly-created std::string/std::vector-like container.
Definition: emit.hpp:790
CharOwningContainer emitrs_json(ConstNodeRef const &n, EmitOptions const &opts={})
(3) emit+resize: JSON to a newly-created std::string/std::vector-like container.
Definition: emit.hpp:801
OStream & operator<<(OStream &s, as_yaml const &y)
emit yaml to an STL-like stream
Definition: emit.hpp:436
EmitType_e
Specifies the type of content to emit.
Definition: emit.hpp:51
@ EMIT_YAML
emit YAML
Definition: emit.hpp:52
@ EMIT_JSON
emit JSON
Definition: emit.hpp:53
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition: node_type.hpp:29
@ VALANCH
the val has an &anchor
Definition: node_type.hpp:45
@ VALREF
a *reference: the val references an &anchor
Definition: node_type.hpp:43
@ KEY
is member of a map
Definition: node_type.hpp:36
@ KEYQUO
key style is one of ', ", > or |
Definition: node_type.hpp:89
@ VAL_STYLE
mask of all the scalar styles for val (not container styles!)
Definition: node_type.hpp:92
@ VAL
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
Definition: node_type.hpp:37
@ KEY_STYLE
mask of all the scalar styles for key (not container styles!)
Definition: node_type.hpp:91
@ KEYREF
a *reference: the key references an &anchor
Definition: node_type.hpp:42
@ KEYANCH
the key has an &anchor
Definition: node_type.hpp:44
@ VALQUO
val style is one of ', ", > or |
Definition: node_type.hpp:90
@ CONTAINER_STYLE
Definition: node_type.hpp:96
substr to_substr(substr s) noexcept
neutral version for use in generic code
Definition: substr.hpp:2184
bool operator==(const char(&s)[N], basic_substring< C > const that) noexcept
Definition: substr.hpp:2223
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
Definition: common.hpp:253
@ NONE
an index to none
Definition: common.hpp:260
Definition: common.cpp:12
Node classes.
A lightweight object containing options to be used when emitting.
Definition: emit.hpp:63
a node scalar is a csubstr, which may be tagged and anchored.
Definition: tree.hpp:64
wraps a NodeType_e element with some syntactic sugar and predicates
Definition: node_type.hpp:120
mark a tree or node to be emitted as yaml when using operator<<, with options.
Definition: emit.hpp:393
mark a tree or node to be emitted as yaml when using operator<< .
Definition: emit.hpp:414