rapidyaml  0.13.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 // NOLINTBEGIN(modernize-avoid-c-style-cast)
21 
22 
23 //-----------------------------------------------------------------------------
24 //-----------------------------------------------------------------------------
25 //-----------------------------------------------------------------------------
26 
27 namespace c4 {
28 namespace yml {
29 
30 /** @addtogroup doc_emit
31  *
32  * @{
33  */
34 
35 // fwd declarations
36 template<class Writer> class Emitter;
37 template<class OStream>
41 
42 
43 //-----------------------------------------------------------------------------
44 //-----------------------------------------------------------------------------
45 //-----------------------------------------------------------------------------
46 
47 /** Specifies the type of content to emit */
48 typedef enum { // NOLINT
49  EMIT_YAML = 0, ///< emit YAML
50  EMIT_JSON = 1, ///< emit JSON
51 } EmitType_e;
52 
53 
54 //-----------------------------------------------------------------------------
55 //-----------------------------------------------------------------------------
56 //-----------------------------------------------------------------------------
57 
58 /** A lightweight object containing options to be used when emitting. */
60 {
61 public:
62 
63  /** @cond dev */
64  typedef enum : uint32_t { // NOLINT
65  EMIT_NONROOT_KEY = 1u << 0u,
66  EMIT_NONROOT_DASH = 1u << 1u,
67  EMIT_NONROOT_MARKUP = EMIT_NONROOT_KEY|EMIT_NONROOT_DASH,
68  INDENT_FLOW_ML = 1u << 2u,
69  JSON_ERR_ON_TAG = 1u << 3u,
70  JSON_ERR_ON_ANCHOR = 1u << 4u,
71  _JSON_ERR_MASK = JSON_ERR_ON_TAG|JSON_ERR_ON_ANCHOR,
72  DEFAULT_FLAGS = EMIT_NONROOT_KEY|INDENT_FLOW_ML,
73  } EmitOptionFlags_e;
74  /** @endcond */
75 
76 public:
77 
78  /** @name option flags
79  *
80  * @{ */
81 
82  C4_ALWAYS_INLINE bool emit_nonroot_key() const noexcept { return (m_option_flags & EMIT_NONROOT_KEY) != 0; }
83  EmitOptions& emit_nonroot_key(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | EMIT_NONROOT_KEY) : (m_option_flags & ~EMIT_NONROOT_KEY)); return *this; }
84 
85  C4_ALWAYS_INLINE bool emit_nonroot_dash() const noexcept { return (m_option_flags & EMIT_NONROOT_DASH) != 0; }
86  EmitOptions& emit_nonroot_dash(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | EMIT_NONROOT_DASH) : (m_option_flags & ~EMIT_NONROOT_DASH)); return *this; }
87 
88  C4_ALWAYS_INLINE bool indent_flow_ml() const noexcept { return (m_option_flags & INDENT_FLOW_ML) != 0; }
89  EmitOptions& indent_flow_ml(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | INDENT_FLOW_ML) : (m_option_flags & ~INDENT_FLOW_ML)); return *this; }
90 
91  C4_ALWAYS_INLINE EmitOptionFlags_e json_error_flags() const noexcept { return (EmitOptionFlags_e)(m_option_flags & _JSON_ERR_MASK); }
92  EmitOptions& json_error_flags(EmitOptionFlags_e d) noexcept { m_option_flags = (EmitOptionFlags_e)(d & _JSON_ERR_MASK); return *this; }
93 
94  /** @} */
95 
96 public:
97 
98  /** @name max depth for the emitted tree
99  *
100  * This makes the emitter fail when emitting trees exceeding the
101  * max_depth.
102  *
103  * @{ */
104  C4_ALWAYS_INLINE id_type max_depth() const noexcept { return m_max_depth; }
105  EmitOptions& max_depth(id_type d) noexcept { m_max_depth = d; return *this; }
106  static constexpr const id_type max_depth_default = 64;
107  /** @} */
108 
109 public:
110 
111  bool operator== (const EmitOptions& that) const noexcept
112  {
113  return m_max_depth == that.m_max_depth &&
114  m_option_flags == that.m_option_flags;
115  }
116 
117 private:
118 
119  /** @cond dev */
120  id_type m_max_depth{max_depth_default};
121  EmitOptionFlags_e m_option_flags{DEFAULT_FLAGS};
122  /** @endcond */
123 };
124 
125 
126 //-----------------------------------------------------------------------------
127 //-----------------------------------------------------------------------------
128 //-----------------------------------------------------------------------------
129 
130 /** A stateful emitter, for use with a writer such as @ref WriterBuf,
131  * @ref WriterFile, or @ref WriterOStream */
132 template<class Writer>
133 class Emitter : public Writer
134 {
135 public:
136 
137  /** Construct the emitter and its internal Writer state.
138  *
139  * @param opts @ref EmitOptions
140  * @param args arguments to be forwarded to the constructor of the writer.
141  */
142  template<class ...WriterArgs>
143  Emitter(EmitOptions const& opts, WriterArgs &&...args)
144  : Writer(std::forward<WriterArgs>(args)...)
145  , m_tree()
146  , m_opts(opts)
147  , m_col()
148  , m_depth()
149  , m_ilevel()
150  , m_pws()
151  {}
152 
153  /** Construct the emitter and its internal Writer state, using default emit options.
154  * @param args arguments to be forwarded to the constructor of the writer.
155  */
156  template<class ...WriterArgs>
157  Emitter(WriterArgs &&...args)
158  : Writer(std::forward<WriterArgs>(args)...)
159  , m_tree()
160  , m_opts()
161  , m_col()
162  , m_depth()
163  , m_ilevel()
164  , m_pws()
165  {}
166 
167 public:
168 
169  /** emit!
170  *
171  * When writing to a buffer, returns a substr of the emitted YAML.
172  * If the given buffer has insufficient space, the returned substr
173  * will be null and its size will be the needed space. Whatever
174  * the size of the buffer, it is guaranteed that no writes are
175  * done past its end.
176  *
177  * When writing to a file, the returned substr will be null, but its
178  * length will be set to the number of bytes written.
179  *
180  * @param type specify what to emit (YAML or JSON)
181  * @param t the tree to emit
182  * @param id the id of the node to emit
183  * @param error_on_excess when true, an error is raised when the
184  * output buffer is too small for the emitted YAML/JSON
185  * */
186  substr emit_as(EmitType_e type, Tree const& t, id_type id, bool error_on_excess);
187  /** emit starting at the root node */
188  substr emit_as(EmitType_e type, Tree const& t, bool error_on_excess=true)
189  {
190  if(t.empty())
191  return {};
192  return this->emit_as(type, t, t.root_id(), error_on_excess);
193  }
194  /** emit starting at the given node */
195  substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true)
196  {
197  if(!n.readable())
198  return {};
199  return this->emit_as(type, *n.tree(), n.id(), error_on_excess);
200  }
201 
202 public:
203 
204  /** get the emit options for this object */
205  EmitOptions const& options() const noexcept { return m_opts; }
206  /** set the emit options for this object */
207  void options(EmitOptions opts) noexcept { m_opts = opts; }
208 
209  /** set the max depth for emitted trees (to prevent a stack overflow) */
210  void max_depth(id_type max_depth) noexcept { m_opts.max_depth(max_depth); }
211  /** get the max depth for emitted trees (to prevent a stack overflow) */
212  id_type max_depth() const noexcept { return m_opts.max_depth(); }
213 
214 private:
215 
216  /** @cond dev */
217 
218  void _emit_yaml(id_type id);
219 
220  void _visit_stream(id_type id);
221  void _visit_doc(id_type id);
222  void _visit_doc_val(id_type id);
223  void _visit_blck_container(id_type id);
224  void _visit_flow_container(id_type id);
225 
226  void _visit_flow_sl(id_type id);
227  void _visit_flow_sl_seq(id_type id);
228  void _visit_flow_sl_map(id_type id);
229 
230  void _visit_flow_ml(id_type id);
231  void _visit_flow_ml_seq(id_type id);
232  void _visit_flow_ml_map(id_type id);
233 
234  void _visit_blck(id_type id);
235  void _visit_blck_seq(id_type id);
236  void _visit_blck_map(id_type id);
237 
238  void _top_open_entry(id_type id);
239  void _top_close_entry(id_type id);
240  void _blck_seq_open_entry(id_type id);
241  void _blck_map_open_entry(id_type id);
242  void _blck_close_entry(id_type id);
243  void _blck_write_qmrk(id_type id, csubstr key, type_bits type, bool has_qmrk_comments);
244  void _blck_write_scalar(csubstr str, type_bits type);
245 
246  void _flow_seq_open_entry(id_type id);
247  void _flow_map_open_entry(id_type id);
248  void _flow_close_entry_sl(id_type id, id_type last_sibling);
249  void _flow_close_entry_ml(id_type id, id_type last_sibling);
250  void _flow_write_scalar(csubstr str, type_bits type);
251 
252 private:
253 
254  void _json_emit(id_type id);
255  void _write_scalar_literal(csubstr s, id_type level);
256  void _write_scalar_folded(csubstr s, id_type level);
257  void _write_scalar_squo(csubstr s, id_type level);
258  void _write_scalar_dquo(csubstr s, id_type level);
259  void _write_scalar_plain(csubstr s, id_type level);
260 
261  size_t _write_escaped_newlines(csubstr s, size_t i);
262  size_t _write_indented_block(csubstr s, size_t i, id_type level);
263 
264 private:
265 
266  void _json_visit_ml(id_type id, id_type depth);
267  void _json_visit_sl(id_type id, id_type depth);
268  bool _json_maybe_write_naninf(csubstr s);
269  void _json_writek(id_type id, NodeType ty);
270  void _json_writev(id_type id, NodeType ty);
271  void _json_write_scalar_dquo(csubstr s);
272  void _json_write_number(csubstr s);
273 
274 private:
275 
276  void _write_tag(csubstr tag)
277  {
278  if(!tag.begins_with('!'))
279  _write('!');
280  _write(tag);
281  }
282  void _write_ref(csubstr ref)
283  {
284  if(ref != "<<")
285  {
286  if(!ref.begins_with('*'))
287  _write('*');
288  _write(ref);
289  }
290  }
291 
292 private:
293 
294  C4_ALWAYS_INLINE void _indent(id_type level)
295  {
296  size_t num = (size_t)(2u * level);
297  this->Writer::_do_write(' ', num);
298  m_col += num;
299  }
300 
301  template<size_t N>
302  C4_ALWAYS_INLINE void _write(const char (&a)[N])
303  {
304  this->Writer::_do_write(std::forward<const char (&)[N]>(a));
305  m_col += N-1;
306  }
307  C4_ALWAYS_INLINE void _write(csubstr s)
308  {
309  this->Writer::_do_write(s);
310  m_col += s.len;
311  }
312  C4_ALWAYS_INLINE void _write(char c)
313  {
314  this->Writer::_do_write(c);
315  ++m_col;
316  }
317  C4_ALWAYS_INLINE void _write(char c, size_t num)
318  {
319  this->Writer::_do_write(c, num);
320  m_col += num;
321  }
322 
323  /// write a newline and reset the column
324  C4_ALWAYS_INLINE void _newl()
325  {
326  this->Writer::_do_write('\n');
327  m_col = 0;
328  }
329 
330 private: // pending whitespace
331 
332  /// pending whitespace
333  typedef enum : uint32_t { _PWS_NONE, _PWS_SPACE, _PWS_NEWL } Pws_e; // NOLINT
334 
335  /// set pending whitespace, ignoring pending
336  C4_ALWAYS_INLINE void _pend_none() noexcept
337  {
338  m_pws = _PWS_NONE;
339  }
340  /// set pending whitespace, ignoring pending
341  C4_ALWAYS_INLINE void _pend_newl() noexcept
342  {
343  m_pws = _PWS_NEWL;
344  }
345  /// set pending whitespace, ignoring pending
346  C4_ALWAYS_INLINE void _pend_space() noexcept
347  {
348  m_pws = _PWS_SPACE;
349  }
350  /// write pending whitespace, and then set the next pending whitespace
351  C4_ALWAYS_INLINE void _write_pws_and_pend(Pws_e next=_PWS_NONE) noexcept
352  {
353  if(m_pws == _PWS_SPACE)
354  {
355  _write(' ');
356  }
357  else if(m_pws == _PWS_NEWL)
358  {
359  _newl();
360  _indent(m_ilevel);
361  }
362  m_pws = next;
363  }
364 
365 private:
366 
367  Tree const* C4_RESTRICT m_tree;
368  EmitOptions m_opts;
369  size_t m_col;
370  id_type m_depth;
371  id_type m_ilevel;
372  Pws_e m_pws;
373 
374 private:
375 
376  // g++-4.8 has problems with the operand types here...
377  #if defined(__GNUC__) && (__GNUC__ < 5) && (!defined(__clang__))
378  #pragma GCC diagnostic push
379  #pragma GCC diagnostic ignored "-Wparentheses"
380  #endif
381  enum : type_bits { // NOLINT
382  _styles_block_key = KEY_LITERAL|KEY_FOLDED,
383  _styles_block_val = VAL_LITERAL|VAL_FOLDED,
384  _styles_block = ((type_bits)_styles_block_key) | ((type_bits)_styles_block_val),
385  _styles_flow_key = KEY_STYLE & (~((type_bits)_styles_block_key)),
386  _styles_flow_val = VAL_STYLE & (~((type_bits)_styles_block_val)),
387  _styles_flow = ((type_bits)_styles_flow_key) | ((type_bits)_styles_flow_val),
388  _styles_squo = KEY_SQUO|VAL_SQUO,
389  _styles_dquo = KEY_DQUO|VAL_DQUO,
390  _styles_plain = KEY_PLAIN|VAL_PLAIN,
391  _styles_literal = KEY_LITERAL|VAL_LITERAL,
392  _styles_folded = KEY_FOLDED|VAL_FOLDED,
393  };
394  #if defined(__GNUC__) && (__GNUC__ < 5) && (!defined(__clang__))
395  #pragma GCC diagnostic pop
396  #endif
397 
398  /** @endcond */
399 };
400 
401 
402 //-----------------------------------------------------------------------------
403 //-----------------------------------------------------------------------------
404 //-----------------------------------------------------------------------------
405 
406 /** @defgroup doc_emit_to_file Emit to file
407  *
408  * @{
409  */
410 
411 
412 // emit from tree and node id -----------------------
413 
414 /** (1) emit YAML to the given file, starting at the given node. A null
415  * file defaults to stdout. Return the number of bytes written. */
416 inline size_t emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, FILE *f)
417 {
418  EmitterFile em(opts, f);
419  return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
420 }
421 /** (2) like (1), but use default emit options */
422 inline size_t emit_yaml(Tree const& t, id_type id, FILE *f)
423 {
424  EmitterFile em(f);
425  return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
426 }
427 /** (1) emit JSON to the given file, starting at the given node. A null
428  * file defaults to stdout. Return the number of bytes written. */
429 inline size_t emit_json(Tree const& t, id_type id, EmitOptions const& opts, FILE *f)
430 {
431  EmitterFile em(opts, f);
432  return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
433 }
434 /** (2) like (1), but use default emit options */
435 inline size_t emit_json(Tree const& t, id_type id, FILE *f)
436 {
437  EmitterFile em(f);
438  return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
439 }
440 
441 
442 // emit from root -------------------------
443 
444 /** (1) emit YAML to the given file, starting at the root node. A null file defaults to stdout.
445  * Return the number of bytes written. */
446 inline size_t emit_yaml(Tree const& t, EmitOptions const& opts, FILE *f=nullptr)
447 {
448  EmitterFile em(opts, f);
449  return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
450 }
451 /** (2) like (1), but use default emit options */
452 inline size_t emit_yaml(Tree const& t, FILE *f=nullptr)
453 {
454  EmitterFile em(f);
455  return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
456 }
457 /** (1) emit JSON to the given file. A null file defaults to stdout.
458  * Return the number of bytes written. */
459 inline size_t emit_json(Tree const& t, EmitOptions const& opts, FILE *f=nullptr)
460 {
461  EmitterFile em(opts, f);
462  return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
463 }
464 /** (2) like (1), but use default emit options */
465 inline size_t emit_json(Tree const& t, FILE *f=nullptr)
466 {
467  EmitterFile em(f);
468  return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
469 }
470 
471 
472 // emit from ConstNodeRef ------------------------
473 
474 /** (1) emit YAML to the given file. A null file defaults to stdout.
475  * Return the number of bytes written. */
476 inline size_t emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, FILE *f=nullptr)
477 {
478  if(!r.readable())
479  return {};
480  EmitterFile em(opts, f);
481  return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
482 }
483 /** (2) like (1), but use default emit options */
484 inline size_t emit_yaml(ConstNodeRef const& r, FILE *f=nullptr)
485 {
486  if(!r.readable())
487  return {};
488  EmitterFile em(f);
489  return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
490 }
491 /** (1) emit JSON to the given file. A null file defaults to stdout.
492  * Return the number of bytes written. */
493 inline size_t emit_json(ConstNodeRef const& r, EmitOptions const& opts, FILE *f=nullptr)
494 {
495  if(!r.readable())
496  return {};
497  EmitterFile em(opts, f);
498  return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
499 }
500 /** (2) like (1), but use default emit options */
501 inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr)
502 {
503  if(!r.readable())
504  return {};
505  EmitterFile em(f);
506  return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
507 }
508 
509 /** @} */
510 
511 
512 //-----------------------------------------------------------------------------
513 
514 /** @defgroup doc_emit_to_ostream Emit to an STL-like ostream
515  *
516  * @{
517  */
518 
519 /** emit YAML to an STL-like ostream */
520 template<class OStream>
521 inline OStream& operator<< (OStream& s, Tree const& t)
522 {
524  em.emit_as(EMIT_YAML, t);
525  return s;
526 }
527 
528 /** emit YAML to an STL-like ostream
529  * @overload */
530 template<class OStream>
531 inline OStream& operator<< (OStream& s, ConstNodeRef const& n)
532 {
533  if(!n.readable())
534  return s;
536  em.emit_as(EMIT_YAML, n);
537  return s;
538 }
539 
540 /** mark a tree or node to be emitted as yaml when using @ref
541  * operator<<, with options. For example:
542  *
543  * ```cpp
544  * Tree t = parse_in_arena("{foo: bar}");
545  * std::cout << t; // emits YAML
546  * std::cout << as_yaml(t); // emits YAML, same as above
547  * std::cout << as_yaml(t, EmitOptions().max_depth(10)); // emits JSON with a max tree depth
548  * ```
549  *
550  * @see @ref operator<< */
551 struct as_json
552 {
553  Tree const* tree;
556  as_json(Tree const& t, EmitOptions const& opts={}) : tree(&t), node(t.empty() ? NONE : t.root_id()), options(opts) {}
557  as_json(Tree const& t, id_type id, EmitOptions const& opts={}) : tree(&t), node(id), options(opts) {}
558  as_json(ConstNodeRef const& n, EmitOptions const& opts={}) : tree(n.tree()), node(n.id()), options(opts) {}
559 };
560 
561 /** mark a tree or node to be emitted as yaml when using @ref
562  * operator<< . For example:
563  *
564  * ```cpp
565  * Tree t = parse_in_arena("{foo: bar}");
566  * std::cout << t; // emits YAML
567  * std::cout << as_json(t); // emits JSON
568  * std::cout << as_json(t, EmitOptions().max_depth(10)); // emits JSON with a max tree depth
569  * ```
570  *
571  * @see @ref operator<< */
572 struct as_yaml
573 {
574  Tree const* tree;
577  as_yaml(Tree const& t, EmitOptions const& opts={}) : tree(&t), node(t.empty() ? NONE : t.root_id()), options(opts) {}
578  as_yaml(Tree const& t, id_type id, EmitOptions const& opts={}) : tree(&t), node(id), options(opts) {}
579  as_yaml(ConstNodeRef const& n, EmitOptions const& opts={}) : tree(n.tree()), node(n.id()), options(opts) {}
580 };
581 
582 /** emit json to an STL-like stream */
583 template<class OStream>
584 inline OStream& operator<< (OStream& s, as_json const& j)
585 {
586  if(!j.tree || j.tree->empty())
587  return s;
589  em.emit_as(EMIT_JSON, *j.tree, j.node != NONE ? j.node : j.tree->root_id(), true);
590  return s;
591 }
592 
593 /** emit yaml to an STL-like stream */
594 template<class OStream>
595 inline OStream& operator<< (OStream& s, as_yaml const& y)
596 {
597  if(!y.tree || y.tree->empty())
598  return s;
600  em.emit_as(EMIT_YAML, *y.tree, y.node != NONE ? y.node : y.tree->root_id(), true);
601  return s;
602 }
603 
604 /** @} */
605 
606 
607 //-----------------------------------------------------------------------------
608 
609 /** @defgroup doc_emit_to_buffer Emit to memory buffer
610  *
611  * @{
612  */
613 
614 // emit from tree and node id -----------------------
615 
616 /** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
617  * @param t the tree to emit.
618  * @param id the node where to start emitting.
619  * @param opts emit options.
620  * @param buf the output buffer.
621  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
622  * @return a substr trimmed to the result in the output buffer. If the buffer is
623  * insufficient (when error_on_excess is false), the string pointer of the
624  * result will be set to null, and the length will report the required buffer size. */
625 inline substr emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true)
626 {
627  EmitterBuf em(opts, buf);
628  return em.emit_as(EMIT_YAML, t, id, error_on_excess);
629 }
630 /** (2) like (1), but use default emit options */
631 inline substr emit_yaml(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
632 {
633  EmitterBuf em(buf);
634  return em.emit_as(EMIT_YAML, t, id, error_on_excess);
635 }
636 /** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
637  * @param t the tree to emit.
638  * @param id the node where to start emitting.
639  * @param opts emit options.
640  * @param buf the output buffer.
641  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
642  * @return a substr trimmed to the result in the output buffer. If the buffer is
643  * insufficient (when error_on_excess is false), the string pointer of the
644  * result will be set to null, and the length will report the required buffer size. */
645 inline substr emit_json(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true)
646 {
647  EmitterBuf em(opts, buf);
648  return em.emit_as(EMIT_JSON, t, id, error_on_excess);
649 }
650 /** (2) like (1), but use default emit options */
651 inline substr emit_json(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
652 {
653  EmitterBuf em(buf);
654  return em.emit_as(EMIT_JSON, t, id, error_on_excess);
655 }
656 
657 
658 // emit from root -------------------------
659 
660 /** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
661  * @param t the tree; will be emitted from the root node.
662  * @param opts emit options.
663  * @param buf the output buffer.
664  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
665  * @return a substr trimmed to the result in the output buffer. If the buffer is
666  * insufficient (when error_on_excess is false), the string pointer of the
667  * result will be set to null, and the length will report the required buffer size. */
668 inline substr emit_yaml(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true)
669 {
670  EmitterBuf em(opts, buf);
671  return em.emit_as(EMIT_YAML, t, error_on_excess);
672 }
673 /** (2) like (1), but use default emit options */
674 inline substr emit_yaml(Tree const& t, substr buf, bool error_on_excess=true)
675 {
676  EmitterBuf em(buf);
677  return em.emit_as(EMIT_YAML, t, error_on_excess);
678 }
679 /** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
680  * @param t the tree; will be emitted from the root node.
681  * @param opts emit options.
682  * @param buf the output buffer.
683  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
684  * @return a substr trimmed to the result in the output buffer. If the buffer is
685  * insufficient (when error_on_excess is false), the string pointer of the
686  * result will be set to null, and the length will report the required buffer size. */
687 inline substr emit_json(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true)
688 {
689  EmitterBuf em(opts, buf);
690  return em.emit_as(EMIT_JSON, t, error_on_excess);
691 }
692 /** (2) like (1), but use default emit options */
693 inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true)
694 {
695  EmitterBuf em(buf);
696  return em.emit_as(EMIT_JSON, t, error_on_excess);
697 }
698 
699 
700 // emit from ConstNodeRef ------------------------
701 
702 /** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
703  * @param r the starting node.
704  * @param buf the output buffer.
705  * @param opts emit options.
706  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
707  * @return a substr trimmed to the result in the output buffer. If the buffer is
708  * insufficient (when error_on_excess is false), the string pointer of the
709  * result will be set to null, and the length will report the required buffer size. */
710 inline substr emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true)
711 {
712  if(!r.readable())
713  return {};
714  EmitterBuf em(opts, buf);
715  return em.emit_as(EMIT_YAML, r, error_on_excess);
716 }
717 /** (2) like (1), but use default emit options */
718 inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
719 {
720  if(!r.readable())
721  return {};
722  EmitterBuf em(buf);
723  return em.emit_as(EMIT_YAML, r, error_on_excess);
724 }
725 /** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
726  * @param r the starting node.
727  * @param buf the output buffer.
728  * @param opts emit options.
729  * @param error_on_excess Raise an error if the space in the buffer is insufficient.
730  * @return a substr trimmed to the result in the output buffer. If the buffer is
731  * insufficient (when error_on_excess is false), the string pointer of the
732  * result will be set to null, and the length will report the required buffer size. */
733 inline substr emit_json(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true)
734 {
735  if(!r.readable())
736  return {};
737  EmitterBuf em(opts, buf);
738  return em.emit_as(EMIT_JSON, r, error_on_excess);
739 }
740 /** (2) like (1), but use default emit options */
741 inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
742 {
743  if(!r.readable())
744  return {};
745  EmitterBuf em(buf);
746  return em.emit_as(EMIT_JSON, r, error_on_excess);
747 }
748 
749 
750 //-----------------------------------------------------------------------------
751 
752 /** @defgroup doc_emit_to_container Emit to resizeable container
753  *
754  * @{
755  */
756 
757 // emit from tree and node id ---------------------------
758 
759 /** (1) emit+resize: emit YAML to the given `std::string`/`std::vector`-like
760  * container, resizing it as needed to fit the emitted YAML. If @p append is
761  * set to true, the emitted YAML is appended at the end of the container.
762  *
763  * @return a substr trimmed to the emitted YAML (excluding the initial contents, when appending) */
764 template<class CharOwningContainer>
765 substr emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
766 {
767  size_t startpos = append ? cont->size() : 0u;
768  cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail
769  substr buf = to_substr(*cont).sub(startpos);
770  substr ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/false);
771  if(ret.str == nullptr && ret.len > 0)
772  {
773  cont->resize(startpos + ret.len);
774  buf = to_substr(*cont).sub(startpos);
775  ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/true);
776  }
777  else
778  {
779  cont->resize(startpos + ret.len);
780  }
781  return ret;
782 }
783 /** (2) like (1), but use default emit options */
784 template<class CharOwningContainer>
785 substr emitrs_yaml(Tree const& t, id_type id, CharOwningContainer * cont, bool append=false)
786 {
787  return emitrs_yaml(t, id, EmitOptions{}, cont, append);
788 }
789 /** (1) emit+resize: emit JSON to the given `std::string`/`std::vector`-like
790  * container, resizing it as needed to fit the emitted JSON. If @p append is
791  * set to true, the emitted YAML is appended at the end of the container.
792  *
793  * @return a substr trimmed to the emitted JSON (excluding the initial contents, when appending) */
794 template<class CharOwningContainer>
795 substr emitrs_json(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
796 {
797  const size_t startpos = append ? cont->size() : 0u;
798  cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail
799  substr buf = to_substr(*cont).sub(startpos);
800  EmitterBuf em(opts, buf);
801  substr ret = emit_json(t, id, opts, buf, /*error_on_excess*/false);
802  if(ret.str == nullptr && ret.len > 0)
803  {
804  cont->resize(startpos + ret.len);
805  buf = to_substr(*cont).sub(startpos);
806  ret = emit_json(t, id, opts, buf, /*error_on_excess*/true);
807  }
808  else
809  {
810  cont->resize(startpos + ret.len);
811  }
812  return ret;
813 }
814 /** (2) like (1), but use default emit options */
815 template<class CharOwningContainer>
816 substr emitrs_json(Tree const& t, id_type id, CharOwningContainer * cont, bool append=false)
817 {
818  return emitrs_json(t, id, EmitOptions{}, cont, append);
819 }
820 
821 
822 /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
823 template<class CharOwningContainer>
824 CharOwningContainer emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts={})
825 {
826  CharOwningContainer c;
827  emitrs_yaml(t, id, opts, &c);
828  return c;
829 }
830 /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
831 template<class CharOwningContainer>
832 CharOwningContainer emitrs_json(Tree const& t, id_type id, EmitOptions const& opts={})
833 {
834  CharOwningContainer c;
835  emitrs_json(t, id, opts, &c);
836  return c;
837 }
838 
839 
840 // emit from root -------------------------
841 
842 /** (1) emit+resize: YAML to the given `std::string`/`std::vector`-like
843  * container, resizing it as needed to fit the emitted YAML.
844  * @return a substr trimmed to the new emitted contents. */
845 template<class CharOwningContainer>
846 substr emitrs_yaml(Tree const& t, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
847 {
848  if(t.empty())
849  return {};
850  return emitrs_yaml(t, t.root_id(), opts, cont, append);
851 }
852 /** (2) like (1), but use default emit options */
853 template<class CharOwningContainer>
854 substr emitrs_yaml(Tree const& t, CharOwningContainer * cont, bool append=false)
855 {
856  if(t.empty())
857  return {};
858  return emitrs_yaml(t, t.root_id(), EmitOptions{}, cont, append);
859 }
860 /** (1) emit+resize: JSON to the given `std::string`/`std::vector`-like
861  * container, resizing it as needed to fit the emitted JSON.
862  * @return a substr trimmed to the new emitted contents. */
863 template<class CharOwningContainer>
864 substr emitrs_json(Tree const& t, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
865 {
866  if(t.empty())
867  return {};
868  return emitrs_json(t, t.root_id(), opts, cont, append);
869 }
870 /** (2) like (1), but use default emit options */
871 template<class CharOwningContainer>
872 substr emitrs_json(Tree const& t, CharOwningContainer * cont, bool append=false)
873 {
874  if(t.empty())
875  return {};
876  return emitrs_json(t, t.root_id(), EmitOptions{}, cont, append);
877 }
878 
879 
880 /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
881 template<class CharOwningContainer>
882 CharOwningContainer emitrs_yaml(Tree const& t, EmitOptions const& opts={})
883 {
884  CharOwningContainer c;
885  if(t.empty())
886  return c;
887  emitrs_yaml(t, t.root_id(), opts, &c);
888  return c;
889 }
890 /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
891 template<class CharOwningContainer>
892 CharOwningContainer emitrs_json(Tree const& t, EmitOptions const& opts={})
893 {
894  CharOwningContainer c;
895  if(t.empty())
896  return c;
897  emitrs_json(t, t.root_id(), opts, &c);
898  return c;
899 }
900 
901 
902 // emit from ConstNodeRef ------------------------
903 
904 
905 /** (1) emit+resize: YAML to the given `std::string`/`std::vector`-like container,
906  * resizing it as needed to fit the emitted YAML.
907  * @return a substr trimmed to the new emitted contents */
908 template<class CharOwningContainer>
909 substr emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
910 {
911  if(!n.readable())
912  return {};
913  return emitrs_yaml(*n.tree(), n.id(), opts, cont, append);
914 }
915 /** (2) like (1), but use default emit options */
916 template<class CharOwningContainer>
917 substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont, bool append=false)
918 {
919  if(!n.readable())
920  return {};
921  return emitrs_yaml(*n.tree(), n.id(), EmitOptions{}, cont, append);
922 }
923 /** (1) emit+resize: JSON to the given `std::string`/`std::vector`-like container,
924  * resizing it as needed to fit the emitted JSON.
925  * @return a substr trimmed to the new emitted contents */
926 template<class CharOwningContainer>
927 substr emitrs_json(ConstNodeRef const& n, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
928 {
929  if(!n.readable())
930  return {};
931  return emitrs_json(*n.tree(), n.id(), opts, cont, append);
932 }
933 /** (2) like (1), but use default emit options */
934 template<class CharOwningContainer>
935 substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont, bool append=false)
936 {
937  if(!n.readable())
938  return {};
939  return emitrs_json(*n.tree(), n.id(), EmitOptions{}, cont, append);
940 }
941 
942 
943 /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
944 template<class CharOwningContainer>
945 CharOwningContainer emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts={})
946 {
947  if(!n.readable())
948  return {};
949  CharOwningContainer c;
950  emitrs_yaml(*n.tree(), n.id(), opts, &c);
951  return c;
952 }
953 /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
954 template<class CharOwningContainer>
955 CharOwningContainer emitrs_json(ConstNodeRef const& n, EmitOptions const& opts={})
956 {
957  if(!n.readable())
958  return {};
959  CharOwningContainer c;
960  emitrs_json(*n.tree(), n.id(), opts, &c);
961  return c;
962 }
963 
964 
965 /** @} */
966 
967 
968 //-----------------------------------------------------------------------------
969 
970 /** @cond dev */
971 
972 #define RYML_DEPRECATE_EMIT \
973  RYML_DEPRECATED("use emit_yaml() instead. " \
974  "See https://github.com/biojppm/rapidyaml/issues/120")
975 #define RYML_DEPRECATE_EMITRS \
976  RYML_DEPRECATED("use emitrs_yaml() instead. " \
977  "See https://github.com/biojppm/rapidyaml/issues/120")
978 
979 // workaround for Qt emit which is a macro;
980 // see https://github.com/biojppm/rapidyaml/issues/120.
981 // emit is defined in qobjectdefs.h (as an empty define).
982 #ifdef emit
983 #define RYML_TMP_EMIT_
984 #undef emit
985 #endif
986 
987 RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, id_type id, FILE *f)
988 {
989  return emit_yaml(t, id, f);
990 }
991 RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, FILE *f=nullptr)
992 {
993  return emit_yaml(t, f);
994 }
995 RYML_DEPRECATE_EMIT inline size_t emit(ConstNodeRef const& r, FILE *f=nullptr)
996 {
997  return emit_yaml(r, f);
998 }
999 
1000 RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
1001 {
1002  return emit_yaml(t, id, buf, error_on_excess);
1003 }
1004 RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, substr buf, bool error_on_excess=true)
1005 {
1006  return emit_yaml(t, buf, error_on_excess);
1007 }
1008 RYML_DEPRECATE_EMIT inline substr emit(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
1009 {
1010  return emit_yaml(r, buf, error_on_excess);
1011 }
1012 
1013 #ifdef RYML_TMP_EMIT_
1014 #define emit
1015 #undef RYML_TMP_EMIT_
1016 #endif
1017 
1018 template<class CharOwningContainer>
1019 RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, id_type id, CharOwningContainer * cont)
1020 {
1021  return emitrs_yaml(t, id, cont);
1022 }
1023 template<class CharOwningContainer>
1024 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, id_type id)
1025 {
1026  return emitrs_yaml<CharOwningContainer>(t, id);
1027 }
1028 template<class CharOwningContainer>
1029 RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, CharOwningContainer * cont)
1030 {
1031  return emitrs_yaml(t, cont);
1032 }
1033 template<class CharOwningContainer>
1034 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t)
1035 {
1036  return emitrs_yaml<CharOwningContainer>(t);
1037 }
1038 template<class CharOwningContainer>
1039 RYML_DEPRECATE_EMITRS substr emitrs(ConstNodeRef const& n, CharOwningContainer * cont)
1040 {
1041  return emitrs_yaml(n, cont);
1042 }
1043 template<class CharOwningContainer>
1044 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(ConstNodeRef const& n)
1045 {
1046  return emitrs_yaml<CharOwningContainer>(n);
1047 }
1048 
1049 /** @endcond */
1050 
1051 
1052 } // namespace yml
1053 } // namespace c4
1054 
1055 // NOLINTEND(modernize-avoid-c-style-cast)
1056 C4_SUPPRESS_WARNING_GCC_CLANG_POP
1057 
1058 #undef RYML_DEPRECATE_EMIT
1059 #undef RYML_DEPRECATE_EMITRS
1060 
1061 #include "c4/yml/emit.def.hpp" // NOLINT
1062 
1063 #endif /* _C4_YML_EMIT_HPP_ */
Holds a pointer to an existing tree, and a node id.
Definition: node.hpp:856
Tree const * tree() const noexcept
Definition: node.hpp:927
id_type id() const noexcept
Definition: node.hpp:928
bool readable() const noexcept
because a ConstNodeRef cannot be used to write to the tree, readable() has the same meaning as !...
Definition: node.hpp:913
A stateful emitter, for use with a writer such as WriterBuf, WriterFile, or WriterOStream.
Definition: emit.hpp:134
id_type root_id() const
Get the id of the root node. The tree must not be empty.
Definition: tree.hpp:335
bool empty() const
Definition: tree.hpp:282
Definitions for emit functions.
EmitOptionFlags_e json_error_flags() const noexcept
Definition: emit.hpp:91
id_type max_depth() const noexcept
get the max depth for emitted trees (to prevent a stack overflow)
Definition: emit.hpp:212
substr emit_as(EmitType_e type, ConstNodeRef const &n, bool error_on_excess=true)
emit starting at the given node
Definition: emit.hpp:195
bool emit_nonroot_key() const noexcept
Definition: emit.hpp:82
void max_depth(id_type max_depth) noexcept
set the max depth for emitted trees (to prevent a stack overflow)
Definition: emit.hpp:210
as_yaml(Tree const &t, EmitOptions const &opts={})
Definition: emit.hpp:577
Tree const * tree
Definition: emit.hpp:574
void options(EmitOptions opts) noexcept
set the emit options for this object
Definition: emit.hpp:207
substr emit_as(EmitType_e type, Tree const &t, bool error_on_excess=true)
emit starting at the root node
Definition: emit.hpp:188
id_type max_depth() const noexcept
Definition: emit.hpp:104
Tree const * tree
Definition: emit.hpp:553
as_yaml(Tree const &t, id_type id, EmitOptions const &opts={})
Definition: emit.hpp:578
Emitter(WriterArgs &&...args)
Construct the emitter and its internal Writer state, using default emit options.
Definition: emit.hpp:157
EmitOptions options
Definition: emit.hpp:576
Emitter(EmitOptions const &opts, WriterArgs &&...args)
Construct the emitter and its internal Writer state.
Definition: emit.hpp:143
substr emit_as(EmitType_e type, Tree const &t, id_type id, bool error_on_excess)
emit!
Definition: emit.def.hpp:20
EmitOptions options
Definition: emit.hpp:555
EmitOptions & emit_nonroot_key(bool enabled) noexcept
Definition: emit.hpp:83
as_json(Tree const &t, EmitOptions const &opts={})
Definition: emit.hpp:556
as_yaml(ConstNodeRef const &n, EmitOptions const &opts={})
Definition: emit.hpp:579
substr emit_yaml(ConstNodeRef const &r, substr buf, bool error_on_excess=true)
(2) like (1), but use default emit options
Definition: emit.hpp:718
EmitOptions & indent_flow_ml(bool enabled) noexcept
Definition: emit.hpp:89
bool indent_flow_ml() const noexcept
Definition: emit.hpp:88
EmitOptions & emit_nonroot_dash(bool enabled) noexcept
Definition: emit.hpp:86
bool emit_nonroot_dash() const noexcept
Definition: emit.hpp:85
as_json(ConstNodeRef const &n, EmitOptions const &opts={})
Definition: emit.hpp:558
EmitOptions const & options() const noexcept
get the emit options for this object
Definition: emit.hpp:205
substr emit_json(ConstNodeRef const &r, substr buf, bool error_on_excess=true)
(2) like (1), but use default emit options
Definition: emit.hpp:741
EmitOptions & max_depth(id_type d) noexcept
Definition: emit.hpp:105
as_json(Tree const &t, id_type id, EmitOptions const &opts={})
Definition: emit.hpp:557
id_type node
Definition: emit.hpp:575
id_type node
Definition: emit.hpp:554
EmitOptions & json_error_flags(EmitOptionFlags_e d) noexcept
Definition: emit.hpp:92
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:945
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:955
OStream & operator<<(OStream &s, as_yaml const &y)
emit yaml to an STL-like stream
Definition: emit.hpp:595
EmitType_e
Specifies the type of content to emit.
Definition: emit.hpp:48
@ EMIT_YAML
emit YAML
Definition: emit.hpp:49
@ EMIT_JSON
emit JSON
Definition: emit.hpp:50
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition: node_type.hpp:30
@ KEY_DQUO
mark key scalar as double quoted "
Definition: node_type.hpp:69
@ VAL_FOLDED
mark val scalar as multiline, block folded >
Definition: node_type.hpp:66
@ VAL_STYLE
mask of all the scalar styles for val (not container styles!)
Definition: node_type.hpp:93
@ VAL_SQUO
mark val scalar as single quoted '
Definition: node_type.hpp:68
@ KEY_STYLE
mask of all the scalar styles for key (not container styles!)
Definition: node_type.hpp:92
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:72
@ VAL_DQUO
mark val scalar as double quoted "
Definition: node_type.hpp:70
@ KEY_SQUO
mark key scalar as single quoted '
Definition: node_type.hpp:67
@ VAL_LITERAL
mark val scalar as multiline, block literal |
Definition: node_type.hpp:64
@ KEY_LITERAL
mark key scalar as multiline, block literal |
Definition: node_type.hpp:63
@ KEY_PLAIN
mark key scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:71
@ KEY_FOLDED
mark key scalar as multiline, block folded >
Definition: node_type.hpp:65
Key< K > key(K &k)
Definition: node.hpp:46
substr to_substr(substr s) noexcept
neutral version for use in generic code
Definition: substr.hpp:2202
bool operator==(const char(&s)[N], basic_substring< C > const that) noexcept
Definition: substr.hpp:2282
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:244
@ NONE
an index to none
Definition: common.hpp:251
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
Definition: common.cpp:14
Node classes.
A lightweight object containing options to be used when emitting.
Definition: emit.hpp:60
wraps a NodeType_e element with some syntactic sugar and predicates
Definition: node_type.hpp:121
mark a tree or node to be emitted as yaml when using operator<<, with options.
Definition: emit.hpp:552
mark a tree or node to be emitted as yaml when using operator<< .
Definition: emit.hpp:573