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