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